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. --- Makefile | 10 +- 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 +++++++++++ video/out/cocoa_common.h | 14 +- video/out/cocoa_common.m | 665 +++++++++++-------------------------------- 10 files changed, 722 insertions(+), 515 deletions(-) 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 diff --git a/Makefile b/Makefile index 2e93e1f7d5..0c9eabc806 100644 --- a/Makefile +++ b/Makefile @@ -44,11 +44,15 @@ SOURCES-$(LIBBS2B) += audio/filter/af_bs2b.c SOURCES-$(LIBPOSTPROC) += video/filter/vf_pp.c SOURCES-$(LIBSMBCLIENT) += stream/stream_smb.c -SOURCES-$(COCOA) += video/out/cocoa_common.m \ - osdep/path-macosx.m \ +SOURCES-$(COCOA) += video/out/cocoa/view.m \ + video/out/cocoa/window.m \ + video/out/cocoa/additions.m \ + video/out/cocoa_common.m \ osdep/macosx_application.m \ osdep/macosx_events.m \ - osdep/ar/HIDRemote.m + osdep/ar/HIDRemote.m \ + osdep/path-macosx.m + SOURCES-$(MNG) += demux/demux_mng.c SOURCES-$(MPG123) += audio/decode/ad_mpg123.c 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 + diff --git a/video/out/cocoa_common.h b/video/out/cocoa_common.h index 081c43d2ce..5584ae6b05 100644 --- a/video/out/cocoa_common.h +++ b/video/out/cocoa_common.h @@ -1,20 +1,20 @@ /* * Cocoa OpenGL Backend * - * This file is part of mplayer2. + * This file is part of mpv. * - * mplayer2 is free software; you can redistribute it and/or modify + * 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. * - * mplayer2 is distributed in the hope that it will be useful, + * 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 + * 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 mplayer2. If not, see . + * with mpv. If not, see . */ #ifndef MPLAYER_COCOA_COMMON_H @@ -29,7 +29,6 @@ void *vo_cocoa_glgetaddr(const char *s); int vo_cocoa_init(struct vo *vo); void vo_cocoa_uninit(struct vo *vo); -int vo_cocoa_change_attributes(struct vo *vo); int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, uint32_t d_height, uint32_t flags, int gl3profile); @@ -37,8 +36,6 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, void vo_cocoa_set_current_context(struct vo *vo, bool current); void vo_cocoa_swap_buffers(struct vo *vo); int vo_cocoa_check_events(struct vo *vo); -void vo_cocoa_fullscreen(struct vo *vo); -void vo_cocoa_ontop(struct vo *vo); int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg); void vo_cocoa_register_resize_callback(struct vo *vo, @@ -49,7 +46,6 @@ int vo_cocoa_swap_interval(int enabled); void *vo_cocoa_cgl_context(struct vo *vo); void *vo_cocoa_cgl_pixel_format(struct vo *vo); - int vo_cocoa_cgl_color_size(struct vo *vo); #endif /* MPLAYER_COCOA_COMMON_H */ diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m index 9a5945d82d..612ea27c73 100644 --- a/video/out/cocoa_common.m +++ b/video/out/cocoa_common.m @@ -1,82 +1,54 @@ /* * Cocoa OpenGL Backend * - * This file is part of mplayer2. + * This file is part of mpv. * - * mplayer2 is free software; you can redistribute it and/or modify + * 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. * - * mplayer2 is distributed in the hope that it will be useful, + * 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 mplayer2. If not, see . + * with mpv. If not, see . */ #import #import // for CGDisplayHideCursor #import #include -#include #include "cocoa_common.h" +#include "video/out/cocoa/window.h" +#include "video/out/cocoa/view.h" +#import "video/out/cocoa/mpvadapter.h" + +#include "osdep/macosx_application.h" +#include "osdep/macosx_events.h" #include "config.h" #include "mpvcore/options.h" -#include "vo.h" -#include "aspect.h" +#include "video/out/vo.h" +#include "video/out/aspect.h" #include "mpvcore/input/input.h" #include "talloc.h" #include "mpvcore/mp_msg.h" -#include "osdep/macosx_application.h" -#include "osdep/macosx_events.h" -#include "osdep/macosx_compat.h" - -@interface GLMPlayerWindow : NSWindow -- (BOOL)canBecomeKeyWindow; -- (BOOL)canBecomeMainWindow; -- (void)fullscreen; -- (void)mulSize:(float)multiplier; -- (int)titleHeight; -- (void)setCenteredContentSize:(NSSize)newSize; -@property(nonatomic, assign) struct vo *videoOutput; -@end - -@interface GLMPlayerOpenGLView : NSView { - BOOL hasMouseDown; -} -@property(nonatomic, retain) NSTrackingArea *tracker; -@property(nonatomic, assign) struct vo *videoOutput; -- (BOOL)canHideCursor; -- (void)recalcDraggableState; -- (void)signalMousePosition; -@end - -@interface NSScreen (mpvadditions) -- (BOOL)hasDock; -- (BOOL)hasMenubar; -@end - -@interface NSEvent (mpvadditions) -- (int)mpvButtonNumber; -@end +static void vo_cocoa_fullscreen(struct vo *vo); +static void vo_cocoa_ontop(struct vo *vo); struct vo_cocoa_state { - GLMPlayerWindow *window; - GLMPlayerOpenGLView *view; - NSOpenGLContext *glContext; - NSOpenGLPixelFormat *pixelFormat; - - NSSize current_video_size; - NSSize previous_video_size; + MpvVideoWindow *window; + MpvVideoView *view; + NSOpenGLContext *gl_ctx; + NSOpenGLPixelFormat *gl_pixfmt; NSScreen *current_screen; NSScreen *fs_screen; @@ -86,14 +58,11 @@ struct vo_cocoa_state { struct aspect_data aspdat; bool did_resize; - bool did_async_resize; - bool out_fs_resize; - bool want_redraw; + bool skip_next_swap_buffer; + bool inside_sync_section; IOPMAssertionID power_mgmt_assertion; - CGFloat accumulated_scroll; - NSLock *lock; bool enable_resize_redraw; void (*resize_redraw)(struct vo *vo, int w, int h); @@ -101,31 +70,6 @@ struct vo_cocoa_state { struct mp_log *log; }; -static struct vo_cocoa_state *vo_cocoa_init_state(struct vo *vo) -{ - struct vo_cocoa_state *s = talloc_ptrtype(vo, s); - *s = (struct vo_cocoa_state){ - .did_resize = NO, - .did_async_resize = NO, - .want_redraw = NO, - .current_video_size = {0,0}, - .previous_video_size = {0,0}, - .out_fs_resize = NO, - .power_mgmt_assertion = kIOPMNullAssertionID, - .accumulated_scroll = 0, - .lock = [[NSLock alloc] init], - .enable_resize_redraw = NO, - .log = mp_log_new(s, vo->log, "cocoa"), - }; - return s; -} - -static NSRect to_pixels(struct vo *vo, NSRect frame) -{ - struct vo_cocoa_state *s = vo->cocoa; - return [s->view convertRectToBacking: frame]; -} - void *vo_cocoa_glgetaddr(const char *s) { void *ret = NULL; @@ -160,8 +104,17 @@ static void disable_power_management(struct vo *vo) int vo_cocoa_init(struct vo *vo) { - vo->cocoa = vo_cocoa_init_state(vo); - + struct vo_cocoa_state *s = talloc_zero(vo, struct vo_cocoa_state); + *s = (struct vo_cocoa_state){ + .did_resize = false, + .skip_next_swap_buffer = false, + .inside_sync_section = false, + .power_mgmt_assertion = kIOPMNullAssertionID, + .lock = [[NSLock alloc] init], + .enable_resize_redraw = NO, + .log = mp_log_new(s, vo->log, "cocoa"), + }; + vo->cocoa = s; return 1; } @@ -185,13 +138,21 @@ void vo_cocoa_uninit(struct vo *vo) enable_power_management(vo); [NSApp setPresentationOptions:NSApplicationPresentationDefault]; - if (vo->opts->fullscreen) + // XXX: It looks like there are some circular retain cycles for the + // video view / video window that cause them to not be deallocated, + // This is a workaround to make the fullscreen window be released, + // but the retain cycle problems should be investigated. + if ([s->view isInFullScreenMode]) [[s->view window] release]; [s->window release]; s->window = nil; - [s->glContext release]; - s->glContext = nil; + + [s->gl_ctx release]; + s->gl_ctx = nil; + + [s->lock release]; + s->lock = nil; }); } @@ -226,7 +187,7 @@ static int get_screen_handle(struct vo *vo, int identifier, NSWindow *window, } } -static void update_screen_info(struct vo *vo) +static void vo_cocoa_update_screens_pointers(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; struct mp_vo_opts *opts = vo->opts; @@ -239,7 +200,7 @@ static void vo_cocoa_update_screen_info(struct vo *vo) struct vo_cocoa_state *s = vo->cocoa; struct mp_vo_opts *opts = vo->opts; - update_screen_info(vo); + vo_cocoa_update_screens_pointers(vo); NSRect r = [s->current_screen frame]; @@ -253,10 +214,10 @@ static void vo_cocoa_update_screen_info(struct vo *vo) static void resize_window(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - NSRect frame = to_pixels(vo, [s->view frame]); + NSRect frame = [s->view frameInPixels]; vo->dwidth = frame.size.width; vo->dheight = frame.size.height; - [s->glContext update]; + [s->gl_ctx update]; } static void vo_set_level(struct vo *vo, int ontop) @@ -272,28 +233,13 @@ static void vo_set_level(struct vo *vo, int ontop) [s->window setLevel:s->window_level]; } -void vo_cocoa_ontop(struct vo *vo) +static void vo_cocoa_ontop(struct vo *vo) { struct mp_vo_opts *opts = vo->opts; opts->ontop = !opts->ontop; vo_set_level(vo, opts->ontop); } -static void update_state_sizes(struct vo_cocoa_state *s, - uint32_t d_width, uint32_t d_height) -{ - if (s->current_video_size.width > 0 || s->current_video_size.height > 0) - s->previous_video_size = s->current_video_size; - s->current_video_size = NSMakeSize(d_width, d_height); -} - -static void resize_window_from_stored_size(struct vo *vo) -{ - struct vo_cocoa_state *s = vo->cocoa; - [s->window setCenteredContentSize:s->current_video_size]; - [s->window setContentAspectRatio:s->current_video_size]; -} - static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, uint32_t flags) { @@ -311,12 +257,12 @@ static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, } s->window = - [[GLMPlayerWindow alloc] initWithContentRect:contentRect - styleMask:window_mask - backing:NSBackingStoreBuffered - defer:NO]; + [[MpvVideoWindow alloc] initWithContentRect:contentRect + styleMask:window_mask + backing:NSBackingStoreBuffered + defer:NO]; + s->view = [[[MpvVideoView alloc] initWithFrame:contentRect] autorelease]; - s->view = [[GLMPlayerOpenGLView alloc] initWithFrame:contentRect]; [s->view setWantsBestResolutionOpenGLSurface:YES]; cocoa_register_menu_item_action(MPM_H_SIZE, @selector(halfSize)); @@ -327,10 +273,12 @@ static void create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, [s->window setRestorable:NO]; [s->window setContentView:s->view]; - [s->view release]; - [s->glContext setView:s->view]; - s->window.videoOutput = vo; - s->view.videoOutput = vo; + [s->gl_ctx setView:s->view]; + + MpvCocoaAdapter *adapter = [[[MpvCocoaAdapter alloc] init] autorelease]; + adapter.vout = vo; + s->view.adapter = adapter; + s->window.adapter = adapter; [s->window setDelegate:s->window]; [s->window makeMainWindow]; @@ -367,19 +315,19 @@ static int create_gl_context(struct vo *vo, int gl3profile) 0 }; - s->pixelFormat = + s->gl_pixfmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease]; - if (!s->pixelFormat) { + if (!s->gl_pixfmt) { MP_ERR(s, "Trying to build invalid OpenGL pixel format\n"); return -1; } - s->glContext = - [[NSOpenGLContext alloc] initWithFormat:s->pixelFormat + s->gl_ctx = + [[NSOpenGLContext alloc] initWithFormat:s->gl_pixfmt shareContext:nil]; - [s->glContext makeCurrentContext]; + [s->gl_ctx makeCurrentContext]; return 0; } @@ -390,38 +338,38 @@ static void cocoa_set_window_title(struct vo *vo, const char *title) [s->window setTitle: [NSString stringWithUTF8String:title]]; } -static void update_window(struct vo *vo) +static void update_window(struct vo *vo, int d_width, int d_height) { struct vo_cocoa_state *s = vo->cocoa; - - if (!CGSizeEqualToSize(s->current_video_size, s->previous_video_size)) { - if (vo->opts->fullscreen) { - // we will resize as soon as we get out of fullscreen - s->out_fs_resize = YES; - } else { - // only if we are not in fullscreen and the video size did - // change we resize the window and set a new aspect ratio - resize_window_from_stored_size(vo); - } - } - - [s->view recalcDraggableState]; + [s->window queueNewVideoSize:NSMakeSize(d_width, d_height)]; cocoa_set_window_title(vo, vo_get_window_title(vo)); - vo_cocoa_fullscreen(vo); } -static void resize_redraw(struct vo *vo, int width, int height) +static void vo_cocoa_resize_redraw(struct vo *vo, int width, int height) { struct vo_cocoa_state *s = vo->cocoa; - if (s->resize_redraw) { + + if (!s->resize_redraw) + return; + + if (!s->inside_sync_section) vo_cocoa_set_current_context(vo, true); - [s->glContext update]; + + [s->gl_ctx update]; + + if (s->enable_resize_redraw) { s->resize_redraw(vo, width, height); - [s->glContext flushBuffer]; - s->did_async_resize = YES; - vo_cocoa_set_current_context(vo, false); + s->skip_next_swap_buffer = true; + } else { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } + + [s->gl_ctx flushBuffer]; + + if (!s->inside_sync_section) + vo_cocoa_set_current_context(vo, false); } int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, @@ -430,11 +378,11 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, { struct vo_cocoa_state *s = vo->cocoa; __block int ctxok = 0; - s->enable_resize_redraw = NO; dispatch_sync(dispatch_get_main_queue(), ^{ + s->inside_sync_section = true; + s->enable_resize_redraw = false; s->aspdat = vo->aspdat; - update_state_sizes(s, d_width, d_height); if (flags & VOFLAG_HIDDEN) { // This is certainly the first execution of vo_config_window and @@ -444,12 +392,12 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, // a drawable. ctxok = create_gl_context(vo, gl3profile); if (ctxok < 0) return; - } else if (!s->glContext || !s->window) { - // Either glContext+Window or Window alone are not created. + } else if (!s->gl_ctx || !s->window) { + // Either gl_ctx+window or window alone is not created. // Handle each of them independently. This is to handle correctly // both VOs like vo_corevideo who skip the the OpenGL detection // phase completly and generic OpenGL VOs who use VOFLAG_HIDDEN. - if (!s->glContext) { + if (!s->gl_ctx) { ctxok = create_gl_context(vo, gl3profile); if (ctxok < 0) return; } @@ -460,31 +408,29 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t d_width, if (s->window) { // Everything is properly initialized - update_window(vo); + update_window(vo, d_width, d_height); } }); + dispatch_sync(dispatch_get_main_queue(), ^{ + s->inside_sync_section = false; + s->enable_resize_redraw = true; + }); + if (ctxok < 0) return ctxok; - [vo->cocoa->glContext makeCurrentContext]; - s->enable_resize_redraw = YES; + [vo->cocoa->gl_ctx makeCurrentContext]; return 0; } -static bool resize_callback_registered(struct vo *vo) -{ - struct vo_cocoa_state *s = vo->cocoa; - return s->enable_resize_redraw && !!s->resize_redraw; -} - void vo_cocoa_set_current_context(struct vo *vo, bool current) { struct vo_cocoa_state *s = vo->cocoa; if (current) { [s->lock lock]; - [s->glContext makeCurrentContext]; + [s->gl_ctx makeCurrentContext]; } else { [NSOpenGLContext clearCurrentContext]; [s->lock unlock]; @@ -494,14 +440,14 @@ void vo_cocoa_set_current_context(struct vo *vo, bool current) void vo_cocoa_swap_buffers(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - if (s->did_async_resize && resize_callback_registered(vo)) { - // when in live resize the GL view asynchronously updates itself from + if (s->skip_next_swap_buffer) { + // When in live resize the GL view asynchronously updates itself from // it's drawRect: implementation and calls flushBuffer. This means the // backbuffer is probably in an inconsistent state, so we skip one // flushBuffer call here on the playloop thread. - s->did_async_resize = NO; + s->skip_next_swap_buffer = false; } else { - [s->glContext flushBuffer]; + [s->gl_ctx flushBuffer]; } } @@ -510,25 +456,44 @@ int vo_cocoa_check_events(struct vo *vo) struct vo_cocoa_state *s = vo->cocoa; if (s->did_resize) { - s->did_resize = NO; + s->did_resize = false; resize_window(vo); return VO_EVENT_RESIZE; } - if (s->want_redraw) { - s->want_redraw = NO; - vo->want_redraw = true; - } - return 0; } -void vo_cocoa_fullscreen(struct vo *vo) +static void vo_cocoa_fullscreen_sync(struct vo *vo) +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + vo->cocoa->inside_sync_section = true; + vo->cocoa->enable_resize_redraw = false; + + vo_cocoa_fullscreen(vo); + + vo->cocoa->enable_resize_redraw = true; + vo->cocoa->inside_sync_section = false; + }); +} + +static void vo_cocoa_fullscreen(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - [s->window performSelectorOnMainThread:@selector(fullscreen) - withObject:nil - waitUntilDone:YES]; + struct mp_vo_opts *opts = vo->opts; + + vo_cocoa_update_screen_info(vo); + + if (opts->native_fs) { + [s->window setFullScreen:opts->fullscreen]; + } else { + [s->view setFullScreen:opts->fullscreen]; + } + + [s->window didChangeFullScreenState]; + + // Make the core aware of the view size change. + resize_window(vo); } int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) @@ -538,7 +503,7 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) *events |= vo_cocoa_check_events(vo); return VO_TRUE; case VOCTRL_FULLSCREEN: - vo_cocoa_fullscreen(vo); + vo_cocoa_fullscreen_sync(vo); *events |= VO_EVENT_RESIZE; return VO_TRUE; case VOCTRL_ONTOP: @@ -550,10 +515,9 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) case VOCTRL_SET_CURSOR_VISIBILITY: vo_cocoa_set_cursor_visibility(vo, arg); return VO_TRUE; - case VOCTRL_UPDATE_WINDOW_TITLE: { + case VOCTRL_UPDATE_WINDOW_TITLE: cocoa_set_window_title(vo, (const char *) arg); return VO_TRUE; - } case VOCTRL_RESTORE_SCREENSAVER: enable_power_management(vo); return VO_TRUE; @@ -574,7 +538,7 @@ int vo_cocoa_swap_interval(int enabled) void *vo_cocoa_cgl_context(struct vo *vo) { struct vo_cocoa_state *s = vo->cocoa; - return [s->glContext CGLContextObj]; + return [s->gl_ctx CGLContextObj]; } void *vo_cocoa_cgl_pixel_format(struct vo *vo) @@ -598,361 +562,56 @@ int vo_cocoa_cgl_color_size(struct vo *vo) return 8; } -@implementation GLMPlayerWindow -@synthesize videoOutput = _video_output; -- (void)windowDidResize:(NSNotification *) notification -{ - if (self.videoOutput) { - struct vo_cocoa_state *s = self.videoOutput->cocoa; - s->did_resize = YES; - } -} - -- (void)windowDidChangeBackingProperties:(NSNotification *)notification { - if (self.videoOutput) { - struct vo_cocoa_state *s = self.videoOutput->cocoa; - s->did_resize = YES; - } -} - -- (BOOL)isInFullScreenMode -{ - return (([self styleMask] & NSFullScreenWindowMask) == - NSFullScreenWindowMask); -} - -- (void)toggleMissionControlFullScreen:(BOOL)willBeFullscreen -{ - struct vo_cocoa_state *s = self.videoOutput->cocoa; - - if (willBeFullscreen && ![self isInFullScreenMode]) { - [self setContentResizeIncrements:NSMakeSize(1, 1)]; - [self toggleFullScreen:nil]; - } - - if (!willBeFullscreen && [self isInFullScreenMode]) { - [self setContentAspectRatio:s->current_video_size]; - [self toggleFullScreen:nil]; - } - -} -- (void)toggleViewFullscreen:(BOOL)willBeFullscreen -{ - struct vo_cocoa_state *s = self.videoOutput->cocoa; - - if (willBeFullscreen && ![s->view isInFullScreenMode]) { - NSApplicationPresentationOptions popts = - NSApplicationPresentationDefault; - - if ([s->fs_screen 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 ([s->fs_screen hasDock]) - popts |= NSApplicationPresentationAutoHideDock; - - NSDictionary *fsopts = @{ - NSFullScreenModeAllScreens : @NO, - NSFullScreenModeApplicationPresentationOptions : @(popts) - }; - - [s->view enterFullScreenMode:s->fs_screen withOptions:fsopts]; - - // The original "windowed" window will staty 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. - [s->window orderOut:self]; - } - - if (!willBeFullscreen && [s->view isInFullScreenMode]) { - [s->view exitFullScreenModeWithOptions:nil]; - - // Show the "windowed" window again. - [s->window makeKeyAndOrderFront:self]; - [self makeFirstResponder:s->view]; - } -} -- (void)fullscreen -{ - struct vo_cocoa_state *s = self.videoOutput->cocoa; - struct mp_vo_opts *opts = self.videoOutput->opts; - - vo_cocoa_update_screen_info(self.videoOutput); - - // Go use the fullscreen API selected by the user. View-based or Mission - // Control. - if (opts->native_fs) { - [self toggleMissionControlFullScreen:opts->fullscreen]; - } else { - [self toggleViewFullscreen:opts->fullscreen]; - } - - [s->view recalcDraggableState]; - - // Change window size if the core attempted to change it while we were in - // fullscreen. For example config() might have been called as a result of - // a new file changing the window size. - if (!opts->fullscreen && s->out_fs_resize) { - resize_window_from_stored_size(self.videoOutput); - s->out_fs_resize = NO; - } - - // Make the core aware of the view size change. - resize_window(self.videoOutput); -} - -- (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.videoOutput->opts->fullscreen) { - NSSize size = { - .width = self.videoOutput->cocoa->aspdat.prew * multiplier, - .height = self.videoOutput->cocoa->aspdat.preh * multiplier - }; - [self setCenteredContentSize:size]; - [(GLMPlayerOpenGLView *)self.contentView signalMousePosition]; - } -} - -- (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; -} - -@end - -@implementation GLMPlayerOpenGLView -@synthesize tracker = _tracker; -@synthesize videoOutput = _video_output; -// 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; } +@implementation MpvCocoaAdapter +@synthesize vout = _video_output; -- (BOOL)canHideCursor -{ - return !self->hasMouseDown && [self containsMouseLocation]; +- (void)setNeedsResize { + struct vo_cocoa_state *s = self.vout->cocoa; + s->did_resize = true; } -- (void)recalcDraggableState +- (void)recalcMovableByWindowBackground:(NSPoint)p { - struct vo *vo = self.videoOutput; - BOOL movable = NO; - - if (!vo->opts->fullscreen) { - NSPoint loc = [self mouseLocation]; - movable = !mp_input_test_dragging(vo->input_ctx, loc.x, loc.y); + BOOL movable = NO; + if (![self isInFullScreenMode]) { + movable = !mp_input_test_dragging(self.vout->input_ctx, p.x, p.y); } - [self.window setMovableByWindowBackground:movable]; -} - -- (void)mouseEntered:(NSEvent *)event -{ - // do nothing! -} - -- (void)mouseExited:(NSEvent *)event -{ - cocoa_put_key(MP_KEY_MOUSE_LEAVE); -} - -- (void)signalMousePosition -{ - NSPoint p = [self convertPoint:[self mouseLocation] fromView:nil]; - vo_mouse_movement(self.videoOutput, p.x, p.y); -} - -- (void)signalMouseMovement:(NSEvent *)event -{ - [self recalcDraggableState]; - NSPoint p = [self convertPoint:[event locationInWindow] fromView:nil]; - vo_mouse_movement(self.videoOutput, p.x, p.y); + [self.vout->cocoa->window setMovableByWindowBackground:movable]; } -- (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 -{ - struct vo *vo = self.videoOutput; - 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]) { - mp_input_put_axis(vo->input_ctx, cmd, delta); - } else { - if (delta > 0) - cocoa_put_key_with_modifiers(MP_MOUSE_BTN3, [event modifierFlags]); - else - cocoa_put_key_with_modifiers(MP_MOUSE_BTN4, [event modifierFlags]); - } +- (void)signalMouseMovement:(NSPoint)point { + vo_mouse_movement(self.vout, point.x, point.y); + [self recalcMovableByWindowBackground:point]; } -- (void)mouseDownEvent:(NSEvent *)event +- (void)putKey:(int)mpkey withModifiers:(int)modifiers { - [self putMouseEvent:event withState:MP_KEY_STATE_DOWN]; - - if ([event clickCount] > 1) - [self putMouseEvent:event withState:MP_KEY_STATE_UP]; + cocoa_put_key_with_modifiers(mpkey, modifiers); } -- (void)mouseUpEvent:(NSEvent *)event +- (void)putAxis:(int)mpkey delta:(float)delta; { - [self putMouseEvent:event withState:MP_KEY_STATE_UP]; + mp_input_put_axis(self.vout->input_ctx, mpkey, delta); } -- (void)putMouseEvent:(NSEvent *)event withState:(int)state -{ - self->hasMouseDown = (state == MP_KEY_STATE_DOWN); - int mp_key = (MP_MOUSE_BTN0 + [event mpvButtonNumber]); - cocoa_put_key_with_modifiers(mp_key | state, [event modifierFlags]); +- (void)performAsyncResize:(NSSize)size { + vo_cocoa_resize_redraw(self.vout, size.width, size.height); } -- (void)drawRect:(NSRect)rect -{ - struct vo *vo = [self videoOutput]; - - if (vo && resize_callback_registered(vo)) { - if ([self inLiveResize]) { - NSSize size = to_pixels(vo, [self bounds]).size; - resize_redraw(vo, size.width, size.height); - } else { - // If not in live resize window was probably resized from - // fullscreen toggle or resize. Make sure we invoke a real repaint - // ASAP so that the displayed image is correct. - struct vo_cocoa_state *s = vo->cocoa; - s->want_redraw = YES; - } - } else { - [[NSColor blackColor] set]; - NSRectFill([self bounds]); - } +- (BOOL)isInFullScreenMode { + return self.vout->opts->fullscreen; } -@end -@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]]; +- (NSSize)videoSize { + return (NSSize) { + .width = self.vout->cocoa->aspdat.prew, + .height = self.vout->cocoa->aspdat.preh, + }; } -@end -@implementation NSEvent (mpvadditions) -- (int)mpvButtonNumber -{ - int buttonNumber = [self buttonNumber]; - switch (buttonNumber) { - case 1: return 2; - case 2: return 1; - default: return buttonNumber; - } +- (NSScreen *)fsScreen { + struct vo_cocoa_state *s = self.vout->cocoa; + return s->fs_screen; } @end -- cgit v1.2.3