mac/app: rewrite App c main function, startup and termination in swift

also move main invocation into AppHub and completely delete the old
Application c implementation.
This commit is contained in:
der richter 2024-03-30 19:26:45 +01:00
parent b7c5b26d35
commit 7619cceb87
10 changed files with 96 additions and 202 deletions

View File

@ -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

View File

@ -17,6 +17,12 @@
#pragma once
#include <stdbool.h>
#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;

View File

@ -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];
}

View File

@ -15,6 +15,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#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;

View File

@ -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?

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
// multithreaded wrapper for mpv_main
int cocoa_main(int argc, char *argv[]);

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#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;
}
}

View File

@ -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<UnsafeMutablePointer<CChar>?>?
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<UnsafeMutablePointer<CChar>?>) -> 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
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#import <Cocoa/Cocoa.h>
#include "osdep/mac/application.h"

View File

@ -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