]> git.sesse.net Git - vlc/blobdiff - modules/gui/macosx/VideoView.m
macosx/CAS: implement basic error checking for the stream-out settings
[vlc] / modules / gui / macosx / VideoView.m
index ea702a26fa414c07ded0520cc4c3804cfdd125a0..cd88942d5f47d1a5c05ae25589062607147898f6 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * VideoView.m: MacOS X video output module
  *****************************************************************************
- * Copyright (C) 2002-2011 the VideoLAN team
+ * Copyright (C) 2002-2012 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Derk-Jan Hartman <hartman at videolan dot org>
@@ -14,7 +14,7 @@
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
- * 
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 #import "MainWindow.h"
 
 #import <vlc_common.h>
-#import <vlc_vout_window.h>
-#import <vlc_vout_display.h>
 #import <vlc_keys.h>
-/*****************************************************************************
- * DeviceCallback: Callback triggered when the video-device variable is changed
- *****************************************************************************/
-int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
-                     vlc_value_t old_val, vlc_value_t new_val, void *param )
-{
-    vlc_value_t val;
-    vout_thread_t *p_vout = (vout_thread_t *)p_this;
-
-    msg_Dbg( p_vout, "set %"PRId64, new_val.i_int );
-    var_Create( p_vout->p_libvlc, "video-device", VLC_VAR_INTEGER );
-    var_Set( p_vout->p_libvlc, "video-device", new_val );
 
-    val.b_bool = true;
-    var_Set( p_vout, "intf-change", val );
-    return VLC_SUCCESS;
-}
 
 /*****************************************************************************
  * VLCVoutView implementation
  *****************************************************************************/
 @implementation VLCVoutView
-- (void)setVoutView:(id)theView
+
+#pragma mark -
+#pragma mark drag & drop support
+
+- (void)dealloc
 {
-    vout_thread_t * p_vout = getVout();
-    if( !p_vout )
-        return;
+    if (p_vout)
+        vlc_object_release(p_vout);
 
-    int i_device;
-    NSArray *o_screens = [NSScreen screens];
-    if( [o_screens count] <= 0 )
-    {
-        msg_Err( VLCIntf, "no OSX screens available" );
-        return;
-    }
+    [self unregisterDraggedTypes];
+    [super dealloc];
+}
 
-    /* Get the pref value when this is the first time, otherwise retrieve the device from the top level video-device var */
-    if( var_Type( p_vout->p_libvlc, "video-device" ) == 0 )
-    {
-        i_device = var_GetInteger( p_vout, "macosx-vdev" );
-    }
-    else
-    {
-        i_device = var_GetInteger( p_vout->p_libvlc, "video-device" );
+-(id)initWithFrame:(NSRect)frameRect
+{
+    if (self = [super initWithFrame:frameRect]) {
+        [self registerForDraggedTypes:[NSArray arrayWithObject: NSFilenamesPboardType]];
     }
 
-    /* Setup the menuitem for the multiple displays. */
-    if( var_Type( p_vout, "video-device" ) == 0 )
-    {
-        int i = 1;
-        vlc_value_t val2, text;
-        NSScreen * o_screen;
-
-        var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
-                   VLC_VAR_HASCHOICE );
-        text.psz_string = _("Fullscreen Video Device");
-        var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
-
-        NSEnumerator * o_enumerator = [o_screens objectEnumerator];
-
-        val2.i_int = 0;
-        text.psz_string = _("Default");
-        var_Change( p_vout, "video-device", VLC_VAR_ADDCHOICE, &val2, &text );
-        var_Set( p_vout, "video-device", val2 );
-
-        while( (o_screen = [o_enumerator nextObject]) != NULL )
-        {
-            char psz_temp[255];
-            NSRect s_rect = [o_screen frame];
-
-            snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, "%s %d (%dx%d)", _("Screen"), i, (int)s_rect.size.width, (int)s_rect.size.height );
-
-            text.psz_string = psz_temp;
-            val2.i_int = (int)[o_screen displayID];
-            var_Change( p_vout, "video-device", VLC_VAR_ADDCHOICE, &val2, &text );
-            if( (int)[o_screen displayID] == i_device )
-            {
-                var_Set( p_vout, "video-device", val2 );
-            }
-            i++;
-        }
+    i_lastScrollWheelDirection = 0;
+    f_cumulated_magnification = 0.0;
 
-        var_AddCallback( p_vout, "video-device", DeviceCallback,
-                        NULL );
-
-        val2.b_bool = true;
-        var_Set( p_vout, "intf-change", val2 );
-    }
+    return self;
+}
 
