From 5865086aa84cae5e5505698ccf115a53a1b6b4fa Mon Sep 17 00:00:00 2001 From: Akemi Date: Wed, 6 Jun 2018 17:04:40 +0200 Subject: cocoa-cb: remove pre-allocation of window, view and layer the pre-allocation was needed because the layer allocated a opengl context async itself and we couldn't influence that. so we had to start the core after the context was actually allocated. furthermore a window, view and layer hierarchy had to be created so the layer would create a context. now, instead of relying on the layer to create a context we do this manually and re-use that context later when the layer wants to create one async itself. --- osdep/macOS_mpv_helper.swift | 7 +++ osdep/macOS_swift_bridge.h | 1 + osdep/macosx_application.h | 5 ++ osdep/macosx_application.m | 70 ++++++++++------------- osdep/macosx_application_objc.h | 2 +- video/out/cocoa-cb/events_view.swift | 5 +- video/out/cocoa-cb/video_layer.swift | 30 ++++------ video/out/cocoa-cb/window.swift | 74 ++++++++++++++++--------- video/out/cocoa_cb_common.swift | 104 +++++++++++++++++------------------ 9 files changed, 158 insertions(+), 140 deletions(-) diff --git a/osdep/macOS_mpv_helper.swift b/osdep/macOS_mpv_helper.swift index b023c4f098..e1fb3cf6cb 100644 --- a/osdep/macOS_mpv_helper.swift +++ b/osdep/macOS_mpv_helper.swift @@ -21,6 +21,12 @@ import OpenGL.GL3 let glDummy: @convention(c) () -> Void = {} +extension Bool { + init(_ num: Int32) { + self.init(num > 0) + } +} + class MPVHelper: NSObject { var mpvHandle: OpaquePointer? @@ -28,6 +34,7 @@ class MPVHelper: NSObject { var mpvLog: OpaquePointer? var inputContext: OpaquePointer? var mpctx: UnsafeMutablePointer? + var macOpts: macos_opts? var fbo: GLint = 1 init(_ mpv: OpaquePointer) { diff --git a/osdep/macOS_swift_bridge.h b/osdep/macOS_swift_bridge.h index b0121d2a0f..4204b514d1 100644 --- a/osdep/macOS_swift_bridge.h +++ b/osdep/macOS_swift_bridge.h @@ -23,6 +23,7 @@ #include "video/out/libmpv.h" #include "libmpv/render_gl.h" +#include "options/m_config.h" #include "player/core.h" #include "input/input.h" #include "video/out/win_state.h" diff --git a/osdep/macosx_application.h b/osdep/macosx_application.h index 96a861fa35..c04f479c5b 100644 --- a/osdep/macosx_application.h +++ b/osdep/macosx_application.h @@ -20,6 +20,11 @@ #include "osdep/macosx_menubar.h" +struct macos_opts { + int macos_title_bar_style; + int macos_fs_animation_duration; +}; + // multithreaded wrapper for mpv_main int cocoa_main(int argc, char *argv[]); void cocoa_register_menu_item_action(MPMenuKey key, void* action); diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m index 4bc8eec0eb..66daa45909 100644 --- a/osdep/macosx_application.m +++ b/osdep/macosx_application.m @@ -40,11 +40,6 @@ #define MPV_PROTOCOL @"mpv://" -struct macos_opts { - int macos_title_bar_style; - int macos_fs_animation_duration; -}; - #define OPT_BASE_STRUCT struct macos_opts const struct m_sub_options macos_conf = { .opts = (const struct m_option[]) { @@ -66,13 +61,7 @@ const struct m_sub_options macos_conf = { // running in libmpv mode, and cocoa_main() was never called. static bool application_instantiated; -struct playback_thread_ctx { - int *argc; - char ***argv; -}; - static pthread_t playback_thread_id; -static struct playback_thread_ctx thread_ctx = {0}; @interface Application () { @@ -92,18 +81,6 @@ static void terminate_cocoa_application(void) [NSApp terminate:NSApp]; } -static void *playback_thread(void *ctx_obj) -{ - mpthread_set_name("playback core (OSX)"); - @autoreleasepool { - struct playback_thread_ctx *ctx = (struct playback_thread_ctx*) ctx_obj; - int r = mpv_main(*ctx->argc, *ctx->argv); - terminate_cocoa_application(); - // normally never reached - unless the cocoa mainloop hasn't started yet - exit(r); - } -} - @implementation Application @synthesize menuBar = _menu_bar; @synthesize openCount = _open_count; @@ -141,12 +118,6 @@ static void *playback_thread(void *ctx_obj) [super dealloc]; } -- (void)initMPVCore -{ - pthread_create(&playback_thread_id, NULL, playback_thread, &thread_ctx); - [[EventsResponder sharedInstance] waitForInputContext]; -} - static const char macosx_icon[] = #include "osdep/macosx_icon.inc" ; @@ -187,9 +158,14 @@ static const char macosx_icon[] = - (void)setMpvHandle:(struct mpv_handle *)ctx { - if (_cocoa_cb) { - [_cocoa_cb setMpvHandle:ctx]; - } +#if HAVE_MACOS_COCOA_CB + [NSApp setCocoaCB:[[CocoaCB alloc] init:ctx]]; +#endif +} + +- (const struct m_sub_options *)getMacOSConf +{ + return &macos_conf; } - (void)queueCommand:(char *)cmd @@ -255,6 +231,11 @@ static const char macosx_icon[] = } @end +struct playback_thread_ctx { + int *argc; + char ***argv; +}; + static void cocoa_run_runloop(void) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; @@ -262,6 +243,18 @@ static void cocoa_run_runloop(void) [pool drain]; } +static void *playback_thread(void *ctx_obj) +{ + mpthread_set_name("playback core (OSX)"); + @autoreleasepool { + struct playback_thread_ctx *ctx = (struct playback_thread_ctx*) ctx_obj; + int r = mpv_main(*ctx->argc, *ctx->argv); + terminate_cocoa_application(); + // normally never reached - unless the cocoa mainloop hasn't started yet + exit(r); + } +} + void cocoa_register_menu_item_action(MPMenuKey key, void* action) { if (application_instantiated) @@ -274,10 +267,6 @@ static void init_cocoa_application(bool regular) [NSApp setDelegate:NSApp]; [NSApp setMenuBar:[[MenuBar alloc] init]]; -#if HAVE_MACOS_COCOA_CB - [NSApp setCocoaCB:[[CocoaCB alloc] init]]; -#endif - // Will be set to Regular from cocoa_common during UI creation so that we // don't create an icon when playing audio only files. [NSApp setActivationPolicy: regular ? @@ -339,8 +328,9 @@ int cocoa_main(int argc, char *argv[]) application_instantiated = true; [[EventsResponder sharedInstance] setIsApplication:YES]; - thread_ctx.argc = &argc; - thread_ctx.argv = &argv; + struct playback_thread_ctx ctx = {0}; + ctx.argc = &argc; + ctx.argv = &argv; if (bundle_started_from_finder(argv)) { setup_bundle(&argc, argv); @@ -353,8 +343,8 @@ int cocoa_main(int argc, char *argv[]) init_cocoa_application(false); } - if (![NSApp cocoaCB]) - [NSApp initMPVCore]; + pthread_create(&playback_thread_id, NULL, playback_thread, &ctx); + [[EventsResponder sharedInstance] waitForInputContext]; cocoa_run_runloop(); // This should never be reached: cocoa_run_runloop blocks until the diff --git a/osdep/macosx_application_objc.h b/osdep/macosx_application_objc.h index 22e6f7e525..1685c99ba8 100644 --- a/osdep/macosx_application_objc.h +++ b/osdep/macosx_application_objc.h @@ -31,7 +31,7 @@ struct mpv_handle; - (void)stopMPV:(char *)cmd; - (void)openFiles:(NSArray *)filenames; - (void)setMpvHandle:(struct mpv_handle *)ctx; -- (void)initMPVCore; +- (const struct m_sub_options *)getMacOSConf; @property(nonatomic, retain) MenuBar *menuBar; @property(nonatomic, assign) size_t openCount; diff --git a/video/out/cocoa-cb/events_view.swift b/video/out/cocoa-cb/events_view.swift index 7cc6a4022d..7cc295f362 100644 --- a/video/out/cocoa-cb/events_view.swift +++ b/video/out/cocoa-cb/events_view.swift @@ -31,9 +31,9 @@ class EventsView: NSView { override var acceptsFirstResponder: Bool { return true } - init(frame frameRect: NSRect, cocoaCB ccb: CocoaCB) { + init(cocoaCB ccb: CocoaCB) { cocoaCB = ccb - super.init(frame: frameRect) + super.init(frame: NSMakeRect(0, 0, 960, 480)) autoresizingMask = [.viewWidthSizable, .viewHeightSizable] wantsBestResolutionOpenGLSurface = true register(forDraggedTypes: [NSFilenamesPboardType, NSURLPboardType]) @@ -249,6 +249,7 @@ class EventsView: NSView { } func canHideCursor() -> Bool { + if cocoaCB.window == nil { return false } return !hasMouseDown && containsMouseLocation() && window!.isKeyWindow } diff --git a/video/out/cocoa-cb/video_layer.swift b/video/out/cocoa-cb/video_layer.swift index 2e347ba22e..c600b06b6c 100644 --- a/video/out/cocoa-cb/video_layer.swift +++ b/video/out/cocoa-cb/video_layer.swift @@ -61,7 +61,15 @@ class VideoLayer: CAOpenGLLayer { super.init() autoresizingMask = [.layerWidthSizable, .layerHeightSizable] backgroundColor = NSColor.black.cgColor - contentsScale = cocoaCB.window.backingScaleFactor + + CGLCreateContext(copyCGLPixelFormat(forDisplayMask: 0), nil, &cglContext) + var i: GLint = 1 + CGLSetParameter(cglContext!, kCGLCPSwapInterval, &i) + CGLSetCurrentContext(cglContext!) + + mpv.initRender() + mpv.setRenderUpdateCallback(updateCallback, context: self) + mpv.setRenderControlCallback(cocoaCB.controlCallback, context: cocoaCB) } override init(layer: Any) { @@ -74,12 +82,6 @@ class VideoLayer: CAOpenGLLayer { fatalError("init(coder:) has not been implemented") } - func setUpRender() { - mpv.initRender() - mpv.setRenderUpdateCallback(updateCallback, context: self) - mpv.setRenderControlCallback(cocoaCB.controlCallback, context: cocoaCB) - } - override func canDraw(inCGLContext ctx: CGLContextObj, pixelFormat pf: CGLPixelFormatObj, forLayerTime t: CFTimeInterval, @@ -177,23 +179,15 @@ class VideoLayer: CAOpenGLLayer { if err != kCGLNoError || pix == nil { let errS = String(cString: CGLErrorString(err)) - print("Couldn't create CGL pixel format: \(errS) (\(err.rawValue))") + mpv.sendError("Couldn't create CGL pixel format: \(errS) (\(err.rawValue))") exit(1) } return pix! } override func copyCGLContext(forPixelFormat pf: CGLPixelFormatObj) -> CGLContextObj { - let ctx = super.copyCGLContext(forPixelFormat: pf) - var i: GLint = 1 - CGLSetParameter(ctx, kCGLCPSwapInterval, &i) - CGLSetCurrentContext(ctx) - cglContext = ctx - - if let app = NSApp as? Application { - app.initMPVCore() - } - return ctx + contentsScale = cocoaCB.window.backingScaleFactor + return cglContext! } let updateCallback: mpv_render_update_fn = { (ctx) in diff --git a/video/out/cocoa-cb/window.swift b/video/out/cocoa-cb/window.swift index b3db7aeb8f..907476fc09 100644 --- a/video/out/cocoa-cb/window.swift +++ b/video/out/cocoa-cb/window.swift @@ -78,28 +78,23 @@ class Window: NSWindow, NSWindowDelegate { } } - convenience init(cocoaCB ccb: CocoaCB) { - self.init(contentRect: NSMakeRect(0, 0, 960, 480), - styleMask: [.titled, .closable, .miniaturizable, .resizable], - backing: .buffered, defer: false, screen: NSScreen.main()) - cocoaCB = ccb - title = "mpv" - } - - convenience init(contentRect: NSRect, styleMask style: NSWindowStyleMask, - screen: NSScreen?, cocoaCB ccb: CocoaCB) - { - self.init(contentRect: contentRect, styleMask: style, + convenience init(contentRect: NSRect, screen: NSScreen?, view: NSView, cocoaCB ccb: CocoaCB) { + self.init(contentRect: contentRect, + styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false, screen: screen) cocoaCB = ccb + title = cocoaCB.title minSize = NSMakeSize(160, 90) collectionBehavior = .fullScreenPrimary delegate = self + contentView!.addSubview(view) + view.frame = contentView!.frame unfsContentFrame = convertToScreen(contentView!.frame) targetScreen = screen! currentScreen = screen! unfScreen = screen! + initTitleBar() if let app = NSApp as? Application { app.menuBar.register(#selector(setHalfWindowSize), for: MPM_H_SIZE) @@ -123,14 +118,31 @@ class Window: NSWindow, NSWindowDelegate { titleBarEffect!.blendingMode = .withinWindow titleBarEffect!.autoresizingMask = [.viewWidthSizable, .viewMinYMargin] - setTitleBarStyle(mpv.getStringProperty("macos-title-bar-style") ?? "dark") + setTitleBarStyle(Int(mpv.macOpts!.macos_title_bar_style)) contentView!.addSubview(titleBarEffect!, positioned: .above, relativeTo: nil) - - border = mpv.getBoolProperty("border") } - func setTitleBarStyle(_ style: String) { - var effect = style + func setTitleBarStyle(_ style: Any) { + var effect: String + + if style is Int { + switch style as! Int { + case 4: + effect = "auto" + case 3: + effect = "mediumlight" + case 2: + effect = "light" + case 1: + effect = "ultradark" + case 0: fallthrough + default: + effect = "dark" + } + } else { + effect = style as! String + } + if effect == "auto" { let systemStyle = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") effect = systemStyle == nil ? "mediumlight" : "ultradark" @@ -339,16 +351,26 @@ class Window: NSWindow, NSWindowDelegate { } } - func setOnTop(_ state: Bool) { + func setOnTop(_ state: Bool, _ ontopLevel: Any) { if state { - let ontopLevel = mpv.getStringProperty("ontop-level") ?? "window" - switch ontopLevel { - case "window": - level = Int(CGWindowLevelForKey(.floatingWindow)) - case "system": - level = Int(CGWindowLevelForKey(.statusWindow))+1 - default: - level = Int(ontopLevel)! + if ontopLevel is Int { + switch ontopLevel as! Int { + case -1: + level = Int(CGWindowLevelForKey(.floatingWindow)) + case -2: + level = Int(CGWindowLevelForKey(.statusWindow))+1 + default: + level = ontopLevel as! Int + } + } else { + switch ontopLevel as! String { + case "window": + level = Int(CGWindowLevelForKey(.floatingWindow)) + case "system": + level = Int(CGWindowLevelForKey(.statusWindow))+1 + default: + level = Int(ontopLevel as! String)! + } } collectionBehavior.remove(.transient) collectionBehavior.insert(.managed) diff --git a/video/out/cocoa_cb_common.swift b/video/out/cocoa_cb_common.swift index 32e1d42b28..e6705902c4 100644 --- a/video/out/cocoa_cb_common.swift +++ b/video/out/cocoa_cb_common.swift @@ -30,6 +30,10 @@ class CocoaCB: NSObject { var cursorVisibilityWanted: Bool = true var isShuttingDown: Bool = false + var title: String = "mpv" { + didSet { if window != nil { window.title = title } } + } + enum State { case uninit case needsInit @@ -47,31 +51,29 @@ class CocoaCB: NSObject { let queue: DispatchQueue = DispatchQueue(label: "io.mpv.queue") - override init() { - super.init() - window = Window(cocoaCB: self) - - view = EventsView(frame: window.contentView!.bounds, cocoaCB: self) - window.contentView!.addSubview(view) - + convenience init(_ mpvHandle: OpaquePointer) { + self.init() + mpv = MPVHelper(mpvHandle) layer = VideoLayer(cocoaCB: self) - view.layer = layer - view.wantsLayer = true - view.layerContentsPlacement = .scaleProportionallyToFit - } - - func setMpvHandle(_ ctx: OpaquePointer) { - mpv = MPVHelper(ctx) - layer.setUpRender() } - func preinit() { + func preinit(_ vo: UnsafeMutablePointer) { if backendState == .uninit { backendState = .needsInit - DispatchQueue.main.async { - self.updateICCProfile() + + if let app = NSApp as? Application { + let ptr = mp_get_config_group(mpv.mpctx!, vo.pointee.global, + app.getMacOSConf()) + mpv.macOpts = UnsafeMutablePointer(OpaquePointer(ptr))!.pointee } - startDisplayLink() + + view = EventsView(cocoaCB: self) + view.layer = layer + view.wantsLayer = true + view.layerContentsPlacement = .scaleProportionallyToFit + startDisplayLink(vo) + initLightSensor() + addDisplayReconfigureObserver() } } @@ -82,39 +84,37 @@ class CocoaCB: NSObject { func reconfig(_ vo: UnsafeMutablePointer) { if backendState == .needsInit { - initBackend(vo) + DispatchQueue.main.sync { self.initBackend(vo) } } else { - layer.setVideo(true) - updateWindowSize(vo) - layer.update() + DispatchQueue.main.async { + self.layer.setVideo(true) + self.updateWindowSize(vo) + self.layer.update() + } } } func initBackend(_ vo: UnsafeMutablePointer) { let opts: mp_vo_opts = vo.pointee.opts.pointee NSApp.setActivationPolicy(.regular) + setAppIcon() - let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main() + let targetScreen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main() let wr = getWindowGeometry(forScreen: targetScreen!, videoOut: vo) - let win = Window(contentRect: wr, styleMask: window.styleMask, - screen: targetScreen, cocoaCB: self) - win.title = window.title - win.setOnTop(mpv.getBoolProperty("ontop")) - win.keepAspect = mpv.getBoolProperty("keepaspect-window") - window.close() - window = win - window.contentView!.addSubview(view) - view.frame = window.contentView!.frame - window.initTitleBar() + window = Window(contentRect: wr, screen: targetScreen, view: view, cocoaCB: self) + updateICCProfile() + window.setOnTop(Bool(opts.ontop), Int(opts.ontop_level)) + window.keepAspect = Bool(opts.keepaspect_window) + window.title = title + window.border = Bool(opts.border) - setAppIcon() window.isRestorable = false window.makeMain() window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) layer.setVideo(true) - if mpv.getBoolProperty("fullscreen") { + if Bool(opts.fullscreen) { DispatchQueue.main.async { self.window.toggleFullScreen(nil) } @@ -122,13 +122,12 @@ class CocoaCB: NSObject { window.isMovableByWindowBackground = true } - initLightSensor() - addDisplayReconfigureObserver() backendState = .init } func updateWindowSize(_ vo: UnsafeMutablePointer) { - let targetScreen = getTargetScreen(forFullscreen: false) ?? NSScreen.main() + let opts: mp_vo_opts = vo.pointee.opts.pointee + let targetScreen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main() let wr = getWindowGeometry(forScreen: targetScreen!, videoOut: vo) if !window.isVisible { window.makeKeyAndOrderFront(nil) @@ -155,8 +154,11 @@ class CocoaCB: NSObject { return kCVReturnSuccess } - func startDisplayLink() { - let displayId = UInt32(window.screen!.deviceDescription["NSScreenNumber"] as! Int) + func startDisplayLink(_ vo: UnsafeMutablePointer) { + let opts: mp_vo_opts = vo.pointee.opts.pointee + let screen = getScreenBy(id: Int(opts.screen_id)) ?? NSScreen.main() + let displayId = screen!.deviceDescription["NSScreenNumber"] as! UInt32 + CVDisplayLinkCreateWithActiveCGDisplays(&link) CVDisplayLinkSetCurrentCGDisplay(link!, displayId) if #available(macOS 10.12, *) { @@ -324,8 +326,8 @@ class CocoaCB: NSObject { } func getTargetScreen(forFullscreen fs: Bool) -> NSScreen? { - let screenID = fs ? mpv.getStringProperty("fs-screen") ?? "current": - mpv.getStringProperty("screen") ?? "current" + let screenType = fs ? "fs-screen" : "screen" + let screenID = mpv.getStringProperty(screenType) ?? "current" switch screenID { case "current", "default", "all": @@ -432,21 +434,17 @@ class CocoaCB: NSObject { let titleData = data!.assumingMemoryBound(to: Int8.self) let title = String(cString: titleData) DispatchQueue.main.async { - ccb.window.title = String(cString: titleData) + ccb.title = String(cString: titleData) } return VO_TRUE case VOCTRL_PREINIT: - ccb.preinit() + DispatchQueue.main.sync { ccb.preinit(vo!) } return VO_TRUE case VOCTRL_UNINIT: - DispatchQueue.main.async { - ccb.uninit() - } + DispatchQueue.main.async { ccb.uninit() } return VO_TRUE case VOCTRL_RECONFIG: - DispatchQueue.main.async { - ccb.reconfig(vo!) - } + ccb.reconfig(vo!) return VO_TRUE default: return VO_NOTIMPL @@ -472,7 +470,7 @@ class CocoaCB: NSObject { func processEvent(_ event: UnsafePointer) { switch event.pointee.event_id { case MPV_EVENT_SHUTDOWN: - if window.isAnimating { + if window != nil && window.isAnimating { isShuttingDown = true return } @@ -499,7 +497,7 @@ class CocoaCB: NSObject { } case "ontop": if let data = MPVHelper.mpvFlagToBool(property.data) { - window.setOnTop(data) + window.setOnTop(data, mpv.getStringProperty("ontop-level") ?? "window") } case "keepaspect-window": if let data = MPVHelper.mpvFlagToBool(property.data) { -- cgit v1.2.3