]> git.sesse.net Git - vlc/blob - modules/gui/macosx/vout.m
* modules/macosx/{vout,voutqt}.m: The Mac OS X Mozilla plugin lives again!
[vlc] / modules / gui / macosx / vout.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2005 VideoLAN
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  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 /* BeginFullScreen, EndFullScreen */
36 #include <QuickTime/QuickTime.h>
37
38 #include <vlc_keys.h>
39
40 #include "intf.h"
41 #include "vout.h"
42
43 /*****************************************************************************
44  * VLCWindow implementation
45  *****************************************************************************/
46 @implementation VLCWindow
47
48 - (id)initWithVout:(vout_thread_t *)_p_vout frame:(NSRect *)s_frame
49 {
50     int i_timeout;
51     vlc_value_t value_drawable;
52
53     p_vout = _p_vout;
54
55     var_Get( p_vout->p_vlc, "drawable", &value_drawable );
56
57     /* We only wait for NSApp to initialise if we're not embedded (as in the
58      * case of the Mozilla plugin).  We can tell whether we're embedded or not
59      * by examining the "drawable" value: if it's zero, we're running in the
60      * main Mac intf; if it's non-zero, we're embedded. */
61     if( value_drawable.i_int == 0 )
62     {
63         /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
64         for( i_timeout = 20 ; i_timeout-- ; )
65         {
66             if( NSApp == NULL )
67             {
68                 msleep( INTF_IDLE_SLEEP );
69             }
70         }
71
72         if( NSApp == NULL )
73         {
74             /* No MacOS X intf, unable to communicate with MT */
75             msg_Err( p_vout, "no MacOS X interface present" );
76             return NULL;
77         }
78     }
79
80     /* p_real_vout: the vout we have to use to check for video-on-top
81        and a few other things. If we are the QuickTime output, it's us.
82        It we are the OpenGL provider, it is our parent. */
83     if( p_vout->i_object_type == VLC_OBJECT_OPENGL )
84     {
85         p_real_vout = (vout_thread_t *) p_vout->p_parent;
86     }
87     else
88     {
89         p_real_vout = p_vout;
90     }
91
92     p_fullscreen_state = NULL;
93     i_time_mouse_last_moved = mdate();
94
95     NSScreen * o_screen;
96     vlc_bool_t b_main_screen;
97
98     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
99     var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
100     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
101     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
102
103     /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
104     NSArray * o_screens = [NSScreen screens];
105     if( [o_screens count] > 0 && var_Type( p_real_vout, "video-device" ) == 0 )
106     {
107         int i = 1;
108         vlc_value_t val, val2, text;
109         NSScreen * o_screen;
110
111         var_Get( p_real_vout, "macosx-vdev", &val );
112
113         var_Create( p_real_vout, "video-device", VLC_VAR_INTEGER |
114                                             VLC_VAR_HASCHOICE );
115         text.psz_string = _("Video device");
116         var_Change( p_real_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
117
118         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
119
120         while( (o_screen = [o_enumerator nextObject]) != NULL )
121         {
122             char psz_temp[255];
123             NSRect s_rect = [o_screen frame];
124
125             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1,
126                       "%s %d (%dx%d)", _("Screen"), i,
127                       (int)s_rect.size.width, (int)s_rect.size.height );
128
129             text.psz_string = psz_temp;
130             val2.i_int = i;
131             var_Change( p_real_vout, "video-device",
132                         VLC_VAR_ADDCHOICE, &val2, &text );
133
134             if( ( i - 1 ) == val.i_int )
135             {
136                 var_Set( p_real_vout, "video-device", val2 );
137             }
138             i++;
139         }
140
141         var_AddCallback( p_real_vout, "video-device", vout_VarCallback,
142                          NULL );
143
144         val2.b_bool = VLC_TRUE;
145         var_Set( p_real_vout, "intf-change", val2 );
146     }
147
148     /* Find out on which screen to open the window */
149     int i_device = var_GetInteger( p_real_vout, "video-device" );
150     if( i_device < 0 )
151     {
152          /* No preference specified. Use the main screen */
153         o_screen = [NSScreen mainScreen];
154         b_main_screen = 1;
155     }
156     else
157     {
158         NSArray *o_screens = [NSScreen screens];
159         
160         if( [o_screens count] < (unsigned) i_device )
161         {
162             o_screen = [NSScreen mainScreen];
163             b_main_screen = 1;
164         }
165         else
166         {
167             i_device--;
168             o_screen = [o_screens objectAtIndex: i_device];
169             var_SetInteger( p_real_vout, "macosx-vdev", i_device );
170             b_main_screen = ( i_device == 0 );
171         }
172     }
173
174     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
175
176     if( p_vout->b_fullscreen )
177     {
178         NSRect screen_rect = [o_screen frame];
179         screen_rect.origin.x = screen_rect.origin.y = 0;
180
181         /* Creates a window with size: screen_rect on o_screen */
182         [self initWithContentRect: screen_rect
183               styleMask: NSBorderlessWindowMask
184               backing: NSBackingStoreBuffered
185               defer: YES screen: o_screen];
186
187         if( b_main_screen )
188         {
189             BeginFullScreen( &p_fullscreen_state, NULL, 0, 0,
190                              NULL, NULL, fullScreenAllowEvents );
191         }
192     }
193     else
194     {
195         unsigned int i_stylemask = NSTitledWindowMask |
196                                    NSMiniaturizableWindowMask |
197                                    NSClosableWindowMask |
198                                    NSResizableWindowMask;
199
200         NSRect s_rect;
201         if( !s_frame )
202         {
203             s_rect.size.width  = p_vout->i_window_width;
204             s_rect.size.height = p_vout->i_window_height;
205         }
206         else
207         {
208             s_rect = *s_frame;
209         }
210        
211         [self initWithContentRect: s_rect
212               styleMask: i_stylemask
213               backing: NSBackingStoreBuffered
214               defer: YES screen: o_screen];
215
216         [self setAlphaValue: var_GetFloat( p_vout, "macosx-opaqueness" )];
217
218         if( var_GetBool( p_real_vout, "video-on-top" ) )
219         {
220             [self setLevel: NSStatusWindowLevel];
221         }
222
223         if( !s_frame )
224         {
225             [self center];
226         }
227     }
228
229     [self updateTitle];
230     [self makeKeyAndOrderFront: nil];
231     [self setReleasedWhenClosed: YES];
232
233     /* We'll catch mouse events */
234     [self setAcceptsMouseMovedEvents: YES];
235     [self makeFirstResponder: self];
236     
237     [o_pool release];
238     return self;
239 }
240
241 - (void)close
242 {
243     if( p_fullscreen_state )
244     {
245         EndFullScreen( p_fullscreen_state, 0 );
246     }
247     [super close];
248 }
249
250 - (void)setOnTop:(BOOL)b_on_top
251 {
252     if( b_on_top )
253     {
254         [self setLevel: NSStatusWindowLevel];
255     }
256     else
257     {
258         [self setLevel: NSNormalWindowLevel];
259     }
260 }
261
262 - (void)hideMouse:(BOOL)b_hide
263 {
264     BOOL b_inside;
265     NSPoint ml;
266     NSView *o_contents = [self contentView];
267     
268     ml = [self convertScreenToBase:[NSEvent mouseLocation]];
269     ml = [o_contents convertPoint:ml fromView:nil];
270     b_inside = [o_contents mouse: ml inRect: [o_contents bounds]];
271     
272     if( b_hide && b_inside )
273     {
274         [NSCursor setHiddenUntilMouseMoves: YES];
275     }
276     else if( !b_hide )
277     {
278         [NSCursor setHiddenUntilMouseMoves: NO];
279     }
280 }
281
282 - (void)manage
283 {
284     if( p_fullscreen_state )
285     {
286         if( mdate() - i_time_mouse_last_moved > 3000000 )
287         {
288             [self hideMouse: YES];
289         }
290     }
291     else
292     {
293         [self hideMouse: NO];
294     }
295
296     /* Disable screensaver */
297     UpdateSystemActivity( UsrActivity );
298 }
299
300 - (void)scaleWindowWithFactor: (float)factor
301 {
302     NSSize newsize;
303     int i_corrected_height, i_corrected_width;
304     NSPoint topleftbase;
305     NSPoint topleftscreen;
306     
307     if ( !p_vout->b_fullscreen )
308     {
309         topleftbase.x = 0;
310         topleftbase.y = [self frame].size.height;
311         topleftscreen = [self convertBaseToScreen: topleftbase];
312         
313         if( p_vout->render.i_height * p_vout->render.i_aspect > 
314                         p_vout->render.i_width * VOUT_ASPECT_FACTOR )
315         {
316             i_corrected_width = p_vout->render.i_height * p_vout->render.i_aspect /
317                                             VOUT_ASPECT_FACTOR;
318             newsize.width = (int) ( i_corrected_width * factor );
319             newsize.height = (int) ( p_vout->render.i_height * factor );
320         }
321         else
322         {
323             i_corrected_height = p_vout->render.i_width * VOUT_ASPECT_FACTOR /
324                                             p_vout->render.i_aspect;
325             newsize.width = (int) ( p_vout->render.i_width * factor );
326             newsize.height = (int) ( i_corrected_height * factor );
327         }
328     
329         [self setContentSize: newsize];
330         
331         [self setFrameTopLeftPoint: topleftscreen];
332         p_vout->i_changes |= VOUT_SIZE_CHANGE;
333     }
334 }
335
336 - (void)toggleFloatOnTop
337 {
338     vlc_value_t val;
339
340     if( var_Get( p_vout, "video-on-top", &val )>=0 && val.b_bool)
341     {
342         val.b_bool = VLC_FALSE;
343     }
344     else
345     {
346         val.b_bool = VLC_TRUE;
347     }
348     var_Set( p_vout, "video-on-top", val );
349 }
350
351 - (void)toggleFullscreen
352 {
353     vlc_value_t val;
354     val.b_bool = !p_real_vout->b_fullscreen;
355     var_Set( p_real_vout, "fullscreen", val );
356 }
357
358 - (BOOL)isFullscreen
359 {
360     return( p_vout->b_fullscreen );
361 }
362
363 - (void)snapshot
364 {
365     vout_Control( p_vout, VOUT_SNAPSHOT );
366 }
367
368 - (BOOL)canBecomeKeyWindow
369 {
370     return YES;
371 }
372
373 /* Sometimes crashes VLC....
374 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
375 {
376         return [[VLCMain sharedInstance] hasDefinedShortcutKey:o_event];
377 }*/
378
379 - (void)keyDown:(NSEvent *)o_event
380 {
381     unichar key = 0;
382     vlc_value_t val;
383     unsigned int i_pressed_modifiers = 0;
384     val.i_int = 0;
385     
386     i_pressed_modifiers = [o_event modifierFlags];
387
388     if( i_pressed_modifiers & NSShiftKeyMask )
389         val.i_int |= KEY_MODIFIER_SHIFT;
390     if( i_pressed_modifiers & NSControlKeyMask )
391         val.i_int |= KEY_MODIFIER_CTRL;
392     if( i_pressed_modifiers & NSAlternateKeyMask )
393         val.i_int |= KEY_MODIFIER_ALT;
394     if( i_pressed_modifiers & NSCommandKeyMask )
395         val.i_int |= KEY_MODIFIER_COMMAND;
396
397     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
398
399     if( key )
400     {
401         /* Escape should always get you out of fullscreen */
402         if( key == (unichar) 0x1b )
403         {
404              if( [self isFullscreen] )
405              {
406                  [self toggleFullscreen];
407              }
408         }
409         else if ( key == ' ' )
410         {
411             vlc_value_t val;
412             val.i_int = config_GetInt( p_vout, "key-play-pause" );
413             var_Set( p_vout->p_vlc, "key-pressed", val );
414         }
415         else
416         {
417             val.i_int |= CocoaKeyToVLC( key );
418             var_Set( p_vout->p_vlc, "key-pressed", val );
419         }
420     }
421     else
422     {
423         [super keyDown: o_event];
424     }
425 }
426
427 - (void)updateTitle
428 {
429     NSMutableString * o_title,* o_mrl;
430     input_thread_t * p_input;
431     
432     if( p_vout == NULL )
433     {
434         return;
435     }
436     
437     p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT,
438                                                 FIND_PARENT );
439     
440     if( p_input == NULL )
441     {
442         return;
443     }
444
445     if( p_input->input.p_item->psz_name != NULL )
446         o_title = [NSMutableString stringWithUTF8String:
447             p_input->input.p_item->psz_name];
448     if( p_input->input.p_item->psz_uri != NULL )
449         o_mrl = [NSMutableString stringWithUTF8String:
450             p_input->input.p_item->psz_uri];
451     if( o_title == nil )
452         o_title = o_mrl;
453
454     vlc_object_release( p_input );
455     if( o_mrl != nil )
456     {
457         if( p_input->input.p_access && !strcmp( p_input->input.p_access->p_module->psz_shortname, "File" ) )
458         {
459             NSRange prefix_range = [o_mrl rangeOfString: @"file:"];
460             if( prefix_range.location != NSNotFound )
461                 [o_mrl deleteCharactersInRange: prefix_range];
462             [self setRepresentedFilename: o_mrl];
463         }
464         [self setTitle: o_title];
465     }
466     else
467     {
468         [self setTitle: [NSString stringWithCString: VOUT_TITLE]];
469     }
470 }
471
472 /* This is actually the same as VLCControls::stop. */
473 - (BOOL)windowShouldClose:(id)sender
474 {
475     playlist_t * p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
476                                                        FIND_ANYWHERE );
477     if( p_playlist == NULL )      
478     {
479         return NO;
480     }
481
482     playlist_Stop( p_playlist );
483     vlc_object_release( p_playlist );
484
485     /* The window will be closed by the intf later. */
486     return NO;
487 }
488
489 - (BOOL)acceptsFirstResponder
490 {
491     return YES;
492 }
493
494 - (BOOL)becomeFirstResponder
495 {
496     return YES;
497 }
498
499 - (BOOL)resignFirstResponder
500 {
501     /* We need to stay the first responder or we'll miss some
502        events */
503     return NO;
504 }
505
506 - (void)mouseDown:(NSEvent *)o_event
507 {
508     vlc_value_t val;
509
510     switch( [o_event type] )
511     {
512         case NSLeftMouseDown:
513         {
514             var_Get( p_vout, "mouse-button-down", &val );
515             val.i_int |= 1;
516             var_Set( p_vout, "mouse-button-down", val );
517         }
518         break;
519
520         default:
521             [super mouseDown: o_event];
522         break;
523     }
524 }
525
526 - (void)otherMouseDown:(NSEvent *)o_event
527 {
528     vlc_value_t val;
529
530     switch( [o_event type] )
531     {
532         case NSOtherMouseDown:
533         {
534             var_Get( p_vout, "mouse-button-down", &val );
535             val.i_int |= 2;
536             var_Set( p_vout, "mouse-button-down", val );
537         }
538         break;
539
540         default:
541             [super mouseDown: o_event];
542         break;
543     }
544 }
545
546 - (void)rightMouseDown:(NSEvent *)o_event
547 {
548     vlc_value_t val;
549
550     switch( [o_event type] )
551     {
552         case NSRightMouseDown:
553         {
554             var_Get( p_vout, "mouse-button-down", &val );
555             val.i_int |= 4;
556             var_Set( p_vout, "mouse-button-down", val );
557         }
558         break;
559
560         default:
561             [super mouseDown: o_event];
562         break;
563     }
564 }
565
566 - (void)mouseUp:(NSEvent *)o_event
567 {
568     vlc_value_t val;
569
570     switch( [o_event type] )
571     {
572         case NSLeftMouseUp:
573         {
574             vlc_value_t b_val;
575             b_val.b_bool = VLC_TRUE;
576             var_Set( p_vout, "mouse-clicked", b_val );
577
578             var_Get( p_vout, "mouse-button-down", &val );
579             val.i_int &= ~1;
580             var_Set( p_vout, "mouse-button-down", val );
581         }
582         break;
583
584         default:
585             [super mouseUp: o_event];
586         break;
587     }
588 }
589
590 - (void)otherMouseUp:(NSEvent *)o_event
591 {
592     vlc_value_t val;
593
594     switch( [o_event type] )
595     {
596         case NSOtherMouseUp:
597         {
598             var_Get( p_vout, "mouse-button-down", &val );
599             val.i_int &= ~2;
600             var_Set( p_vout, "mouse-button-down", val );
601         }
602         break;
603
604         default:
605             [super mouseUp: o_event];
606         break;
607     }
608 }
609
610 - (void)rightMouseUp:(NSEvent *)o_event
611 {
612     vlc_value_t val;
613
614     switch( [o_event type] )
615     {
616         case NSRightMouseUp:
617         {
618             var_Get( p_vout, "mouse-button-down", &val );
619             val.i_int &= ~4;
620             var_Set( p_vout, "mouse-button-down", val );
621         }
622         break;
623
624         default:
625             [super mouseUp: o_event];
626         break;
627     }
628 }
629
630 - (void)mouseDragged:(NSEvent *)o_event
631 {
632     [self mouseMoved: o_event];
633 }
634
635 - (void)otherMouseDragged:(NSEvent *)o_event
636 {
637     [self mouseMoved: o_event];
638 }
639
640 - (void)rightMouseDragged:(NSEvent *)o_event
641 {
642     [self mouseMoved: o_event];
643 }
644
645 - (void)mouseMoved:(NSEvent *)o_event
646 {   
647     NSPoint ml;
648     NSRect s_rect;
649     BOOL b_inside;
650     NSView * o_view;
651
652     i_time_mouse_last_moved = mdate();
653
654     o_view = [self contentView];
655     s_rect = [o_view bounds];
656     ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
657     b_inside = [o_view mouse: ml inRect: s_rect];
658
659     if( b_inside )
660     {
661         vlc_value_t val;
662         int i_width, i_height, i_x, i_y;
663
664         vout_PlacePicture( p_vout, (unsigned int)s_rect.size.width,
665                                    (unsigned int)s_rect.size.height,
666                                    &i_x, &i_y, &i_width, &i_height );
667
668         val.i_int = ( ((int)ml.x) - i_x ) *  
669                     p_vout->render.i_width / i_width;
670         var_Set( p_vout, "mouse-x", val );
671
672         if( [[o_view className] isEqualToString: @"VLCGLView"] )
673         {
674             val.i_int = ( ((int)(s_rect.size.height - ml.y)) - i_y ) *
675                         p_vout->render.i_height / i_height;
676         }
677         else
678         {
679             val.i_int = ( ((int)ml.y) - i_y ) * 
680                         p_vout->render.i_height / i_height;
681         }
682         var_Set( p_vout, "mouse-y", val );
683             
684         val.b_bool = VLC_TRUE;
685         var_Set( p_vout, "mouse-moved", val ); 
686     }
687
688     [super mouseMoved: o_event];
689 }
690
691 @end