/*****************************************************************************
* 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>
* 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;
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;
- (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