diff --git a/input/input.c b/input/input.c index 9d14ce3b84..9fd2a16f0c 100644 --- a/input/input.c +++ b/input/input.c @@ -50,7 +50,7 @@ #include "common/common.h" #if HAVE_COCOA -#include "osdep/mac/events.h" +#include "osdep/mac/app_bridge.h" #endif #define input_lock(ictx) mp_mutex_lock(&ictx->mutex) diff --git a/meson.build b/meson.build index 08cae3966d..c471d5d7b5 100644 --- a/meson.build +++ b/meson.build @@ -400,7 +400,7 @@ if features['cocoa'] 'osdep/path-mac.m', 'osdep/utils-mac.c', 'osdep/mac/application.m', - 'osdep/mac/events.m') + 'osdep/mac/app_bridge.m') main_fn_source = files('osdep/main-fn-mac.c') endif @@ -1517,9 +1517,10 @@ features += {'swift': swift.allowed()} swift_sources = [] if features['cocoa'] and features['swift'] - swift_sources += files('osdep/mac/libmpv_helper.swift', + swift_sources += files('osdep/mac/app_hub.swift', 'osdep/mac/event_helper.swift', 'osdep/mac/input_helper.swift', + 'osdep/mac/libmpv_helper.swift', 'osdep/mac/log_helper.swift', 'osdep/mac/menu_bar.swift', 'osdep/mac/option_helper.swift', diff --git a/osdep/mac/events.h b/osdep/mac/app_bridge.h similarity index 96% rename from osdep/mac/events.h rename to osdep/mac/app_bridge.h index 5c3ac38b6b..53388d8ba8 100644 --- a/osdep/mac/events.h +++ b/osdep/mac/app_bridge.h @@ -20,12 +20,10 @@ #ifndef MAC_EVENTS #define MAC_EVENTS -struct input_ctx; -struct mpv_handle; +#include "input/input.h" void cocoa_init_media_keys(void); 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); diff --git a/osdep/mac/events_objc.h b/osdep/mac/app_bridge.m similarity index 58% rename from osdep/mac/events_objc.h rename to osdep/mac/app_bridge.m index ece2254702..976cfed3d1 100644 --- a/osdep/mac/events_objc.h +++ b/osdep/mac/app_bridge.m @@ -17,19 +17,34 @@ * License along with mpv. If not, see . */ -#import -#include "osdep/mac/events.h" +#include "config.h" -@class RemoteCommandCenter; -@class InputHelper; -struct input_ctx; +#include "osdep/mac/app_bridge.h" +#if HAVE_SWIFT +#include "osdep/mac/swift.h" +#endif -@interface EventsResponder : NSObject +void cocoa_init_media_keys(void) +{ + [[AppHub shared] startRemote]; +} -+ (EventsResponder *)sharedInstance; -- (void)setIsApplication:(BOOL)isApplication; +void cocoa_uninit_media_keys(void) +{ + [[AppHub shared] stopRemote]; +} -@property(nonatomic, retain) RemoteCommandCenter *remoteCommandCenter; -@property(nonatomic, retain) InputHelper *inputHelper; +void cocoa_set_input_context(struct input_ctx *input_context) +{ + [[AppHub shared] initInput:input_context]; +} -@end +void cocoa_set_mpv_handle(struct mpv_handle *ctx) +{ + [[AppHub shared] initMpv:ctx]; +} + +void cocoa_init_cocoa_cb(void) +{ + [[AppHub shared] initCocoaCb]; +} diff --git a/osdep/mac/app_hub.swift b/osdep/mac/app_hub.swift new file mode 100644 index 0000000000..cdfd03f153 --- /dev/null +++ b/osdep/mac/app_hub.swift @@ -0,0 +1,109 @@ +/* + * 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 . + */ + +class AppHub: NSObject { + @objc static let shared = AppHub() + + var mpv: OpaquePointer? + @objc var input: InputHelper +#if HAVE_MACOS_MEDIA_PLAYER + var remote: RemoteCommandCenter? +#endif + + var isApplication: Bool { get { NSApp is Application } } + + private override init() { + input = InputHelper() + } + + @objc func initMpv(_ mpv: OpaquePointer) { + if isApplication { + self.mpv = mpv + mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE) + mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE) + mpv_observe_property(mpv, 0, "speed", MPV_FORMAT_DOUBLE) + mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_FLAG) + mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING) + mpv_observe_property(mpv, 0, "chapter-metadata/title", MPV_FORMAT_STRING) + mpv_observe_property(mpv, 0, "metadata/by-key/album", MPV_FORMAT_STRING) + mpv_observe_property(mpv, 0, "metadata/by-key/artist", MPV_FORMAT_STRING) + mpv_set_wakeup_callback(mpv, wakeup, TypeHelper.bridge(obj: self)) + return + } + + mpv_destroy(mpv) + } + + @objc func initInput(_ input: OpaquePointer?) { + self.input.signal(input: input) + } + + @objc func initCocoaCb() { + guard let app = NSApp as? Application else { return } + DispatchQueue.main.sync { app.initCocoaCb(mpv) } + } + + @objc func startRemote() { +#if HAVE_MACOS_MEDIA_PLAYER + if remote == nil { remote = RemoteCommandCenter() } + remote?.start() +#endif + } + + @objc func stopRemote() { +#if HAVE_MACOS_MEDIA_PLAYER + remote?.stop() +#endif + } + + let wakeup: EventHelper.wakeup_cb = { ( ctx ) in + let event = unsafeBitCast(ctx, to: AppHub.self) + DispatchQueue.main.async { event.eventLoop() } + } + + func eventLoop() { + while let mpv = mpv, let event = mpv_wait_event(mpv, 0) { + if event.pointee.event_id == MPV_EVENT_NONE { break } + handle(event: event) + } + } + + func handle(event: UnsafeMutablePointer) { + if let app = NSApp as? Application { + app.processEvent(event) + } + +#if HAVE_MACOS_MEDIA_PLAYER + if let remote = remote { + remote.processEvent(event) + } +#endif + + switch event.pointee.event_id { + case MPV_EVENT_SHUTDOWN: +#if HAVE_MACOS_COCOA_CB + if let app = NSApp as? Application, app.cocoaCB?.isShuttingDown ?? false { + mpv = nil; + return + } +#endif + mpv_destroy(mpv) + mpv = nil + default: break + } + } +} diff --git a/osdep/mac/application.m b/osdep/mac/application.m index 32066d8ae0..b56882b6b8 100644 --- a/osdep/mac/application.m +++ b/osdep/mac/application.m @@ -26,7 +26,6 @@ #include "options/options.h" #import "osdep/mac/application_objc.h" -#import "osdep/mac/events_objc.h" #include "osdep/threads.h" #include "osdep/main-fn.h" @@ -87,7 +86,7 @@ static mp_thread playback_thread_id; @interface Application () { - EventsResponder *_eventsResponder; + AppHub *_appHub; } @end @@ -112,15 +111,15 @@ static void terminate_cocoa_application(void) - (void)sendEvent:(NSEvent *)event { - if ([self modalWindow] || ![_eventsResponder.inputHelper processKeyWithEvent:event]) + if ([self modalWindow] || ![_appHub.input processKeyWithEvent:event]) [super sendEvent:event]; - [_eventsResponder.inputHelper wakeup]; + [_appHub.input wakeup]; } - (id)init { if (self = [super init]) { - _eventsResponder = [EventsResponder sharedInstance]; + _appHub = [AppHub shared]; NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager]; [em setEventHandler:self @@ -203,7 +202,7 @@ static const char mac_icon[] = - (void)handleQuitEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent { - if (![_eventsResponder.inputHelper command:@"quit"]) + if (![_appHub.input command:@"quit"]) terminate_cocoa_application(); } @@ -219,7 +218,7 @@ static const char mac_icon[] = range:NSMakeRange(0, [MPV_PROTOCOL length])]; url = [url stringByRemovingPercentEncoding]; - [_eventsResponder.inputHelper openWithFiles:@[url]]; + [_appHub.input openWithFiles:@[url]]; } - (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames @@ -231,7 +230,7 @@ static const char mac_icon[] = SEL cmpsel = @selector(localizedStandardCompare:); NSArray *files = [filenames sortedArrayUsingSelector:cmpsel]; - [_eventsResponder.inputHelper openWithFiles:files]; + [_appHub.input openWithFiles:files]; } @end @@ -315,7 +314,6 @@ int cocoa_main(int argc, char *argv[]) { @autoreleasepool { application_instantiated = true; - [[EventsResponder sharedInstance] setIsApplication:YES]; struct playback_thread_ctx ctx = {0}; ctx.argc = &argc; @@ -332,7 +330,7 @@ int cocoa_main(int argc, char *argv[]) } mp_thread_create(&playback_thread_id, playback_thread, &ctx); - [[EventsResponder sharedInstance].inputHelper wait]; + [[AppHub shared].input wait]; cocoa_run_runloop(); // This should never be reached: cocoa_run_runloop blocks until the diff --git a/osdep/mac/events.m b/osdep/mac/events.m deleted file mode 100644 index 127a674339..0000000000 --- a/osdep/mac/events.m +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Cocoa Application Event Handling - * - * 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 "mpv_talloc.h" -#include "input/event.h" -#include "input/input.h" -#include "player/client.h" - -#import "osdep/mac/events_objc.h" -#import "osdep/mac/application_objc.h" - -#include "config.h" - -#if HAVE_SWIFT -#include "osdep/mac/swift.h" -#endif - -@interface EventsResponder () -{ - struct mpv_handle *_ctx; - BOOL _is_application; -} - -- (BOOL)setMpvHandle:(struct mpv_handle *)ctx; -- (void)initCocoaCb; -- (void)readEvents; -- (void)startMediaKeys; -- (void)stopMediaKeys; -@end - -void cocoa_init_media_keys(void) -{ - [[EventsResponder sharedInstance] startMediaKeys]; -} - -void cocoa_uninit_media_keys(void) -{ - [[EventsResponder sharedInstance] stopMediaKeys]; -} - -void cocoa_set_input_context(struct input_ctx *input_context) -{ - [[EventsResponder sharedInstance].inputHelper signalWithInput:input_context]; -} - -static void wakeup(void *context) -{ - [[EventsResponder sharedInstance] readEvents]; -} - -void cocoa_set_mpv_handle(struct mpv_handle *ctx) -{ - if ([[EventsResponder sharedInstance] setMpvHandle:ctx]) { - mpv_observe_property(ctx, 0, "duration", MPV_FORMAT_DOUBLE); - mpv_observe_property(ctx, 0, "time-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(ctx, 0, "speed", MPV_FORMAT_DOUBLE); - mpv_observe_property(ctx, 0, "pause", MPV_FORMAT_FLAG); - mpv_observe_property(ctx, 0, "media-title", MPV_FORMAT_STRING); - mpv_observe_property(ctx, 0, "chapter-metadata/title", MPV_FORMAT_STRING); - mpv_observe_property(ctx, 0, "metadata/by-key/album", MPV_FORMAT_STRING); - mpv_observe_property(ctx, 0, "metadata/by-key/artist", MPV_FORMAT_STRING); - mpv_set_wakeup_callback(ctx, wakeup, NULL); - } -} - -void cocoa_init_cocoa_cb(void) -{ - [[EventsResponder sharedInstance] initCocoaCb]; -} - -@implementation EventsResponder - -@synthesize remoteCommandCenter = _remoteCommandCenter; -@synthesize inputHelper = _inputHelper; - -+ (EventsResponder *)sharedInstance -{ - static EventsResponder *responder = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - responder = [EventsResponder new]; - responder.inputHelper = [[InputHelper alloc] init: nil :nil]; - }); - return responder; -} - -- (void)setIsApplication:(BOOL)isApplication -{ - _is_application = isApplication; -} - -- (BOOL)setMpvHandle:(struct mpv_handle *)ctx -{ - if (_is_application) { - _ctx = ctx; - return YES; - } - - mpv_destroy(ctx); - return NO; -} - -- (void)initCocoaCb -{ - if (_is_application) { - dispatch_sync(dispatch_get_main_queue(), ^{ - [NSApp initCocoaCb:_ctx]; - }); - } -} - -- (void)readEvents -{ - dispatch_async(dispatch_get_main_queue(), ^{ - while (_ctx) { - mpv_event *event = mpv_wait_event(_ctx, 0); - if (event->event_id == MPV_EVENT_NONE) - break; - [self processEvent:event]; - } - }); -} - --(void)processEvent:(struct mpv_event *)event -{ - if(_is_application) { - [NSApp processEvent:event]; - } - - if (_remoteCommandCenter) { - [_remoteCommandCenter processEvent:event]; - } - - switch (event->event_id) { - case MPV_EVENT_SHUTDOWN: { -#if HAVE_MACOS_COCOA_CB - if ([(Application *)NSApp cocoaCB].isShuttingDown) { - _ctx = nil; - return; - } -#endif - mpv_destroy(_ctx); - _ctx = nil; - break; - } - default: - break; - } -} - -- (void)startMediaKeys -{ -#if HAVE_MACOS_MEDIA_PLAYER - if (_remoteCommandCenter == nil) { - _remoteCommandCenter = [[RemoteCommandCenter alloc] init]; - } -#endif - - [_remoteCommandCenter start]; -} - -- (void)stopMediaKeys -{ - [_remoteCommandCenter stop]; -} - -@end diff --git a/osdep/mac/menu_bar.swift b/osdep/mac/menu_bar.swift index 631007a074..f81e97daaf 100644 --- a/osdep/mac/menu_bar.swift +++ b/osdep/mac/menu_bar.swift @@ -318,7 +318,7 @@ class MenuBar: NSObject { @objc func quit(_ menuItem: MenuItem) { guard let menuConfig = menuItem.config else { return } - EventsResponder.sharedInstance().inputHelper.command(menuConfig.command) + AppHub.shared.input.command(menuConfig.command) } @objc func openFiles() { @@ -327,7 +327,7 @@ class MenuBar: NSObject { panel.canChooseDirectories = true if panel.runModal() == .OK { - EventsResponder.sharedInstance().inputHelper.open(files: panel.urls.map { $0.path }) + AppHub.shared.input.open(files: panel.urls.map { $0.path }) } } @@ -335,7 +335,7 @@ class MenuBar: NSObject { let panel = NSOpenPanel() if panel.runModal() == .OK, let url = panel.urls.first { - EventsResponder.sharedInstance().inputHelper.command("loadlist \"\(url.path)\"") + AppHub.shared.input.command("loadlist \"\(url.path)\"") } } @@ -355,13 +355,13 @@ class MenuBar: NSObject { } if alert.runModal() == .alertFirstButtonReturn && input.stringValue.count > 0 { - EventsResponder.sharedInstance().inputHelper.open(files: [input.stringValue]) + AppHub.shared.input.open(files: [input.stringValue]) } } @objc func command(_ menuItem: MenuItem) { guard let menuConfig = menuItem.config else { return } - EventsResponder.sharedInstance().inputHelper.command(menuConfig.command) + AppHub.shared.input.command(menuConfig.command) } @objc func url(_ menuItem: MenuItem) { diff --git a/osdep/mac/meson.build b/osdep/mac/meson.build index faebf7e1d6..de32eb2a3c 100644 --- a/osdep/mac/meson.build +++ b/osdep/mac/meson.build @@ -19,10 +19,18 @@ if get_option('optimization') != '0' swift_flags += '-O' endif +if macos_cocoa_cb.allowed() + swift_flags += ['-D', 'HAVE_MACOS_COCOA_CB'] +endif + if macos_touchbar.allowed() swift_flags += ['-D', 'HAVE_MACOS_TOUCHBAR'] endif +if macos_media_player.allowed() + swift_flags += ['-D', 'HAVE_MACOS_MEDIA_PLAYER'] +endif + extra_flags = get_option('swift-flags').split() swift_flags += extra_flags diff --git a/osdep/mac/remote_command_center.swift b/osdep/mac/remote_command_center.swift index bec63a1984..547a14c232 100644 --- a/osdep/mac/remote_command_center.swift +++ b/osdep/mac/remote_command_center.swift @@ -155,7 +155,7 @@ class RemoteCommandCenter: NSObject { self.configs[event.command]?.state = state } - EventsResponder.sharedInstance().inputHelper.put(key: config.key | Int32(state)) + AppHub.shared.input.put(key: config.key | Int32(state)) return .success } @@ -166,7 +166,7 @@ class RemoteCommandCenter: NSObject { } let cmd = String(format: "seek %.02f absolute", posEvent.positionTime) - return EventsResponder.sharedInstance().inputHelper.command(cmd) ? .success : .commandFailed + return AppHub.shared.input.command(cmd) ? .success : .commandFailed } @objc func processEvent(_ event: UnsafeMutablePointer) { diff --git a/osdep/mac/swift_bridge.h b/osdep/mac/swift_bridge.h index b47fbbaf05..f4d1c545d7 100644 --- a/osdep/mac/swift_bridge.h +++ b/osdep/mac/swift_bridge.h @@ -32,7 +32,6 @@ #include "video/out/win_state.h" #include "osdep/mac/application_objc.h" -#include "osdep/mac/events_objc.h" // complex macros won't get imported to Swift so we have to reassign them diff --git a/osdep/mac/touch_bar.swift b/osdep/mac/touch_bar.swift index 002c3dacb1..c21776c9e2 100644 --- a/osdep/mac/touch_bar.swift +++ b/osdep/mac/touch_bar.swift @@ -225,12 +225,12 @@ class TouchBar: NSTouchBar, NSTouchBarDelegate { @objc func buttonAction(_ button: NSButton) { guard let identifier = getIdentifierFrom(view: button), let command = configs[identifier]?.command else { return } - EventsResponder.sharedInstance().inputHelper.command(command) + AppHub.shared.input.command(command) } @objc func seekbarChanged(_ slider: NSSlider) { guard let identifier = getIdentifierFrom(view: slider), let command = configs[identifier]?.command else { return } - EventsResponder.sharedInstance().inputHelper.command(String(format: command, slider.doubleValue)) + AppHub.shared.input.command(String(format: command, slider.doubleValue)) } func format(time: Int) -> String { diff --git a/player/main.c b/player/main.c index 09cce990bb..48d29b520e 100644 --- a/player/main.c +++ b/player/main.c @@ -71,7 +71,7 @@ static const char def_config[] = ; #if HAVE_COCOA -#include "osdep/mac/events.h" +#include "osdep/mac/app_bridge.h" #endif #ifndef FULLCONFIG diff --git a/video/out/vo_libmpv.c b/video/out/vo_libmpv.c index fafe85acda..7974eed3f7 100644 --- a/video/out/vo_libmpv.c +++ b/video/out/vo_libmpv.c @@ -28,7 +28,7 @@ #include "libmpv.h" #if HAVE_MACOS_COCOA_CB -#include "osdep/mac/events.h" +#include "osdep/mac/app_bridge.h" #endif /*