-    /* Add the view. It's automatically resized to fit the window */
-    if (o_view) {
-        [o_view removeFromSuperview];
-        [o_view release];
-    }
-    o_view = theView;
-    [o_view retain];
-    [self addSubview: o_view];
-    [self setAutoresizesSubviews: YES];
-
-    /* make sure that we look alright */
-    [[self window] setAlphaValue: var_CreateGetFloat( p_vout, "macosx-opaqueness" )];
-    vlc_object_release( p_vout );
+- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
+{
+    if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
+        return NSDragOperationGeneric;
+    return NSDragOperationNone;
 }
 
-- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize
+- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
 {
-    [super resizeSubviewsWithOldSize: oldBoundsSize];
-    [o_view setFrameSize: [self frame].size];
+    return YES;
 }
 
-- (void)closeVout
+- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 {
-    /* Make sure we don't see a white flash */
-    [o_view removeFromSuperview];
-    [o_view release];
-    o_view = nil;
+    BOOL b_returned;
+    b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation: sender];
+
+    [self setNeedsDisplay:YES];
+    return b_returned;
 }
 
-- (void)scrollWheel:(NSEvent *)theEvent
+- (void)concludeDragOperation:(id <NSDraggingInfo>)sender
 {
-    VLCControls * o_controls = (VLCControls *)[[NSApp delegate] controls];
-    [o_controls scrollWheel: theEvent];
+    [self setNeedsDisplay:YES];
 }
 
