From 0ab9634eb3f3005c37a0346d9035dbc4d4abd144 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Tue, 3 Sep 2013 21:18:28 +0200 Subject: cocoa_common: split the code, refactoring and cleanups Split the code to several files. The GUI elements now each have they own files and private state. The original code was a mess to respect the retarded mplayer convention of having everything in a single file. This commit also seems to fix the long running bug of artifacts showing randomly when going fullscreen using nVidia GPUs. --- video/out/cocoa/additions.h | 27 ++++++ video/out/cocoa/additions.m | 51 +++++++++++ video/out/cocoa/mpvadapter.h | 32 +++++++ video/out/cocoa/view.h | 30 ++++++ video/out/cocoa/view.m | 213 +++++++++++++++++++++++++++++++++++++++++++ video/out/cocoa/window.h | 34 +++++++ video/out/cocoa/window.m | 161 ++++++++++++++++++++++++++++++++ 7 files changed, 548 insertions(+) create mode 100644 video/out/cocoa/additions.h create mode 100644 video/out/cocoa/additions.m create mode 100644 video/out/cocoa/mpvadapter.h create mode 100644 video/out/cocoa/view.h create mode 100644 video/out/cocoa/view.m create mode 100644 video/out/cocoa/window.h create mode 100644 video/out/cocoa/window.m (limited to 'video/out/cocoa') diff --git a/video/out/cocoa/additions.h b/video/out/cocoa/additions.h new file mode 100644 index 0000000000..8c237c9a40 --- /dev/null +++ b/video/out/cocoa/additions.h @@ -0,0 +1,27 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#import + +@interface NSScreen (mpvadditions) +- (BOOL)hasDock; +- (BOOL)hasMenubar; +@end + +@interface NSEvent (mpvadditions) +- (int)mpvButtonNumber; +@end diff --git a/video/out/cocoa/additions.m b/video/out/cocoa/additions.m new file mode 100644 index 0000000000..3caabef956 --- /dev/null +++ b/video/out/cocoa/additions.m @@ -0,0 +1,51 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#import "additions.h" + +@implementation NSScreen (mpvadditions) +- (BOOL)hasDock +{ + NSRect vF = [self visibleFrame]; + NSRect f = [self frame]; + return + // The visible frame's width is smaller: dock is on left or right end + // of this method's receiver. + vF.size.width < f.size.width || + // The visible frame's veritical origin is bigger: dock is + // on the bottom of this method's receiver. + vF.origin.y > f.origin.y; + +} + +- (BOOL)hasMenubar +{ + return [self isEqual: [NSScreen screens][0]]; +} +@end + +@implementation NSEvent (mpvadditions) +- (int)mpvButtonNumber +{ + int buttonNumber = [self buttonNumber]; + switch (buttonNumber) { + case 1: return 2; + case 2: return 1; + default: return buttonNumber; + } +} +@end diff --git a/video/out/cocoa/mpvadapter.h b/video/out/cocoa/mpvadapter.h new file mode 100644 index 0000000000..eafeefce9f --- /dev/null +++ b/video/out/cocoa/mpvadapter.h @@ -0,0 +1,32 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#import +#include "video/out/vo.h" + +@interface MpvCocoaAdapter : NSObject +- (void)setNeedsResize; +- (void)signalMouseMovement:(NSPoint)point; +- (void)putKey:(int)mpkey withModifiers:(int)modifiers; +- (void)putAxis:(int)mpkey delta:(float)delta; +- (void)performAsyncResize:(NSSize)size; + +- (BOOL)isInFullScreenMode; +- (NSSize)videoSize; +- (NSScreen *)fsScreen; +@property(nonatomic, assign) struct vo *vout; +@end diff --git a/video/out/cocoa/view.h b/video/out/cocoa/view.h new file mode 100644 index 0000000000..71d58a3348 --- /dev/null +++ b/video/out/cocoa/view.h @@ -0,0 +1,30 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#import +#import "video/out/cocoa/mpvadapter.h" + +@interface MpvVideoView : NSView { + BOOL hasMouseDown; +} +@property(nonatomic, retain) MpvCocoaAdapter *adapter; +@property(nonatomic, retain) NSTrackingArea *tracker; +- (void)setFullScreen:(BOOL)willBeFullscreen; +- (NSRect)frameInPixels; +- (BOOL)canHideCursor; +- (void)signalMousePosition; +@end diff --git a/video/out/cocoa/view.m b/video/out/cocoa/view.m new file mode 100644 index 0000000000..bac485b225 --- /dev/null +++ b/video/out/cocoa/view.m @@ -0,0 +1,213 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include + +#include "mpvcore/input/input.h" +#include "mpvcore/input/keycodes.h" + +#include "osdep/macosx_compat.h" +#include "video/out/cocoa_common.h" +#import "video/out/cocoa/additions.h" + +#include "view.h" + +@implementation MpvVideoView +@synthesize adapter = _adapter; +@synthesize tracker = _tracker; + +- (void)setFullScreen:(BOOL)willBeFullscreen +{ + if (willBeFullscreen && ![self isInFullScreenMode]) { + NSApplicationPresentationOptions popts = + NSApplicationPresentationDefault; + + if ([[self.adapter fsScreen] hasMenubar]) + // Cocoa raises an exception when autohiding the menubar but + // not the dock. They probably got bored while programming the + // multi screen support and took some shortcuts (tested on 10.8). + popts |= NSApplicationPresentationAutoHideMenuBar | + NSApplicationPresentationAutoHideDock; + + if ([[self.adapter fsScreen] hasDock]) + popts |= NSApplicationPresentationAutoHideDock; + + NSDictionary *fsopts = @{ + NSFullScreenModeAllScreens : @NO, + NSFullScreenModeApplicationPresentationOptions : @(popts) + }; + + // The original "windowed" window will stay around since sending a + // view fullscreen wraps it in another window. This is noticeable when + // sending the View fullscreen to another screen. Make it go away + // manually. + [self.window orderOut:self]; + + [self enterFullScreenMode:[self.adapter fsScreen] + withOptions:fsopts]; + } + + if (!willBeFullscreen && [self isInFullScreenMode]) { + [self exitFullScreenModeWithOptions:nil]; + + // Show the "windowed" window again. + [self.window makeKeyAndOrderFront:self]; + [self.window makeFirstResponder: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]; + + NSTrackingAreaOptions trackingOptions = + NSTrackingEnabledDuringMouseDrag | + NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | + NSTrackingActiveAlways; + + self.tracker = + [[[NSTrackingArea alloc] initWithRect:[self bounds] + options:trackingOptions + owner:self + userInfo:nil] autorelease]; + + [self addTrackingArea:self.tracker]; +} + +- (NSPoint)mouseLocation +{ + NSPoint wLoc = [self.window mouseLocationOutsideOfEventStream]; + return [self convertPoint:wLoc fromView:nil]; +} + +- (BOOL)containsMouseLocation +{ + NSRect vF = [[self.window screen] visibleFrame]; + NSRect vFW = [self.window convertRectFromScreen:vF]; + NSRect vFV = [self convertRect:vFW fromView:nil]; + + // clip bounds to current visibleFrame + NSRect clippedBounds = CGRectIntersection([self bounds], vFV); + return CGRectContainsPoint(clippedBounds, [self mouseLocation]); +} + +- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent { return YES; } +- (BOOL)acceptsFirstResponder { return YES; } +- (BOOL)becomeFirstResponder { return YES; } +- (BOOL)resignFirstResponder { return YES; } + +- (NSRect)frameInPixels +{ + return [self convertRectToBacking:[self frame]]; +} + +- (BOOL)canHideCursor +{ + return !self->hasMouseDown && [self containsMouseLocation]; +} + +- (void)mouseEntered:(NSEvent *)event +{ + // do nothing! +} + +- (void)mouseExited:(NSEvent *)event +{ + [self.adapter putKey:MP_KEY_MOUSE_LEAVE withModifiers:0]; +} + +- (void)setFrameSize:(NSSize)size +{ + [super setFrameSize:size]; + [self signalMousePosition]; +} + +- (void)signalMousePosition +{ + NSPoint p = [self convertPoint:[self mouseLocation] fromView:nil]; + [self.adapter signalMouseMovement:p]; +} + +- (void)signalMouseMovement:(NSEvent *)event +{ + NSPoint p = [self convertPoint:[event locationInWindow] fromView:nil]; + [self.adapter signalMouseMovement:p]; +} + +- (void)mouseMoved:(NSEvent *)event { [self signalMouseMovement:event]; } +- (void)mouseDragged:(NSEvent *)event { [self signalMouseMovement:event]; } +- (void)mouseDown:(NSEvent *)evt { [self mouseDownEvent:evt]; } +- (void)mouseUp:(NSEvent *)evt { [self mouseUpEvent:evt]; } +- (void)rightMouseDown:(NSEvent *)evt { [self mouseDownEvent:evt]; } +- (void)rightMouseUp:(NSEvent *)evt { [self mouseUpEvent:evt]; } +- (void)otherMouseDown:(NSEvent *)evt { [self mouseDownEvent:evt]; } +- (void)otherMouseUp:(NSEvent *)evt { [self mouseUpEvent:evt]; } + +- (void)scrollWheel:(NSEvent *)event +{ + CGFloat delta; + int cmd; + + if (FFABS([event deltaY]) >= FFABS([event deltaX])) { + delta = [event deltaY] * 0.1; + cmd = delta > 0 ? MP_AXIS_UP : MP_AXIS_DOWN; + delta = FFABS(delta); + } else { + delta = [event deltaX] * 0.1; + cmd = delta > 0 ? MP_AXIS_RIGHT : MP_AXIS_LEFT; + delta = FFABS(delta); + } + + if ([event hasPreciseScrollingDeltas]) { + [self.adapter putAxis:cmd delta:delta]; + } else { + const int modifiers = [event modifierFlags]; + const int mpkey = delta > 0 ? MP_MOUSE_BTN3 : MP_MOUSE_BTN4; + [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 = (MP_MOUSE_BTN0 + [event mpvButtonNumber]); + [self.adapter putKey:(mpkey | state) withModifiers:[event modifierFlags]]; +} + +- (void)drawRect:(NSRect)rect +{ + [self.adapter performAsyncResize:[self frameInPixels].size]; + [self.adapter setNeedsResize]; +} +@end diff --git a/video/out/cocoa/window.h b/video/out/cocoa/window.h new file mode 100644 index 0000000000..d6c753cc1b --- /dev/null +++ b/video/out/cocoa/window.h @@ -0,0 +1,34 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#import +#import "video/out/cocoa/mpvadapter.h" + +@interface MpvVideoWindow : NSWindow +@property(nonatomic, retain) MpvCocoaAdapter *adapter; +- (void)setFullScreen:(BOOL)willBeFullscreen; +- (BOOL)canBecomeKeyWindow; +- (BOOL)canBecomeMainWindow; +- (void)mulSize:(float)multiplier; +- (void)setCenteredContentSize:(NSSize)newSize; + +- (void)queueNewVideoSize:(NSSize)newSize; +- (void)dispatchNewVideoSize; + +// This really needs to use KVO +- (void)didChangeFullScreenState; +@end diff --git a/video/out/cocoa/window.m b/video/out/cocoa/window.m new file mode 100644 index 0000000000..fa51b8d82a --- /dev/null +++ b/video/out/cocoa/window.m @@ -0,0 +1,161 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or modify + * 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. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with mpv. If not, see . + */ + +#include "mpvcore/input/keycodes.h" + +#include "osdep/macosx_application.h" +#include "osdep/macosx_events.h" +#include "osdep/macosx_compat.h" + +#include "video/out/cocoa/additions.h" +#include "video/out/cocoa_common.h" + +#include "window.h" + +@implementation MpvVideoWindow { + NSSize _queued_video_size; + bool _fs_resize_scheduled; +} + +@synthesize adapter = _adapter; +- (id)initWithContentRect:(NSRect)content_rect + styleMask:(NSUInteger)style_mask + backing:(NSBackingStoreType)buffering_type + defer:(BOOL)flag +{ + if (self = [super initWithContentRect:content_rect + styleMask:style_mask + backing:buffering_type + defer:flag]) { + [self setBackgroundColor:[NSColor blackColor]]; + } + return self; +} + +- (void)windowDidResize:(NSNotification *) notification +{ + [self.adapter setNeedsResize]; +} + +- (void)windowDidChangeBackingProperties:(NSNotification *)notification { + [self.adapter setNeedsResize]; +} + +- (BOOL)isInFullScreenMode +{ + return (([self styleMask] & NSFullScreenWindowMask) == + NSFullScreenWindowMask); +} + +- (void)setFullScreen:(BOOL)willBeFullscreen +{ + if (willBeFullscreen && ![self isInFullScreenMode]) { + [self setContentResizeIncrements:NSMakeSize(1, 1)]; + [self toggleFullScreen:nil]; + } + + if (!willBeFullscreen && [self isInFullScreenMode]) { + [self setContentAspectRatio:self->_queued_video_size]; + [self toggleFullScreen:nil]; + } + +} + +- (BOOL)canBecomeMainWindow { return YES; } +- (BOOL)canBecomeKeyWindow { return YES; } +- (BOOL)windowShouldClose:(id)sender +{ + cocoa_put_key(MP_KEY_CLOSE_WIN); + // We have to wait for MPlayer to handle this, + // otherwise we are in trouble if the + // MP_KEY_CLOSE_WIN handler is disabled + return NO; +} + +- (void)normalSize { [self mulSize:1.0f]; } + +- (void)halfSize { [self mulSize:0.5f];} + +- (void)doubleSize { [self mulSize:2.0f];} + +- (void)mulSize:(float)multiplier +{ + if (![self.adapter isInFullScreenMode]) { + NSSize size = [self.adapter videoSize]; + size.width *= multiplier; + size.height *= multiplier; + [self setCenteredContentSize:size]; + } +} + +- (int)titleHeight +{ + NSRect of = [self frame]; + NSRect cb = [[self contentView] bounds]; + return of.size.height - cb.size.height; +} + +- (void)setCenteredContentSize:(NSSize)ns +{ + NSRect f = [self frame]; + CGFloat dx = (f.size.width - ns.width) / 2; + CGFloat dy = (f.size.height - ns.height - [self titleHeight]) / 2; + NSRect nf = NSRectFromCGRect(CGRectInset(NSRectToCGRect(f), dx, dy)); + [self setFrame:nf display:NO animate:NO]; +} + +- (NSRect)constrainFrameRect:(NSRect)nf toScreen:(NSScreen *)screen +{ + NSRect s = [[self screen] visibleFrame]; + if (nf.origin.y + nf.size.height > s.origin.y + s.size.height) + nf.origin.y = s.origin.y + s.size.height - nf.size.height; + return nf; +} + +- (void)queueNewVideoSize:(NSSize)new_size +{ + NSSize prev_size = self->_queued_video_size; + self->_queued_video_size = new_size; + + if (!CGSizeEqualToSize(prev_size, new_size)) + [self dispatchNewVideoSize]; +} + +- (void)dispatchNewVideoSize +{ + if ([self.adapter isInFullScreenMode]) { + self->_fs_resize_scheduled = true; + } else { + [self applyNewVideoSize]; + } +} + +- (void)applyNewVideoSize +{ + [self setCenteredContentSize:self->_queued_video_size]; + [self setContentAspectRatio:self->_queued_video_size]; +} + +- (void)didChangeFullScreenState +{ + if (![self.adapter isInFullScreenMode] && self->_fs_resize_scheduled) { + self->_fs_resize_scheduled = false; + [self applyNewVideoSize]; + } +} +@end + -- cgit v1.2.3