/* * This file is part of mpv. * * mpv is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * mpv 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 * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mpv. If not, see . */ #include "input/input.h" #include "input/keycodes.h" #include "osdep/macosx_compat.h" #include "video/out/cocoa_common.h" #include "events_view.h" @interface MpvEventsView() @property(nonatomic, assign) BOOL hasMouseDown; @property(nonatomic, retain) NSTrackingArea *tracker; - (int)mpvButtonNumber:(NSEvent*)event; - (void)mouseDownEvent:(NSEvent *)event; - (void)mouseUpEvent:(NSEvent *)event; @end @implementation MpvEventsView @synthesize adapter = _adapter; @synthesize tracker = _tracker; @synthesize hasMouseDown = _mouse_down; - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { [self registerForDraggedTypes:@[NSFilenamesPboardType, NSURLPboardType]]; [self setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; } return self; } // mpv uses flipped coordinates, because X11 uses those. So let's just use them // as well without having to do any coordinate conversion of mouse positions. - (BOOL)isFlipped { return YES; } - (void)updateTrackingAreas { if (self.tracker) [self removeTrackingArea:self.tracker]; if (![self.adapter mouseEnabled]) return; NSTrackingAreaOptions trackingOptions = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways; self.tracker = [[[NSTrackingArea alloc] initWithRect:[self bounds] options:trackingOptions owner:self userInfo:nil] autorelease]; [self addTrackingArea:self.tracker]; if (![self containsMouseLocation]) [self.adapter putKey:MP_KEY_MOUSE_LEAVE withModifiers:0]; } - (NSPoint)mouseLocation { return [self.window mouseLocationOutsideOfEventStream]; } - (BOOL)containsMouseLocation { CGFloat topMargin = 0.0; CGFloat menuBarHeight = [[NSApp mainMenu] menuBarHeight]; // menuBarHeight is 0 when menu bar is hidden in fullscreen // 1pt to compensate of the black line beneath the menu bar if ([self.adapter isInFullScreenMode] && menuBarHeight > 0) { NSRect tr = [NSWindow frameRectForContentRect:CGRectZero styleMask:NSWindowStyleMaskTitled]; topMargin = tr.size.height + 1 + menuBarHeight; } NSRect vF = [[self.window screen] frame]; vF.size.height -= topMargin; NSRect vFW = [self.window convertRectFromScreen:vF]; NSRect vFV = [self convertRect:vFW fromView:nil]; NSPoint pt = [self convertPoint:[self mouseLocation] fromView:nil]; // clip bounds to current visibleFrame NSRect clippedBounds = CGRectIntersection([self bounds], vFV); return CGRectContainsPoint(clippedBounds, pt); } - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return [self.adapter mouseEnabled]; } - (BOOL)acceptsFirstResponder { return [self.adapter keyboardEnabled] || [self.adapter mouseEnabled]; } - (BOOL)becomeFirstResponder { return [self.adapter keyboardEnabled] || [self.adapter mouseEnabled]; } - (BOOL)resignFirstResponder { return YES; } - (BOOL)canHideCursor { return !self.hasMouseDown && [self containsMouseLocation] && [[self window] isKeyWindow]; } - (void)mouseEntered:(NSEvent *)event { [super mouseEntered:event]; if ([self.adapter mouseEnabled]) { [self.adapter putKey:MP_KEY_MOUSE_ENTER withModifiers:0]; } } - (void)mouseExited:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self.adapter putKey:MP_KEY_MOUSE_LEAVE withModifiers:0]; } else { [super mouseExited:event]; } } - (NSPoint)convertPointToPixels:(NSPoint)point { point = [self convertPoint:point fromView:nil]; point = [self convertPointToBacking:point]; // flip y since isFlipped returning YES doesn't affect the backing // coordinate system point.y = -point.y; return point; } - (void)signalMouseMovement:(NSEvent *)event { NSPoint p = [self convertPointToPixels:[event locationInWindow]]; [self.adapter signalMouseMovement:p]; } - (void)mouseMoved:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self signalMouseMovement:event]; } else { [super mouseMoved:event]; } } - (void)mouseDragged:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self signalMouseMovement:event]; } else { [super mouseDragged:event]; } } - (void)mouseDown:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseDownEvent:event]; } else { [super mouseDown:event]; } } - (void)mouseUp:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseUpEvent:event]; } else { [super mouseUp:event]; } } - (void)rightMouseDown:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseDownEvent:event]; } else { [super rightMouseUp:event]; } } - (void)rightMouseUp:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseUpEvent:event]; } else { [super rightMouseUp:event]; } } - (void)otherMouseDown:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseDownEvent:event]; } else { [super otherMouseDown:event]; } } - (void)otherMouseUp:(NSEvent *)event { if ([self.adapter mouseEnabled]) { [self mouseUpEvent:event]; } else { [super otherMouseUp:event]; } } - (void)preciseScroll:(NSEvent *)event { CGFloat delta; int cmd; if (fabs([event deltaY]) >= fabs([event deltaX])) { delta = [event deltaY] * 0.1; cmd = delta > 0 ? MP_AXIS_UP : MP_AXIS_DOWN; } else { delta = [event deltaX] * 0.1; cmd = delta > 0 ? MP_AXIS_RIGHT : MP_AXIS_LEFT; } [self.adapter putAxis:cmd delta:fabs(delta)]; } - (void)scrollWheel:(NSEvent *)event { if (![self.adapter mouseEnabled]) { [super scrollWheel:event]; return; } if ([event hasPreciseScrollingDeltas]) { [self preciseScroll:event]; } else { const int modifiers = [event modifierFlags]; const float deltaX = (modifiers & NSEventModifierFlagShift) ? [event scrollingDeltaY] : [event scrollingDeltaX]; const float deltaY = (modifiers & NSEventModifierFlagShift) ? [event scrollingDeltaX] : [event scrollingDeltaY]; int mpkey; if (fabs(deltaY) >= fabs(deltaX)) { mpkey = deltaY > 0 ? MP_WHEEL_UP : MP_WHEEL_DOWN; } else { mpkey = deltaX > 0 ? MP_WHEEL_LEFT : MP_WHEEL_RIGHT; } [self.adapter putKey:mpkey withModifiers:modifiers]; } } - (void)mouseDownEvent:(NSEvent *)event { [self putMouseEvent:event withState:MP_KEY_STATE_DOWN]; if ([event clickCount] > 1) [self putMouseEvent:event withState:MP_KEY_STATE_UP]; } - (void)mouseUpEvent:(NSEvent *)event { [self putMouseEvent:event withState:MP_KEY_STATE_UP]; } - (void)putMouseEvent:(NSEvent *)event withState:(int)state { self.hasMouseDown = (state == MP_KEY_STATE_DOWN); int mpkey = [self mpvButtonNumber:event]; [self.adapter putKey:(mpkey | state) withModifiers:[event modifierFlags]]; } - (NSDragOperation)draggingEntered:(id )sender { NSPasteboard *pboard = [sender draggingPasteboard]; NSArray *types = [pboard types]; if ([types containsObject:NSFilenamesPboardType] || [types containsObject:NSURLPboardType]) { return NSDragOperationCopy; } else { return NSDragOperationNone; } } - (BOOL)performDragOperation:(id )sender { NSPasteboard *pboard = [sender draggingPasteboard]; if ([[pboard types] containsObject:NSFilenamesPboardType]) { NSArray *pbitems = [pboard propertyListForType:NSFilenamesPboardType]; [self.adapter handleFilesArray:pbitems]; return YES; } else if ([[pboard types] containsObject:NSURLPboardType]) { NSURL *url = [NSURL URLFromPasteboard:pboard]; [self.adapter handleFilesArray:@[[url absoluteString]]]; return YES; } return NO; } - (int)mpvButtonNumber:(NSEvent*)event { int buttonNumber = [event buttonNumber]; switch (buttonNumber) { case 0: return MP_MBTN_LEFT; case 1: return MP_MBTN_RIGHT; case 2: return MP_MBTN_MID; case 3: return MP_MBTN_BACK; case 4: return MP_MBTN_FORWARD; default: return MP_MBTN9 - 5 + buttonNumber; } } @end