diff --git a/meson.build b/meson.build index a5aedb3800..c59efc0864 100644 --- a/meson.build +++ b/meson.build @@ -393,7 +393,6 @@ if features['cocoa'] sources += files('osdep/language-mac.c', 'osdep/path-mac.m', 'osdep/utils-mac.c', - 'osdep/mac/application.m', 'osdep/mac/app_bridge.m') main_fn_source = files('osdep/main-fn-mac.c') endif diff --git a/osdep/mac/app_bridge.h b/osdep/mac/app_bridge.h index 27cc2413ea..fe1180a7ec 100644 --- a/osdep/mac/app_bridge.h +++ b/osdep/mac/app_bridge.h @@ -17,6 +17,12 @@ #pragma once +#include +#include "options/m_option.h" + +struct input_ctx; +struct mpv_handle; + enum { FRAME_VISIBLE = 0, FRAME_WHOLE, @@ -46,5 +52,7 @@ void cocoa_uninit_media_keys(void); void cocoa_set_input_context(struct input_ctx *input_context); void cocoa_set_mpv_handle(struct mpv_handle *ctx); void cocoa_init_cocoa_cb(void); +// multithreaded wrapper for mpv_main +int cocoa_main(int argc, char *argv[]); extern const struct m_sub_options macos_conf; diff --git a/osdep/mac/app_bridge.m b/osdep/mac/app_bridge.m index ab8470d9cc..8e9c4a444b 100644 --- a/osdep/mac/app_bridge.m +++ b/osdep/mac/app_bridge.m @@ -114,3 +114,9 @@ void cocoa_init_cocoa_cb(void) { [[AppHub shared] initCocoaCb]; } + +int cocoa_main(int argc, char *argv[]) +{ + return [(Application *)[Application sharedApplication] main:argc :argv]; +} + diff --git a/osdep/mac/app_bridge_objc.h b/osdep/mac/app_bridge_objc.h index c34b09736c..60201982cf 100644 --- a/osdep/mac/app_bridge_objc.h +++ b/osdep/mac/app_bridge_objc.h @@ -15,6 +15,7 @@ * License along with mpv. If not, see . */ +#import #import #include "player/client.h" @@ -28,8 +29,8 @@ #include "input/keycodes.h" #include "video/out/win_state.h" +#include "osdep/main-fn.h" #include "osdep/mac/app_bridge.h" -#include "osdep/mac/application_objc.h" // complex macros won't get imported to swift so we have to reassign them static int SWIFT_MBTN_LEFT = MP_MBTN_LEFT; diff --git a/osdep/mac/app_hub.swift b/osdep/mac/app_hub.swift index 6d4b00fa4c..0172d3904f 100644 --- a/osdep/mac/app_hub.swift +++ b/osdep/mac/app_hub.swift @@ -21,7 +21,7 @@ class AppHub: NSObject { @objc static let shared = AppHub() var mpv: OpaquePointer? - @objc var input: InputHelper + var input: InputHelper var option: OptionHelper? var event: EventHelper? var menu: MenuBar? @@ -29,7 +29,7 @@ class AppHub: NSObject { var remote: RemoteCommandCenter? #endif #if HAVE_MACOS_TOUCHBAR - @objc var touchBar: TouchBar? + var touchBar: TouchBar? #endif #if HAVE_MACOS_COCOA_CB var cocoaCb: CocoaCB? diff --git a/osdep/mac/application.h b/osdep/mac/application.h deleted file mode 100644 index 3a87cd42a7..0000000000 --- a/osdep/mac/application.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 . - */ - -#pragma once - -// multithreaded wrapper for mpv_main -int cocoa_main(int argc, char *argv[]); diff --git a/osdep/mac/application.m b/osdep/mac/application.m deleted file mode 100644 index fb2889e92a..0000000000 --- a/osdep/mac/application.m +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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 -#include "config.h" -#include "mpv_talloc.h" - -#include "common/msg.h" -#include "input/input.h" -#include "player/client.h" -#include "options/m_config.h" -#include "options/options.h" - -#import "osdep/mac/application_objc.h" -#include "osdep/threads.h" -#include "osdep/main-fn.h" - -#if HAVE_SWIFT -#include "osdep/mac/swift.h" -#endif - -static mp_thread playback_thread_id; - -static Application *mpv_shared_app(void) -{ - return (Application *)[Application sharedApplication]; -} - -static void terminate_cocoa_application(void) -{ - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp hide:NSApp]; - [NSApp terminate:NSApp]; - }); -} - -struct playback_thread_ctx { - int *argc; - char ***argv; -}; - -static void cocoa_run_runloop(void) -{ - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - [NSApp run]; - [pool drain]; -} - -static MP_THREAD_VOID playback_thread(void *ctx_obj) -{ - mp_thread_set_name("core/playback"); - @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); - } -} - -static void init_cocoa_application(bool regular) -{ - NSApp = mpv_shared_app(); - [NSApp setDelegate:NSApp]; - - // 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 ? - NSApplicationActivationPolicyRegular : - NSApplicationActivationPolicyAccessory]; - - atexit_b(^{ - // Because activation policy has just been set to behave like a real - // application, that policy must be reset on exit to prevent, among - // other things, the menubar created here from remaining on screen. - dispatch_async(dispatch_get_main_queue(), ^{ - [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; - }); - }); -} - -static bool bundle_started_from_finder() -{ - NSString* bundle = [[[NSProcessInfo processInfo] environment] objectForKey:@"MPVBUNDLE"]; - return [bundle isEqual:@"true"]; -} - -static bool is_psn_argument(char *arg_to_check) -{ - NSString *arg = [NSString stringWithUTF8String:arg_to_check]; - return [arg hasPrefix:@"-psn_"]; -} - -static void setup_bundle(int *argc, char *argv[]) -{ - if (*argc > 1 && is_psn_argument(argv[1])) { - *argc = 1; - argv[1] = NULL; - } - - NSDictionary *env = [[NSProcessInfo processInfo] environment]; - NSString *path_bundle = [env objectForKey:@"PATH"]; - NSString *path_new = [NSString stringWithFormat:@"%@:%@:%@:%@:%@", - path_bundle, - @"/usr/local/bin", - @"/usr/local/sbin", - @"/opt/local/bin", - @"/opt/local/sbin"]; - setenv("PATH", [path_new UTF8String], 1); -} - -int cocoa_main(int argc, char *argv[]) -{ - @autoreleasepool { - struct playback_thread_ctx ctx = {0}; - ctx.argc = &argc; - ctx.argv = &argv; - - if (bundle_started_from_finder()) { - setup_bundle(&argc, argv); - init_cocoa_application(true); - } else { - for (int i = 1; i < argc; i++) - if (argv[i][0] != '-') - mpv_shared_app().openCount++; - init_cocoa_application(false); - } - - mp_thread_create(&playback_thread_id, playback_thread, &ctx); - [[AppHub shared].input wait]; - cocoa_run_runloop(); - - // This should never be reached: cocoa_run_runloop blocks until the - // process is quit - fprintf(stderr, "There was either a problem " - "initializing Cocoa or the Runloop was stopped unexpectedly. " - "Please report this issues to a developer.\n"); - mp_thread_join(playback_thread_id); - return 1; - } -} diff --git a/osdep/mac/application.swift b/osdep/mac/application.swift index 6b317be67c..6a1af89cec 100644 --- a/osdep/mac/application.swift +++ b/osdep/mac/application.swift @@ -18,12 +18,15 @@ import Cocoa class Application: NSApplication, NSApplicationDelegate { - let appHub: AppHub + var appHub: AppHub { get { return AppHub.shared } } let MPV_PROTOCOL: String = "mpv://" @objc var openCount: Int = 0 + var playbackThreadId: mp_thread! + var argc: Int32? + var argv: UnsafeMutablePointer?>? + override init() { - appHub = AppHub.shared super.init() let eventManager = NSAppleEventManager.shared() @@ -45,6 +48,22 @@ class Application: NSApplication, NSApplicationDelegate { eventManager.removeEventHandler(forEventClass: AEEventClass(kCoreEventClass), andEventID: kAEQuitApplication) } + func initApplication(_ regular: Bool) { + NSApp = self + NSApp.delegate = self + + // 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 ? .regular : .accessory) + + atexit_b({ + // Because activation policy has just been set to behave like a real + // application, that policy must be reset on exit to prevent, among + // other things, the menubar created here from remaining on screen. + DispatchQueue.main.async { NSApp.setActivationPolicy(.prohibited) } + }) + } + func terminateApplication() { DispatchQueue.main.async { NSApp.hide(NSApp) @@ -75,6 +94,7 @@ class Application: NSApplication, NSApplicationDelegate { ) } + // quit from App icon @objc func handleQuit(event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) { if !appHub.input.command("quit") { terminateApplication() @@ -103,4 +123,59 @@ class Application: NSApplication, NSApplicationDelegate { } appHub.input.open(files: files) } + + func bundleStartedFromFinder() -> Bool { + return ProcessInfo.processInfo.environment["MPVBUNDLE"] == "true" + } + + func setupBundle() { + // started from finder the first argument after the binary may start with -psn_ + // remove it and all following + if CommandLine.argc > 1 && CommandLine.arguments[1].hasPrefix("-psn_") { + argc? = 1 + argv?[1] = nil + } + + let path = (ProcessInfo.processInfo.environment["PATH"] ?? "") + + ":/usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin" + _ = path.withCString { setenv("PATH", $0, 1) } + } + + let playbackThread: @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? = { (ptr: UnsafeMutableRawPointer) in + let application: Application = TypeHelper.bridge(ptr: ptr) + mp_thread_set_name("core/playback") + let r: Int32 = mpv_main(application.argc ?? 1, application.argv) + application.terminateApplication() + // normally never reached - unless the cocoa mainloop hasn't started yet + exit(r) + } + + @objc func main(_ argc: Int32, _ argv: UnsafeMutablePointer?>) -> Int { + self.argc = argc + self.argv = argv + + if bundleStartedFromFinder() { + setupBundle() + initApplication(true) + } else { + for argument in CommandLine.arguments.dropFirst() { + if !argument.hasPrefix("-") { + openCount += 1 + } + } + initApplication(false) + } + + pthread_create(&playbackThreadId, nil, playbackThread, TypeHelper.bridge(obj: self)) + appHub.input.wait() + NSApp.run() + + // This should never be reached: NSApp.run() blocks until the process is quit + print(""" + There was either a problem initializing Cocoa or the Runloop was stopped unexpectedly. \ + Please report this issues to a developer.\n + """) + pthread_join(playbackThreadId, nil) + return 1 + } } diff --git a/osdep/mac/application_objc.h b/osdep/mac/application_objc.h deleted file mode 100644 index 255ede8bc2..0000000000 --- a/osdep/mac/application_objc.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 . - */ - -#import -#include "osdep/mac/application.h" diff --git a/osdep/main-fn-mac.c b/osdep/main-fn-mac.c index 356f767911..ee2d6e3464 100644 --- a/osdep/main-fn-mac.c +++ b/osdep/main-fn-mac.c @@ -1,4 +1,4 @@ -#include "osdep/mac/application.h" +#include "osdep/mac/app_bridge.h" // This is needed because Cocoa absolutely requires creating the NSApplication // singleton and running it in the "main" thread. It is apparently not