+#pragma mark -
+#pragma mark vout actions
+
 - (void)keyDown:(NSEvent *)o_event
 {
     unichar key = 0;
@@ -174,205 +108,184 @@ int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
 
     i_pressed_modifiers = [o_event modifierFlags];
 
-    if( i_pressed_modifiers & NSShiftKeyMask )
+    if (i_pressed_modifiers & NSShiftKeyMask)
         val.i_int |= KEY_MODIFIER_SHIFT;
-    if( i_pressed_modifiers & NSControlKeyMask )
+    if (i_pressed_modifiers & NSControlKeyMask)
         val.i_int |= KEY_MODIFIER_CTRL;
-    if( i_pressed_modifiers & NSAlternateKeyMask )
+    if (i_pressed_modifiers & NSAlternateKeyMask)
         val.i_int |= KEY_MODIFIER_ALT;
-    if( i_pressed_modifiers & NSCommandKeyMask )
+    if (i_pressed_modifiers & NSCommandKeyMask)
         val.i_int |= KEY_MODIFIER_COMMAND;
 
-    key = [[[o_event charactersIgnoringModifiers] lowercaseString] characterAtIndex: 0];
-
-    if( key )
-    {
-        vout_thread_t * p_vout = getVout();
-        /* Escape should always get you out of fullscreen */
-        if( key == (unichar) 0x1b )
-        {
-            playlist_t * p_playlist = pl_Get( VLCIntf );
-             if( var_GetBool( p_playlist, "fullscreen") )
-                 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
-        }
-        else if ( p_vout )
-        {
-            if( key == ' ')
-                val.i_int = config_GetInt( p_vout, "key-play-pause" );
-            else
-                val.i_int |= (int)CocoaKeyToVLC( key );
-            var_Set( p_vout->p_libvlc, "key-pressed", val );
-            vlc_object_release( p_vout );
-        }
-        else
-            msg_Dbg( VLCIntf, "could not send keyevent to VLC core" );
-    }
-    else
-        [super keyDown: o_event];
-}
+    NSString * characters = [o_event charactersIgnoringModifiers];
+    if ([characters length] > 0) {
+        key = [[characters lowercaseString] characterAtIndex: 0];
 
-- (void)mouseDown:(NSEvent *)o_event
-{
-    vout_thread_t * p_vout = getVout();
-    vlc_value_t val;
-    if( p_vout )
-    {
-        if( ( [o_event type] == NSLeftMouseDown ) &&
-          ( ! ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
-        {
-            if( [o_event clickCount] <= 1 )
-            {
-                /* single clicking */
-                var_Get( p_vout, "mouse-button-down", &val );
-                val.i_int |= 1;
-                var_Set( p_vout, "mouse-button-down", val );
+        if (key) {
+            /* Escape should always get you out of fullscreen */
+            if (key == (unichar) 0x1b) {
+                playlist_t * p_playlist = pl_Get(VLCIntf);
+                 if (var_GetBool(p_playlist, "fullscreen"))
+                     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
             }
-            else
-            {
-                /* multiple clicking */
+            /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
+            else if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask)
                 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
+            else if (p_vout) {
+                if (key == ' ')
+                    [[VLCCoreInteraction sharedInstance] play];
+                else {
+                    val.i_int |= (int)CocoaKeyToVLC(key);
+                    var_Set(p_vout->p_libvlc, "key-pressed", val);
+                }
             }
+            else
+                msg_Dbg(VLCIntf, "could not send keyevent to VLC core");
+
+            return;
         }
-        else if( ( [o_event type] == NSRightMouseDown ) ||
-               ( ( [o_event type] == NSLeftMouseDown ) &&
-                 ( [o_event modifierFlags] &  NSControlKeyMask ) ) )
-        {
-            msg_Dbg( p_vout, "received NSRightMouseDown (generic method) or Ctrl clic" );
-            [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
-        }
-        vlc_object_release( p_vout );
     }
+    [super keyDown: o_event];
+}
 
-    [super mouseDown: o_event];
+- (BOOL)performKeyEquivalent:(NSEvent *)o_event
+{
+    return [[VLCMainWindow sharedInstance] performKeyEquivalent: o_event];
 }
 
-- (void)otherMouseDown:(NSEvent *)o_event
+- (void)mouseDown:(NSEvent *)o_event
 {
-    if( [o_event type] == NSOtherMouseDown )
-    {
-        vout_thread_t * p_vout = getVout();
-        vlc_value_t val;
-
-        if (p_vout)
-        {
-            var_Get( p_vout, "mouse-button-down", &val );
-            val.i_int |= 2;
-            var_Set( p_vout, "mouse-button-down", val );
-        }
-        vlc_object_release( p_vout );
-    }
+    if (([o_event type] == NSLeftMouseDown) && (! ([o_event modifierFlags] &  NSControlKeyMask))) {
+        if ([o_event clickCount] > 1)
+            [[VLCCoreInteraction sharedInstance] toggleFullscreen];
+    } else if (([o_event type] == NSRightMouseDown) ||
+               (([o_event type] == NSLeftMouseDown) &&
+               ([o_event modifierFlags] &  NSControlKeyMask)))
+        [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
 
     [super mouseDown: o_event];
 }
 
 - (void)rightMouseDown:(NSEvent *)o_event
 {
-    if( [o_event type] == NSRightMouseDown )
-    {
-        vout_thread_t * p_vout = getVout();
-        if (p_vout)
-            [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
-        vlc_object_release( p_vout );
-    }
+    if ([o_event type] == NSRightMouseDown)
+        [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
 
     [super mouseDown: o_event];
 }
 
-- (void)mouseUp:(NSEvent *)o_event
+- (void)rightMouseUp:(NSEvent *)o_event
 {
-    if( [o_event type] == NSLeftMouseUp )
-    {
-        vout_thread_t * p_vout = getVout();
-        if (p_vout)
-        {
-            vlc_value_t val;
-            int x, y;
-
-            var_GetCoords( p_vout, "mouse-moved", &x, &y );
-            var_SetCoords( p_vout, "mouse-clicked", x, y );
-
-            var_Get( p_vout, "mouse-button-down", &val );
-            val.i_int &= ~1;
-            var_Set( p_vout, "mouse-button-down", val );
-            vlc_object_release( p_vout );
-        }
-    }
+    if ([o_event type] == NSRightMouseUp)
+        [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
 
     [super mouseUp: o_event];
 }
 
-- (void)otherMouseUp:(NSEvent *)o_event
+- (void)mouseMoved:(NSEvent *)o_event
 {
-    if( [o_event type] == NSOtherMouseUp )
-    {
-        vout_thread_t * p_vout = getVout();
-        if (p_vout)
-        {
-            vlc_value_t val;
-            var_Get( p_vout, "mouse-button-down", &val );
-            val.i_int &= ~2;
-            var_Set( p_vout, "mouse-button-down", val );
-            vlc_object_release( p_vout );
-        }
-    }
+    NSPoint ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
+    if ([self mouse: ml inRect: [self bounds]])
+        [[VLCMain sharedInstance] showFullscreenController];
 
-    [super mouseUp: o_event];
+    [super mouseMoved: o_event];
 }
 
-- (void)rightMouseUp:(NSEvent *)o_event
+- (void)resetScrollWheelDirection
 {
-    if( [o_event type] == NSRightMouseUp )
-    {
-        vout_thread_t * p_vout = getVout();
-        if (p_vout)
-        {
-            [NSMenu popUpContextMenu: [[VLCMainMenu sharedInstance] voutMenu] withEvent: o_event forView: self];
-            vlc_object_release( p_vout );
-        }
-    }
-
-    [super mouseUp: o_event];
+    /* release the scroll direction 0.8 secs after the last event */
+    if (([NSDate timeIntervalSinceReferenceDate] - t_lastScrollEvent) >= 0.80)
+        i_lastScrollWheelDirection = 0;
 }
 
-- (void)mouseDragged:(NSEvent *)o_event
+- (void)scrollWheel:(NSEvent *)theEvent
 {
-    [self mouseMoved: o_event];
+    intf_thread_t * p_intf = VLCIntf;
+    CGFloat f_deltaX = [theEvent deltaX];
+    CGFloat f_deltaY = [theEvent deltaY];
+
+    if (!OSX_SNOW_LEOPARD && [theEvent isDirectionInvertedFromDevice]) {
+        f_deltaX = -f_deltaX;
+        f_deltaY = -f_deltaY;
+    }
+
+    CGFloat f_yabsvalue = f_deltaY > 0.0f ? f_deltaY : -f_deltaY;
+    CGFloat f_xabsvalue = f_deltaX > 0.0f ? f_deltaX : -f_deltaX;
+
+    int i_yvlckey, i_xvlckey = 0;
+    if (f_deltaY < 0.0f)
+        i_yvlckey = KEY_MOUSEWHEELDOWN;
+    else
+        i_yvlckey = KEY_MOUSEWHEELUP;
+
+    if (f_deltaX < 0.0f)
+        i_xvlckey = KEY_MOUSEWHEELRIGHT;
+    else
+        i_xvlckey = KEY_MOUSEWHEELLEFT;
+
+    /* in the following, we're forwarding either a x or a y event */
+    /* Multiple key events are send depending on the intensity of the event */
+    /* the opposite direction is being blocked for 0.8 secs */
+    if (f_yabsvalue > 0.05) {
+        if (i_lastScrollWheelDirection < 0) // last was a X
+            return;
+
+        i_lastScrollWheelDirection = 1; // Y
+        for (NSUInteger i = 0; i < (int)(f_yabsvalue/4.+1.); i++)
+            var_SetInteger(p_intf->p_libvlc, "key-pressed", i_yvlckey);
+
+        t_lastScrollEvent = [NSDate timeIntervalSinceReferenceDate];
+        [self performSelector:@selector(resetScrollWheelDirection)
+                   withObject: NULL
+                   afterDelay:1.00];
+        return;
+    }
+    if (f_xabsvalue > 0.05) {
+        if (i_lastScrollWheelDirection > 0) // last was a Y
+            return;
+
+        i_lastScrollWheelDirection = -1; // X
+        for (NSUInteger i = 0; i < (int)(f_xabsvalue/6.+1.); i++)
+            var_SetInteger(p_intf->p_libvlc, "key-pressed", i_xvlckey);
+
+        t_lastScrollEvent = [NSDate timeIntervalSinceReferenceDate];
+        [self performSelector:@selector(resetScrollWheelDirection)
+                   withObject: NULL
+                   afterDelay:1.00];
+    }
 }
 
-- (void)otherMouseDragged:(NSEvent *)o_event
+#pragma mark -
+#pragma mark Handling of vout related actions
+
+- (void)setVoutThread:(vout_thread_t *)p_vout_thread
 {
-    [self mouseMoved: o_event];
+    assert(p_vout == NULL);
+    p_vout = p_vout_thread;
+    vlc_object_hold(p_vout);
 }
 
-- (void)rightMouseDragged:(NSEvent *)o_event
+- (vout_thread_t *)voutThread
 {
-    [self mouseMoved: o_event];
+    if (p_vout) {
+        vlc_object_hold(p_vout);
+        return p_vout;
+    }
+
+    return NULL;
 }
 
-- (void)mouseMoved:(NSEvent *)o_event
+- (void)releaseVoutThread
 {
-    vout_thread_t * p_vout = getVout();
-    if( p_vout )
-    {
-        NSPoint ml;
-        NSRect s_rect;
-        BOOL b_inside;
-
-        s_rect = [o_view bounds];
-        ml = [o_view convertPoint: [o_event locationInWindow] fromView: nil];
-        b_inside = [o_view mouse: ml inRect: s_rect];
-
-        if( b_inside )
-        {
-            var_SetCoords( p_vout, "mouse-moved", ((int)ml.x), ((int)ml.y) );
-        }
-        vlc_object_release( p_vout );
-        [[VLCMain sharedInstance] showFullscreenController];
+    if (p_vout) {
+        vlc_object_release(p_vout);
+        p_vout = NULL;
     }
-
-    [super mouseMoved: o_event];
 }
 
+#pragma mark -
+#pragma mark Basic view behaviour and touch events handling
+
 - (BOOL)mouseDownCanMoveWindow
 {
     return YES;
@@ -390,15 +303,33 @@ int DeviceCallback( vlc_object_t *p_this, const char *psz_variable,
 
 - (BOOL)resignFirstResponder
 {
-    /* We need to stay the first responder or we'll miss some
-       events */
-    return NO;
+    /* while we need to be the first responder most of the time, we need to give up that status when toggling the playlist */
+    return YES;
+}
+
+-(void)didAddSubview:(NSView *)subview
+{
+    [[self window] makeFirstResponder: subview];
 }
 
-- (void)renewGState
+- (void)magnifyWithEvent:(NSEvent *)event
 {
-    [[self window] disableScreenUpdatesUntilFlush];
+    f_cumulated_magnification += [event magnification];
+
+    // This is the result of [NSEvent standardMagnificationThreshold].
+    // Unfortunately, this is a private API, currently.
+    CGFloat f_threshold = 0.3;
+    BOOL b_fullscreen = [[VLCMainWindow sharedInstance] fullscreen];
 
-    [super renewGState];
+    if ((f_cumulated_magnification > f_threshold && !b_fullscreen) || (f_cumulated_magnification < -f_threshold && b_fullscreen)) {
+        f_cumulated_magnification = 0.0;
+        [[VLCCoreInteraction sharedInstance] toggleFullscreen];
+    }
 }
+
+- (void)beginGestureWithEvent:(NSEvent *)event
+{
+    f_cumulated_magnification = 0.0;
+}
+
 @end