1 /*****************************************************************************
2 * VLCVoutView.m: MacOS X video output module
3 *****************************************************************************
4 * Copyright (C) 2002-2014 VLC authors and VideoLAN
7 * Authors: Derk-Jan Hartman <hartman at videolan dot org>
8 * Eric Petit <titer@m0k.org>
9 * Benjamin Pracht <bigben at videolan dot org>
10 * Pierre d'Herbemont <pdherbemont # videolan org>
11 * Felix Paul Kühne <fkuehne at videolan dot org>
12 * David Fuhrmann <david dot fuhrmann at googlemail dot com>
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.
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.
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 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
32 #import <stdlib.h> /* free() */
34 #import "CompatibilityFixes.h"
36 #import "VLCVoutView.h"
37 #import "VLCCoreInteraction.h"
38 #import "VLCMainMenu.h"
40 #import <QuartzCore/QuartzCore.h>
45 /*****************************************************************************
46 * VLCVoutView implementation
47 *****************************************************************************/
48 @interface VLCVoutView()
50 NSTimer *p_scrollTimer;
51 NSInteger i_lastScrollWheelDirection;
53 CGFloat f_cumulatedXScrollValue;
54 CGFloat f_cumulatedYScrollValue;
56 CGFloat f_cumulated_magnification;
58 vout_thread_t *p_vout;
62 @implementation VLCVoutView
65 #pragma mark drag & drop support
70 vlc_object_release(p_vout);
72 [self unregisterDraggedTypes];
75 -(id)initWithFrame:(NSRect)frameRect
77 if (self = [super initWithFrame:frameRect]) {
78 [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
81 i_lastScrollWheelDirection = 0;
82 f_cumulated_magnification = 0.0;
87 - (void)addVoutLayer:(CALayer *)aLayer
89 if (self.layer == nil) {
90 [self setLayer:[CALayer layer]];
91 [self setWantsLayer:YES];
94 [CATransaction begin];
97 aLayer.bounds = self.layer.bounds;
98 [self.layer addSublayer:aLayer];
99 [self setNeedsDisplay:YES];
100 [aLayer setNeedsDisplay];
101 CGRect frame = aLayer.bounds;
102 frame.origin.x = frame.origin.y = 0.;
103 aLayer.frame = frame;
104 [CATransaction commit];
107 - (void)removeVoutLayer:(CALayer *)aLayer
109 [CATransaction begin];
110 [aLayer removeFromSuperlayer];
111 [CATransaction commit];
114 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
116 if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) == NSDragOperationGeneric)
117 return NSDragOperationGeneric;
118 return NSDragOperationNone;
121 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
126 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
128 BOOL b_returned = [[VLCCoreInteraction sharedInstance] performDragOperation:sender];
130 [self setNeedsDisplay:YES];
134 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
136 [self setNeedsDisplay:YES];
140 #pragma mark vout actions
142 - (void)keyDown:(NSEvent *)o_event
146 unsigned int i_pressed_modifiers = 0;
149 i_pressed_modifiers = [o_event modifierFlags];
151 if (i_pressed_modifiers & NSShiftKeyMask)
152 val.i_int |= KEY_MODIFIER_SHIFT;
153 if (i_pressed_modifiers & NSControlKeyMask)
154 val.i_int |= KEY_MODIFIER_CTRL;
155 if (i_pressed_modifiers & NSAlternateKeyMask)
156 val.i_int |= KEY_MODIFIER_ALT;
157 if (i_pressed_modifiers & NSCommandKeyMask)
158 val.i_int |= KEY_MODIFIER_COMMAND;
160 NSString * characters = [o_event charactersIgnoringModifiers];
161 if ([characters length] > 0) {
162 key = [[characters lowercaseString] characterAtIndex: 0];
165 /* Escape should always get you out of fullscreen */
166 if (key == (unichar) 0x1b) {
167 playlist_t * p_playlist = pl_Get(getIntf());
168 if (var_GetBool(p_playlist, "fullscreen"))
169 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
171 /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
172 else if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask)
173 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
175 val.i_int |= (int)CocoaKeyToVLC(key);
176 var_Set(p_vout->obj.libvlc, "key-pressed", val);
179 msg_Dbg(getIntf(), "could not send keyevent to VLC core");
184 [super keyDown: o_event];
187 - (BOOL)performKeyEquivalent:(NSEvent *)o_event
189 return [[[VLCMain sharedInstance] mainWindow] performKeyEquivalent: o_event];
192 - (void)mouseDown:(NSEvent *)o_event
194 if (([o_event type] == NSLeftMouseDown) && (! ([o_event modifierFlags] & NSControlKeyMask))) {
195 if ([o_event clickCount] == 2)
196 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
198 } else if (([o_event type] == NSRightMouseDown) ||
199 (([o_event type] == NSLeftMouseDown) &&
200 ([o_event modifierFlags] & NSControlKeyMask)))
201 [NSMenu popUpContextMenu: [[[VLCMain sharedInstance] mainMenu] voutMenu] withEvent: o_event forView: self];
203 [super mouseDown: o_event];
206 - (void)rightMouseDown:(NSEvent *)o_event
208 if ([o_event type] == NSRightMouseDown)
209 [NSMenu popUpContextMenu: [[[VLCMain sharedInstance] mainMenu] voutMenu] withEvent: o_event forView: self];
211 [super mouseDown: o_event];
214 - (void)rightMouseUp:(NSEvent *)o_event
216 if ([o_event type] == NSRightMouseUp)
217 [NSMenu popUpContextMenu: [[[VLCMain sharedInstance] mainMenu] voutMenu] withEvent: o_event forView: self];
219 [super mouseUp: o_event];
222 - (void)mouseMoved:(NSEvent *)o_event
224 NSPoint ml = [self convertPoint: [o_event locationInWindow] fromView: nil];
225 if ([self mouse: ml inRect: [self bounds]])
226 [[VLCMain sharedInstance] showFullscreenController];
228 [super mouseMoved: o_event];
231 - (void)resetScrollWheelDirection
233 i_lastScrollWheelDirection = 0;
234 f_cumulatedXScrollValue = f_cumulatedYScrollValue = 0.;
235 msg_Dbg(getIntf(), "Reset scrolling timer");
238 - (void)scrollWheel:(NSEvent *)theEvent
240 const CGFloat f_xThreshold = 0.8;
241 const CGFloat f_yThreshold = 1.0;
242 const CGFloat f_directionThreshold = 0.05;
244 intf_thread_t * p_intf = getIntf();
245 CGFloat f_deltaX = [theEvent deltaX];
246 CGFloat f_deltaY = [theEvent deltaY];
248 if ([theEvent isDirectionInvertedFromDevice]) {
249 f_deltaX = -f_deltaX;
250 f_deltaY = -f_deltaY;
253 CGFloat f_deltaXAbs = ABS(f_deltaX);
254 CGFloat f_deltaYAbs = ABS(f_deltaY);
256 // A mouse scroll wheel has lower sensitivity. We want to scroll at least
257 // with every event here.
258 BOOL isMouseScrollWheel = ([theEvent subtype] == NSMouseEventSubtype);
259 if (isMouseScrollWheel && f_deltaYAbs < f_yThreshold)
260 f_deltaY = f_deltaY > 0. ? f_yThreshold : -f_yThreshold;
262 if (isMouseScrollWheel && f_deltaXAbs < f_xThreshold)
263 f_deltaX = f_deltaX > 0. ? f_xThreshold : -f_xThreshold;
265 /* in the following, we're forwarding either a x or a y event */
266 /* Multiple key events are send depending on the intensity of the event */
267 /* the opposite direction is being blocked for a couple of milli seconds */
268 if (f_deltaYAbs > f_directionThreshold) {
269 if (i_lastScrollWheelDirection < 0) // last was a X
271 i_lastScrollWheelDirection = 1; // Y
273 f_cumulatedYScrollValue += f_deltaY;
274 int key = f_cumulatedYScrollValue < 0.0f ? KEY_MOUSEWHEELDOWN : KEY_MOUSEWHEELUP;
276 while (ABS(f_cumulatedYScrollValue) >= f_yThreshold) {
277 f_cumulatedYScrollValue -= (f_cumulatedYScrollValue > 0 ? f_yThreshold : -f_yThreshold);
278 var_SetInteger(p_intf->obj.libvlc, "key-pressed", key);
279 msg_Dbg(p_intf, "Scrolling in y direction");
282 } else if (f_deltaXAbs > f_directionThreshold) {
283 if (i_lastScrollWheelDirection > 0) // last was a Y
285 i_lastScrollWheelDirection = -1; // X
287 f_cumulatedXScrollValue += f_deltaX;
288 int key = f_cumulatedXScrollValue < 0.0f ? KEY_MOUSEWHEELRIGHT : KEY_MOUSEWHEELLEFT;
290 while (ABS(f_cumulatedXScrollValue) >= f_xThreshold) {
291 f_cumulatedXScrollValue -= (f_cumulatedXScrollValue > 0 ? f_xThreshold : -f_xThreshold);
292 var_SetInteger(p_intf->obj.libvlc, "key-pressed", key);
293 msg_Dbg(p_intf, "Scrolling in x direction");
298 [p_scrollTimer invalidate];
301 p_scrollTimer = [NSTimer scheduledTimerWithTimeInterval:0.4 target:self selector:@selector(resetScrollWheelDirection) userInfo:nil repeats:NO];
305 #pragma mark Handling of vout related actions
307 - (void)setVoutThread:(vout_thread_t *)p_vout_thread
309 assert(p_vout == NULL);
310 p_vout = p_vout_thread;
311 vlc_object_hold(p_vout);
314 - (vout_thread_t *)voutThread
317 vlc_object_hold(p_vout);
324 - (void)releaseVoutThread
327 vlc_object_release(p_vout);
333 #pragma mark Basic view behaviour and touch events handling
335 - (BOOL)mouseDownCanMoveWindow
340 - (BOOL)acceptsFirstResponder
345 - (BOOL)becomeFirstResponder
350 - (BOOL)resignFirstResponder
352 /* while we need to be the first responder most of the time, we need to give up that status when toggling the playlist */
356 -(void)didAddSubview:(NSView *)subview
358 [[self window] makeFirstResponder: subview];
361 - (void)magnifyWithEvent:(NSEvent *)event
363 f_cumulated_magnification += [event magnification];
365 // This is the result of [NSEvent standardMagnificationThreshold].
366 // Unfortunately, this is a private API, currently.
367 CGFloat f_threshold = 0.3;
368 BOOL b_fullscreen = [(VLCVideoWindowCommon *)[self window] fullscreen];
370 if ((f_cumulated_magnification > f_threshold && !b_fullscreen) || (f_cumulated_magnification < -f_threshold && b_fullscreen)) {
371 f_cumulated_magnification = 0.0;
372 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
376 - (void)beginGestureWithEvent:(NSEvent *)event
378 f_cumulated_magnification = 0.0;