summaryrefslogtreecommitdiffstats
path: root/osdep/mac
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--misc/io_utils.h (renamed from osdep/macosx_menubar_objc.h)12
-rw-r--r--osdep/mac/app_bridge.h (renamed from osdep/macosx_application.h)22
-rw-r--r--osdep/mac/app_bridge.m123
-rw-r--r--osdep/mac/app_bridge_objc.h (renamed from osdep/macOS_swift_bridge.h)25
-rw-r--r--osdep/mac/app_hub.swift130
-rw-r--r--osdep/mac/application.swift123
-rw-r--r--osdep/mac/event_helper.swift147
-rw-r--r--osdep/mac/input_helper.swift275
-rw-r--r--osdep/mac/libmpv_helper.swift (renamed from osdep/macos/libmpv_helper.swift)119
-rw-r--r--osdep/mac/log_helper.swift67
-rw-r--r--osdep/mac/menu_bar.swift397
-rw-r--r--osdep/mac/meson.build (renamed from osdep/meson.build)24
-rw-r--r--osdep/mac/option_helper.swift74
-rw-r--r--osdep/mac/precise_timer.swift (renamed from osdep/macos/precise_timer.swift)9
-rw-r--r--osdep/mac/presentation.swift56
-rw-r--r--osdep/mac/remote_command_center.swift202
-rw-r--r--osdep/mac/swift_compat.swift (renamed from osdep/macos/swift_compat.swift)0
-rw-r--r--osdep/mac/swift_extensions.swift93
-rw-r--r--osdep/mac/touch_bar.swift297
-rw-r--r--osdep/mac/type_helper.swift69
-rw-r--r--osdep/macos/log_helper.swift47
-rw-r--r--osdep/macos/mpv_helper.swift156
-rw-r--r--osdep/macos/remote_command_center.swift191
-rw-r--r--osdep/macos/swift_extensions.swift58
-rw-r--r--osdep/macosx_application.m375
-rw-r--r--osdep/macosx_application_objc.h40
-rw-r--r--osdep/macosx_events.h36
-rw-r--r--osdep/macosx_events.m408
-rw-r--r--osdep/macosx_events_objc.h45
-rw-r--r--osdep/macosx_menubar.m853
-rw-r--r--osdep/macosx_touchbar.h46
-rw-r--r--osdep/macosx_touchbar.m334
-rw-r--r--video/out/win32/menu.h (renamed from osdep/macosx_menubar.h)24
33 files changed, 2144 insertions, 2733 deletions
diff --git a/osdep/macosx_menubar_objc.h b/misc/io_utils.h
index 072fef8..012f242 100644
--- a/osdep/macosx_menubar_objc.h
+++ b/misc/io_utils.h
@@ -1,4 +1,6 @@
/*
+ * I/O utility functions
+ *
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
@@ -15,11 +17,9 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#import <Cocoa/Cocoa.h>
-#include "osdep/macosx_menubar.h"
-
-@interface MenuBar : NSObject
+#pragma once
-- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key;
+#include <stddef.h>
-@end
+int mp_mkostemps(char *template, int suffixlen, int flags);
+bool mp_save_to_file(const char *filepath, const void *data, size_t size);
diff --git a/osdep/macosx_application.h b/osdep/mac/app_bridge.h
index 753b9f0..db03c8e 100644
--- a/osdep/macosx_application.h
+++ b/osdep/mac/app_bridge.h
@@ -15,25 +15,27 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MPV_MACOSX_APPLICATION
-#define MPV_MACOSX_APPLICATION
+#pragma once
-#include "osdep/macosx_menubar.h"
+#include <stdbool.h>
#include "options/m_option.h"
+struct input_ctx;
+struct mpv_handle;
+
enum {
FRAME_VISIBLE = 0,
FRAME_WHOLE,
};
enum {
- RENDER_TIMER_CALLBACK = 0,
- RENDER_TIMER_PRECISE,
+ RENDER_TIMER_PRESENTATION_FEEDBACK = -1,
RENDER_TIMER_SYSTEM,
+ RENDER_TIMER_CALLBACK,
+ RENDER_TIMER_PRECISE,
};
struct macos_opts {
- int macos_title_bar_style;
int macos_title_bar_appearance;
int macos_title_bar_material;
struct m_color macos_title_bar_color;
@@ -46,10 +48,12 @@ struct macos_opts {
bool cocoa_cb_10bit_context;
};
+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);
// multithreaded wrapper for mpv_main
int cocoa_main(int argc, char *argv[]);
-void cocoa_register_menu_item_action(MPMenuKey key, void* action);
extern const struct m_sub_options macos_conf;
-
-#endif /* MPV_MACOSX_APPLICATION */
diff --git a/osdep/mac/app_bridge.m b/osdep/mac/app_bridge.m
new file mode 100644
index 0000000..bf39efe
--- /dev/null
+++ b/osdep/mac/app_bridge.m
@@ -0,0 +1,123 @@
+/*
+ * 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 "config.h"
+
+#import "osdep/mac/app_bridge_objc.h"
+
+#if HAVE_SWIFT
+#include "osdep/mac/swift.h"
+#endif
+
+#define OPT_BASE_STRUCT struct macos_opts
+const struct m_sub_options macos_conf = {
+ .opts = (const struct m_option[]) {
+ {"macos-title-bar-appearance", OPT_CHOICE(macos_title_bar_appearance,
+ {"auto", 0}, {"aqua", 1}, {"darkAqua", 2},
+ {"vibrantLight", 3}, {"vibrantDark", 4},
+ {"aquaHighContrast", 5}, {"darkAquaHighContrast", 6},
+ {"vibrantLightHighContrast", 7},
+ {"vibrantDarkHighContrast", 8})},
+ {"macos-title-bar-material", OPT_CHOICE(macos_title_bar_material,
+ {"titlebar", 0}, {"selection", 1}, {"menu", 2},
+ {"popover", 3}, {"sidebar", 4}, {"headerView", 5},
+ {"sheet", 6}, {"windowBackground", 7}, {"hudWindow", 8},
+ {"fullScreen", 9}, {"toolTip", 10}, {"contentBackground", 11},
+ {"underWindowBackground", 12}, {"underPageBackground", 13},
+ {"dark", 14}, {"light", 15}, {"mediumLight", 16},
+ {"ultraDark", 17})},
+ {"macos-title-bar-color", OPT_COLOR(macos_title_bar_color)},
+ {"macos-fs-animation-duration",
+ OPT_CHOICE(macos_fs_animation_duration, {"default", -1}),
+ M_RANGE(0, 1000)},
+ {"macos-force-dedicated-gpu", OPT_BOOL(macos_force_dedicated_gpu)},
+ {"macos-app-activation-policy", OPT_CHOICE(macos_app_activation_policy,
+ {"regular", 0}, {"accessory", 1}, {"prohibited", 2})},
+ {"macos-geometry-calculation", OPT_CHOICE(macos_geometry_calculation,
+ {"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
+ {"macos-render-timer", OPT_CHOICE(macos_render_timer,
+ {"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
+ {"system", RENDER_TIMER_SYSTEM}, {"feedback", RENDER_TIMER_PRESENTATION_FEEDBACK})},
+ {"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
+ {"auto", -1}, {"no", 0}, {"yes", 1})},
+ {"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},
+ {0}
+ },
+ .size = sizeof(struct macos_opts),
+ .defaults = &(const struct macos_opts){
+ .macos_title_bar_color = {0, 0, 0, 0},
+ .macos_fs_animation_duration = -1,
+ .macos_render_timer = RENDER_TIMER_CALLBACK,
+ .cocoa_cb_sw_renderer = -1,
+ .cocoa_cb_10bit_context = true
+ },
+};
+
+static const char app_icon[] =
+#include "TOOLS/osxbundle/icon.icns.inc"
+;
+
+NSData *app_bridge_icon(void)
+{
+ return [NSData dataWithBytesNoCopy:(void *)app_icon length:sizeof(app_icon) - 1 freeWhenDone:NO];
+}
+
+void app_bridge_tarray_append(void *t, char ***a, int *i, char *s)
+{
+ MP_TARRAY_APPEND(t, *a, *i, s);
+}
+
+const struct m_sub_options *app_bridge_mac_conf(void)
+{
+ return &macos_conf;
+}
+
+const struct m_sub_options *app_bridge_vo_conf(void)
+{
+ return &vo_sub_opts;
+}
+
+void cocoa_init_media_keys(void)
+{
+ [[AppHub shared] startRemote];
+}
+
+void cocoa_uninit_media_keys(void)
+{
+ [[AppHub shared] stopRemote];
+}
+
+void cocoa_set_input_context(struct input_ctx *input_context)
+{
+ [[AppHub shared] initInput:input_context];
+}
+
+void cocoa_set_mpv_handle(struct mpv_handle *ctx)
+{
+ [[AppHub shared] initMpv:ctx];
+}
+
+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/macOS_swift_bridge.h b/osdep/mac/app_bridge_objc.h
index 9407b6f..6020198 100644
--- a/osdep/macOS_swift_bridge.h
+++ b/osdep/mac/app_bridge_objc.h
@@ -15,9 +15,7 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-// including frameworks here again doesn't make sense, but otherwise the swift
-// compiler doesn't include the needed headers in our generated header file
-#import <IOKit/pwr_mgt/IOPMLib.h>
+#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#include "player/client.h"
@@ -27,13 +25,14 @@
#include "options/m_config.h"
#include "player/core.h"
#include "input/input.h"
+#include "input/event.h"
+#include "input/keycodes.h"
#include "video/out/win_state.h"
-#include "osdep/macosx_application_objc.h"
-#include "osdep/macosx_events_objc.h"
+#include "osdep/main-fn.h"
+#include "osdep/mac/app_bridge.h"
-
-// complex macros won't get imported to Swift so we have to reassign them
+// complex macros won't get imported to swift so we have to reassign them
static int SWIFT_MBTN_LEFT = MP_MBTN_LEFT;
static int SWIFT_MBTN_MID = MP_MBTN_MID;
static int SWIFT_MBTN_RIGHT = MP_MBTN_RIGHT;
@@ -48,10 +47,10 @@ static int SWIFT_MBTN9 = MP_MBTN9;
static int SWIFT_KEY_MOUSE_LEAVE = MP_KEY_MOUSE_LEAVE;
static int SWIFT_KEY_MOUSE_ENTER = MP_KEY_MOUSE_ENTER;
-// only used from Swift files and therefore seen as unused by the c compiler
-static void SWIFT_TARRAY_STRING_APPEND(void *t, char ***a, int *i, char *s) __attribute__ ((unused));
+static const char *swift_mpv_version = mpv_version;
+static const char *swift_mpv_copyright = mpv_copyright;
-static void SWIFT_TARRAY_STRING_APPEND(void *t, char ***a, int *i, char *s)
-{
- MP_TARRAY_APPEND(t, *a, *i, s);
-}
+NSData *app_bridge_icon(void);
+void app_bridge_tarray_append(void *t, char ***a, int *i, char *s);
+const struct m_sub_options *app_bridge_mac_conf(void);
+const struct m_sub_options *app_bridge_vo_conf(void);
diff --git a/osdep/mac/app_hub.swift b/osdep/mac/app_hub.swift
new file mode 100644
index 0000000..997cc33
--- /dev/null
+++ b/osdep/mac/app_hub.swift
@@ -0,0 +1,130 @@
+/*
+ * 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
+
+class AppHub: NSObject {
+ @objc static let shared = AppHub()
+
+ var mpv: OpaquePointer?
+ var input: InputHelper
+ var log: LogHelper
+ var option: OptionHelper?
+ var event: EventHelper?
+ var menu: MenuBar?
+#if HAVE_MACOS_MEDIA_PLAYER
+ var remote: RemoteCommandCenter?
+#endif
+#if HAVE_MACOS_TOUCHBAR
+ var touchBar: TouchBar?
+#endif
+#if HAVE_MACOS_COCOA_CB
+ var cocoaCb: CocoaCB?
+#endif
+
+ let MPV_PROTOCOL: String = "mpv://"
+ var isApplication: Bool { get { NSApp is Application } }
+ var openEvents: Int = 0
+
+ private override init() {
+ input = InputHelper()
+ log = LogHelper()
+ super.init()
+ if isApplication { menu = MenuBar(self) }
+#if HAVE_MACOS_MEDIA_PLAYER
+ remote = RemoteCommandCenter(self)
+#endif
+ log.verbose("AppHub initialised")
+ }
+
+ @objc func initMpv(_ mpv: OpaquePointer) {
+ event = EventHelper(self, mpv)
+ if let mpv = event?.mpv {
+ self.mpv = mpv
+ log.log = mp_log_new(nil, mp_client_get_log(mpv), "app")
+ option = OptionHelper(UnsafeMutablePointer(mpv), mp_client_get_global(mpv))
+ input.option = option
+ }
+
+#if HAVE_MACOS_MEDIA_PLAYER
+ remote?.registerEvents()
+#endif
+#if HAVE_MACOS_TOUCHBAR
+ touchBar = TouchBar(self)
+#endif
+ log.verbose("AppHub functionality initialised")
+ }
+
+ @objc func initInput(_ input: OpaquePointer?) {
+ log.verbose("Initialising Input")
+ self.input.signal(input: input)
+ }
+
+ @objc func initCocoaCb() {
+#if HAVE_MACOS_COCOA_CB
+ if !isApplication { return }
+ log.verbose("Initialising CocoaCB")
+ DispatchQueue.main.sync {
+ self.cocoaCb = self.cocoaCb ?? CocoaCB(mpv_create_client(mpv, "cocoacb"))
+ }
+#endif
+ }
+
+ @objc func startRemote() {
+#if HAVE_MACOS_MEDIA_PLAYER
+ log.verbose("Starting RemoteCommandCenter")
+ remote?.start()
+#endif
+ }
+
+ @objc func stopRemote() {
+#if HAVE_MACOS_MEDIA_PLAYER
+ log.verbose("Stoping RemoteCommandCenter")
+ remote?.stop()
+#endif
+ }
+
+ func open(urls: [URL]) {
+ let files = urls.map {
+ if $0.isFileURL { return $0.path }
+ var path = $0.absoluteString
+ if path.hasPrefix(MPV_PROTOCOL) { path.removeFirst(MPV_PROTOCOL.count) }
+ return path.removingPercentEncoding ?? path
+ }.sorted { (strL: String, strR: String) -> Bool in
+ return strL.localizedStandardCompare(strR) == .orderedAscending
+ }
+ log.verbose("\(openEvents > 0 ? "Appending" : "Opening") dropped files: \(files)")
+ input.open(files: files, append: openEvents > 0)
+ openEvents += 1
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.openEvents -= 1 }
+ }
+
+ func getIcon() -> NSImage {
+ guard let iconData = app_bridge_icon(), let icon = NSImage(data: iconData) else {
+ return NSImage(size: NSSize(width: 1, height: 1))
+ }
+ return icon
+ }
+
+ func getMacConf() -> UnsafePointer<m_sub_options>? {
+ return app_bridge_mac_conf()
+ }
+
+ func getVoConf() -> UnsafePointer<m_sub_options>? {
+ return app_bridge_vo_conf()
+ }
+}
diff --git a/osdep/mac/application.swift b/osdep/mac/application.swift
new file mode 100644
index 0000000..30f37a6
--- /dev/null
+++ b/osdep/mac/application.swift
@@ -0,0 +1,123 @@
+/*
+ * 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
+
+class Application: NSApplication, NSApplicationDelegate {
+ var appHub: AppHub { get { return AppHub.shared } }
+ var eventManager: NSAppleEventManager { get { return NSAppleEventManager.shared() } }
+ var isBundle: Bool { get { return ProcessInfo.processInfo.environment["MPVBUNDLE"] == "true" } }
+ var playbackThreadId: mp_thread!
+ var argc: Int32?
+ var argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
+
+ override init() {
+ super.init()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func sendEvent(_ event: NSEvent) {
+ if modalWindow != nil || !appHub.input.processKey(event: event) {
+ super.sendEvent(event)
+ }
+ appHub.input.wakeup()
+ }
+
+#if HAVE_MACOS_TOUCHBAR
+ override func makeTouchBar() -> NSTouchBar? {
+ return appHub.touchBar
+ }
+#endif
+
+ func application(_ application: NSApplication, open urls: [URL]) {
+ appHub.open(urls: urls)
+ }
+
+ func applicationWillFinishLaunching(_ notification: Notification) {
+ // register quit and exit events
+ eventManager.setEventHandler(
+ self,
+ andSelector: #selector(handleQuit(event:replyEvent:)),
+ forEventClass: AEEventClass(kCoreEventClass),
+ andEventID: kAEQuitApplication
+ )
+ atexit_b({
+ // clean up after exit() was called
+ DispatchQueue.main.async {
+ NSApp.hide(NSApp)
+ NSApp.setActivationPolicy(.prohibited)
+ self.eventManager.removeEventHandler(forEventClass: AEEventClass(kCoreEventClass), andEventID: kAEQuitApplication)
+ }
+ })
+ }
+
+ // quit from App icon, external quit from NSWorkspace
+ @objc func handleQuit(event: NSAppleEventDescriptor?, replyEvent: NSAppleEventDescriptor?) {
+ // send quit to core, terminates mpv_main called in playbackThread,
+ if !appHub.input.command("quit") {
+ appHub.log.warning("Could not properly shut down mpv")
+ exit(1)
+ }
+ }
+
+ func setupBundle() {
+ if !isBundle { return }
+
+ // started from finder the first argument after the binary may start with -psn_
+ 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"
+ appHub.log.verbose("Setting Bundle $PATH to: \(path)")
+ _ = 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 exitCode: Int32 = mpv_main(application.argc ?? 1, application.argv)
+ // exit of any proper shut down
+ exit(exitCode)
+ }
+
+ @objc func main(_ argc: Int32, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>) -> Int {
+ self.argc = argc
+ self.argv = argv
+
+ NSApp = self
+ NSApp.delegate = self
+ NSApp.setActivationPolicy(isBundle ? .regular : .accessory)
+ setupBundle()
+ pthread_create(&playbackThreadId, nil, playbackThread, TypeHelper.bridge(obj: self))
+ appHub.input.wait()
+ NSApp.run()
+
+ // should never be reached
+ 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/event_helper.swift b/osdep/mac/event_helper.swift
new file mode 100644
index 0000000..277a9aa
--- /dev/null
+++ b/osdep/mac/event_helper.swift
@@ -0,0 +1,147 @@
+/*
+ * 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
+
+protocol EventSubscriber: AnyObject {
+ var uid: Int { get }
+ func handle(event: EventHelper.Event)
+}
+
+extension EventSubscriber {
+ var uid: Int { get { return Int(bitPattern: ObjectIdentifier(self)) }}
+}
+
+extension EventHelper {
+ typealias wakeup_cb = (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
+
+ struct Event {
+ var id: String {
+ get { name + (name.starts(with: "MPV_EVENT_") ? "" : String(format.rawValue)) }
+ }
+ var idReset: String {
+ get { name + (name.starts(with: "MPV_EVENT_") ? "" : String(MPV_FORMAT_NONE.rawValue)) }
+ }
+ let name: String
+ let format: mpv_format
+ let string: String?
+ let bool: Bool?
+ let double: Double?
+
+ init(
+ name: String = "",
+ format: mpv_format = MPV_FORMAT_NONE,
+ string: String? = nil,
+ bool: Bool? = nil,
+ double: Double? = nil
+
+ ) {
+ self.name = name
+ self.format = format
+ self.string = string
+ self.bool = bool
+ self.double = double
+ }
+ }
+}
+
+class EventHelper {
+ unowned let appHub: AppHub
+ var mpv: OpaquePointer?
+ var events: [String:[Int:EventSubscriber]] = [:]
+
+ init?(_ appHub: AppHub, _ mpv: OpaquePointer) {
+ if !appHub.isApplication {
+ mpv_destroy(mpv)
+ return nil
+ }
+
+ self.appHub = appHub
+ self.mpv = mpv
+ mpv_set_wakeup_callback(mpv, wakeup, TypeHelper.bridge(obj: self))
+ }
+
+ func subscribe(_ subscriber: any EventSubscriber, event: Event) {
+ guard let mpv = mpv else { return }
+
+ if !event.name.isEmpty {
+ if !events.keys.contains(event.idReset) {
+ events[event.idReset] = [:]
+ }
+ if !events.keys.contains(event.id) {
+ mpv_observe_property(mpv, 0, event.name, event.format)
+ events[event.id] = [:]
+ }
+ events[event.idReset]?[subscriber.uid] = subscriber
+ events[event.id]?[subscriber.uid] = subscriber
+ }
+ }
+
+ let wakeup: wakeup_cb = { ( ctx ) in
+ let event = unsafeBitCast(ctx, to: EventHelper.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<mpv_event>) {
+ switch event.pointee.event_id {
+ case MPV_EVENT_PROPERTY_CHANGE:
+ handle(property: event)
+ default:
+ for (_, subscriber) in events[String(describing: event.pointee.event_id)] ?? [:] {
+ subscriber.handle(event: .init(name: String(describing: event.pointee.event_id)))
+ }
+ }
+
+ if event.pointee.event_id == MPV_EVENT_SHUTDOWN {
+ mpv_destroy(mpv)
+ mpv = nil
+ }
+ }
+
+ func handle(property mpvEvent: UnsafeMutablePointer<mpv_event>) {
+ let pData = OpaquePointer(mpvEvent.pointee.data)
+ guard let property = UnsafePointer<mpv_event_property>(pData)?.pointee else {
+ return
+ }
+
+ let name = String(cString: property.name)
+ let format = property.format
+ for (_, subscriber) in events[name + String(format.rawValue)] ?? [:] {
+ var event: Event? = nil
+ switch format {
+ case MPV_FORMAT_STRING:
+ event = .init(name: name, format: format, string: TypeHelper.toString(property.data))
+ case MPV_FORMAT_FLAG:
+ event = .init(name: name, format: format, bool: TypeHelper.toBool(property.data))
+ case MPV_FORMAT_DOUBLE:
+ event = .init(name: name, format: format, double: TypeHelper.toDouble(property.data))
+ case MPV_FORMAT_NONE:
+ event = .init(name: name, format: format)
+ default: break
+ }
+
+ if let e = event { subscriber.handle(event: e) }
+ }
+ }
+}
diff --git a/osdep/mac/input_helper.swift b/osdep/mac/input_helper.swift
new file mode 100644
index 0000000..0e4ec1f
--- /dev/null
+++ b/osdep/mac/input_helper.swift
@@ -0,0 +1,275 @@
+/*
+ * 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
+import Carbon.HIToolbox
+
+class InputHelper: NSObject {
+ var option: OptionHelper?
+ var lock = NSCondition()
+ private var input: OpaquePointer?
+
+ let keymap: [mp_keymap] = [
+ // special keys
+ .init(kVK_Return, MP_KEY_ENTER), .init(kVK_Escape, MP_KEY_ESC),
+ .init(kVK_Delete, MP_KEY_BACKSPACE), .init(kVK_Tab, MP_KEY_TAB),
+ .init(kVK_VolumeUp, MP_KEY_VOLUME_UP), .init(kVK_VolumeDown, MP_KEY_VOLUME_DOWN),
+ .init(kVK_Mute, MP_KEY_MUTE),
+
+ // cursor keys
+ .init(kVK_UpArrow, MP_KEY_UP), .init(kVK_DownArrow, MP_KEY_DOWN),
+ .init(kVK_LeftArrow, MP_KEY_LEFT), .init(kVK_RightArrow, MP_KEY_RIGHT),
+
+ // navigation block
+ .init(kVK_Help, MP_KEY_INSERT), .init(kVK_ForwardDelete, MP_KEY_DELETE),
+ .init(kVK_Home, MP_KEY_HOME), .init(kVK_End, MP_KEY_END),
+ .init(kVK_PageUp, MP_KEY_PAGE_UP), .init(kVK_PageDown, MP_KEY_PAGE_DOWN),
+
+ // F-keys
+ .init(kVK_F1, MP_KEY_F + 1), .init(kVK_F2, MP_KEY_F + 2), .init(kVK_F3, MP_KEY_F + 3),
+ .init(kVK_F4, MP_KEY_F + 4), .init(kVK_F5, MP_KEY_F + 5), .init(kVK_F6, MP_KEY_F + 6),
+ .init(kVK_F7, MP_KEY_F + 7), .init(kVK_F8, MP_KEY_F + 8), .init(kVK_F9, MP_KEY_F + 9),
+ .init(kVK_F10, MP_KEY_F + 10), .init(kVK_F11, MP_KEY_F + 11), .init(kVK_F12, MP_KEY_F + 12),
+ .init(kVK_F13, MP_KEY_F + 13), .init(kVK_F14, MP_KEY_F + 14), .init(kVK_F15, MP_KEY_F + 15),
+ .init(kVK_F16, MP_KEY_F + 16), .init(kVK_F17, MP_KEY_F + 17), .init(kVK_F18, MP_KEY_F + 18),
+ .init(kVK_F19, MP_KEY_F + 19), .init(kVK_F20, MP_KEY_F + 20),
+
+ // numpad
+ .init(kVK_ANSI_KeypadPlus, Int32(Character("+").asciiValue ?? 0)),
+ .init(kVK_ANSI_KeypadMinus, Int32(Character("-").asciiValue ?? 0)),
+ .init(kVK_ANSI_KeypadMultiply, Int32(Character("*").asciiValue ?? 0)),
+ .init(kVK_ANSI_KeypadDivide, Int32(Character("/").asciiValue ?? 0)),
+ .init(kVK_ANSI_KeypadEnter, MP_KEY_KPENTER), .init(kVK_ANSI_KeypadDecimal, MP_KEY_KPDEC),
+ .init(kVK_ANSI_Keypad0, MP_KEY_KP0), .init(kVK_ANSI_Keypad1, MP_KEY_KP1),
+ .init(kVK_ANSI_Keypad2, MP_KEY_KP2), .init(kVK_ANSI_Keypad3, MP_KEY_KP3),
+ .init(kVK_ANSI_Keypad4, MP_KEY_KP4), .init(kVK_ANSI_Keypad5, MP_KEY_KP5),
+ .init(kVK_ANSI_Keypad6, MP_KEY_KP6), .init(kVK_ANSI_Keypad7, MP_KEY_KP7),
+ .init(kVK_ANSI_Keypad8, MP_KEY_KP8), .init(kVK_ANSI_Keypad9, MP_KEY_KP9),
+
+ .init(0, 0)
+ ]
+
+ init(_ input: OpaquePointer? = nil, _ option: OptionHelper? = nil) {
+ super.init()
+ self.input = input
+ self.option = option
+ }
+
+ func put(
+ key: Int32,
+ modifiers: NSEvent.ModifierFlags = .init(rawValue: 0),
+ type: NSEvent.EventType = .applicationDefined
+ ) {
+ lock.withLock {
+ putKey(key, modifiers: modifiers, type: type)
+ }
+ }
+
+ private func putKey(
+ _ key: Int32,
+ modifiers: NSEvent.ModifierFlags = .init(rawValue: 0),
+ type: NSEvent.EventType = .applicationDefined
+ ) {
+ if key < 1 { return }
+
+ guard let input = input else { return }
+ let code = key | mapModifier(modifiers) | mapType(type)
+ mp_input_put_key(input, code)
+
+ if type == .keyUp {
+ mp_input_put_key(input, MP_INPUT_RELEASE_ALL)
+ }
+ }
+
+ @objc func processKey(event: NSEvent) -> Bool {
+ if event.type != .keyDown && event.type != .keyUp { return false }
+ if NSApp.mainMenu?.performKeyEquivalent(with: event) ?? false || event.isARepeat { return true }
+
+ return lock.withLock {
+ let mpkey = lookup_keymap_table(keymap, Int32(event.keyCode))
+ if mpkey > 0 {
+ putKey(mpkey, modifiers: event.modifierFlags, type: event.type)
+ return true
+ }
+
+ guard let chars = event.characters, let charsNoMod = event.charactersIgnoringModifiers else { return false }
+ let key = (useAltGr() && event.modifierFlags.contains(.optionRight)) ? chars : charsNoMod
+ key.withCString {
+ var bstr = bstr0($0)
+ putKey(bstr_decode_utf8(bstr, &bstr), modifiers: event.modifierFlags, type: event.type)
+ }
+ return true
+ }
+ }
+
+ func processMouse(event: NSEvent) {
+ if !mouseEnabled() { return }
+ lock.withLock {
+ putKey(map(button: event.buttonNumber), modifiers: event.modifierFlags, type: event.type)
+ if event.clickCount > 1 {
+ putKey(map(button: event.buttonNumber), modifiers: event.modifierFlags, type: .keyUp)
+ }
+ }
+ }
+
+ func processWheel(event: NSEvent) {
+ if !mouseEnabled() { return }
+ lock.withLock {
+ guard let input = input else { return }
+ let modifiers = event.modifierFlags
+ let precise = event.hasPreciseScrollingDeltas
+ var deltaX = event.deltaX * 0.1
+ var deltaY = event.deltaY * 0.1
+
+ if !precise {
+ deltaX = modifiers.contains(.shift) ? event.scrollingDeltaY : event.scrollingDeltaX
+ deltaY = modifiers.contains(.shift) ? event.scrollingDeltaX : event.scrollingDeltaY
+ }
+
+ var key = deltaY > 0 ? SWIFT_WHEEL_UP : SWIFT_WHEEL_DOWN
+ var delta = Double(deltaY)
+ if abs(deltaX) > abs(deltaY) {
+ key = deltaX > 0 ? SWIFT_WHEEL_LEFT : SWIFT_WHEEL_RIGHT
+ delta = Double(deltaX)
+ }
+
+ mp_input_put_wheel(input, key | mapModifier(modifiers), precise ? abs(delta) : 1)
+ }
+ }
+
+ func draggable(at pos: NSPoint) -> Bool {
+ lock.withLock {
+ guard let input = input else { return false }
+ return !mp_input_test_dragging(input, Int32(pos.x), Int32(pos.y))
+ }
+ }
+
+ func mouseEnabled() -> Bool {
+ lock.withLock {
+ guard let input = input else { return true }
+ return mp_input_mouse_enabled(input)
+ }
+ }
+
+ func setMouse(position: NSPoint) {
+ if !mouseEnabled() { return }
+ lock.withLock {
+ guard let input = input else { return }
+ mp_input_set_mouse_pos(input, Int32(position.x), Int32(position.y))
+ }
+ }
+
+ @discardableResult @objc func command(_ cmd: String) -> Bool {
+ lock.withLock {
+ guard let input = input else { return false }
+ let cCmd = UnsafePointer<Int8>(strdup(cmd))
+ let mpvCmd = mp_input_parse_cmd(input, bstr0(cCmd), "")
+ mp_input_queue_cmd(input, mpvCmd)
+ free(UnsafeMutablePointer(mutating: cCmd))
+ return true
+ }
+ }
+
+ private func mapType(_ type: NSEvent.EventType) -> Int32 {
+ let typeMapping: [NSEvent.EventType:UInt32] = [
+ .keyDown: MP_KEY_STATE_DOWN,
+ .keyUp: MP_KEY_STATE_UP,
+ .leftMouseDown: MP_KEY_STATE_DOWN,
+ .leftMouseUp: MP_KEY_STATE_UP,
+ .rightMouseDown: MP_KEY_STATE_DOWN,
+ .rightMouseUp: MP_KEY_STATE_UP,
+ .otherMouseDown: MP_KEY_STATE_DOWN,
+ .otherMouseUp: MP_KEY_STATE_UP,
+ ]
+
+ return Int32(typeMapping[type] ?? 0);
+ }
+
+ private func mapModifier(_ modifiers: NSEvent.ModifierFlags) -> Int32 {
+ var mask: UInt32 = 0;
+
+ if modifiers.contains(.shift) {
+ mask |= MP_KEY_MODIFIER_SHIFT
+ }
+ if modifiers.contains(.control) {
+ mask |= MP_KEY_MODIFIER_CTRL
+ }
+ if modifiers.contains(.command) {
+ mask |= MP_KEY_MODIFIER_META
+ }
+ if modifiers.contains(.optionLeft) || modifiers.contains(.optionRight) && !useAltGr() {
+ mask |= MP_KEY_MODIFIER_ALT
+ }
+
+ return Int32(mask)
+ }
+
+ private func map(button: Int) -> Int32 {
+ let buttonMapping: [Int:Int32] = [
+ 0: SWIFT_MBTN_LEFT,
+ 1: SWIFT_MBTN_RIGHT,
+ 2: SWIFT_MBTN_MID,
+ 3: SWIFT_MBTN_FORWARD,
+ 4: SWIFT_MBTN_BACK,
+ ]
+
+ return Int32(buttonMapping[button] ?? SWIFT_MBTN9 + Int32(button - 5));
+ }
+
+ @objc func open(files: [String], append: Bool = false) {
+ lock.withLock {
+ guard let input = input else { return }
+ if (option?.vo.drag_and_drop ?? -1) == -2 { return }
+
+ var action = DND_APPEND
+ if !append {
+ action = NSEvent.modifierFlags.contains(.shift) ? DND_APPEND : DND_REPLACE
+ if (option?.vo.drag_and_drop ?? -1) >= 0 {
+ action = mp_dnd_action(UInt32(option?.vo.drag_and_drop ?? Int32(DND_REPLACE.rawValue)))
+ }
+ }
+
+ let filesClean = files.map{ $0.hasPrefix("file:///.file/id=") ? (URL(string: $0)?.path ?? $0) : $0 }
+ var filesPtr = filesClean.map { UnsafeMutablePointer<CChar>(strdup($0)) }
+ mp_event_drop_files(input, Int32(files.count), &filesPtr, action)
+ for charPtr in filesPtr { free(UnsafeMutablePointer(mutating: charPtr)) }
+ }
+ }
+
+ private func useAltGr() -> Bool {
+ guard let input = input else { return false }
+ return mp_input_use_alt_gr(input)
+ }
+
+ @objc func wakeup() {
+ lock.withLock {
+ guard let input = input else { return }
+ mp_input_wakeup(input)
+ }
+ }
+
+ func signal(input: OpaquePointer? = nil) {
+ lock.withLock {
+ self.input = input
+ if input != nil { lock.signal() }
+ }
+ }
+
+ @objc func wait() {
+ lock.withLock { while input == nil { lock.wait() } }
+ }
+}
diff --git a/osdep/macos/libmpv_helper.swift b/osdep/mac/libmpv_helper.swift
index 8b1c697..23953eb 100644
--- a/osdep/macos/libmpv_helper.swift
+++ b/osdep/mac/libmpv_helper.swift
@@ -23,27 +23,14 @@ let glDummy: @convention(c) () -> Void = {}
class LibmpvHelper {
var log: LogHelper
- var mpvHandle: OpaquePointer?
+ var mpv: OpaquePointer?
var mpvRenderContext: OpaquePointer?
- var macOptsPtr: UnsafeMutableRawPointer?
- var macOpts: macos_opts = macos_opts()
var fbo: GLint = 1
- let deinitLock = NSLock()
-
- init(_ mpv: OpaquePointer, _ mpLog: OpaquePointer?) {
- mpvHandle = mpv
- log = LogHelper(mpLog)
-
- guard let app = NSApp as? Application,
- let ptr = mp_get_config_group(nil,
- mp_client_get_global(mpvHandle),
- app.getMacOSConf()) else
- {
- log.sendError("macOS config group couldn't be retrieved'")
- exit(1)
- }
- macOptsPtr = ptr
- macOpts = UnsafeMutablePointer<macos_opts>(OpaquePointer(ptr)).pointee
+ let uninitLock = NSLock()
+
+ init(_ mpv: OpaquePointer, _ log: LogHelper) {
+ self.mpv = mpv
+ self.log = log
}
func initRender() {
@@ -52,7 +39,7 @@ class LibmpvHelper {
let pAddress = mpv_opengl_init_params(get_proc_address: getProcAddress,
get_proc_address_ctx: nil)
- MPVHelper.withUnsafeMutableRawPointers([pAddress, advanced]) { (pointers: [UnsafeMutableRawPointer?]) in
+ TypeHelper.withUnsafeMutableRawPointers([pAddress, advanced]) { (pointers: [UnsafeMutableRawPointer?]) in
var params: [mpv_render_param] = [
mpv_render_param(type: MPV_RENDER_PARAM_API_TYPE, data: api),
mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, data: pointers[0]),
@@ -60,12 +47,11 @@ class LibmpvHelper {
mpv_render_param()
]
- if (mpv_render_context_create(&mpvRenderContext, mpvHandle, &params) < 0) {
- log.sendError("Render context init has failed.")
+ if (mpv_render_context_create(&mpvRenderContext, mpv, &params) < 0) {
+ log.error("Render context init has failed.")
exit(1)
}
}
-
}
let getProcAddress: (@convention(c) (UnsafeMutableRawPointer?, UnsafePointer<Int8>?)
@@ -87,17 +73,17 @@ class LibmpvHelper {
func setRenderUpdateCallback(_ callback: @escaping mpv_render_update_fn, context object: AnyObject) {
if mpvRenderContext == nil {
- log.sendWarning("Init mpv render context first.")
+ log.warning("Init mpv render context first.")
} else {
- mpv_render_context_set_update_callback(mpvRenderContext, callback, MPVHelper.bridge(obj: object))
+ mpv_render_context_set_update_callback(mpvRenderContext, callback, TypeHelper.bridge(obj: object))
}
}
func setRenderControlCallback(_ callback: @escaping mp_render_cb_control_fn, context object: AnyObject) {
if mpvRenderContext == nil {
- log.sendWarning("Init mpv render context first.")
+ log.warning("Init mpv render context first.")
} else {
- mp_render_context_set_control_callback(mpvRenderContext, callback, MPVHelper.bridge(obj: object))
+ mp_render_context_set_control_callback(mpvRenderContext, callback, TypeHelper.bridge(obj: object))
}
}
@@ -107,18 +93,18 @@ class LibmpvHelper {
}
func isRenderUpdateFrame() -> Bool {
- deinitLock.lock()
+ uninitLock.lock()
if mpvRenderContext == nil {
- deinitLock.unlock()
+ uninitLock.unlock()
return false
}
let flags: UInt64 = mpv_render_context_update(mpvRenderContext)
- deinitLock.unlock()
+ uninitLock.unlock()
return flags & UInt64(MPV_RENDER_UPDATE_FRAME.rawValue) > 0
}
func drawRender(_ surface: NSSize, _ depth: GLint, _ ctx: CGLContextObj, skip: Bool = false) {
- deinitLock.lock()
+ uninitLock.lock()
if mpvRenderContext != nil {
var i: GLint = 0
let flip: CInt = 1
@@ -134,7 +120,7 @@ class LibmpvHelper {
h: Int32(surface.height),
internal_format: 0)
- MPVHelper.withUnsafeMutableRawPointers([data, flip, ditherDepth, skip]) { (pointers: [UnsafeMutableRawPointer?]) in
+ TypeHelper.withUnsafeMutableRawPointers([data, flip, ditherDepth, skip]) { (pointers: [UnsafeMutableRawPointer?]) in
var params: [mpv_render_param] = [
mpv_render_param(type: MPV_RENDER_PARAM_OPENGL_FBO, data: pointers[0]),
mpv_render_param(type: MPV_RENDER_PARAM_FLIP_Y, data: pointers[1]),
@@ -151,13 +137,13 @@ class LibmpvHelper {
if !skip { CGLFlushDrawable(ctx) }
- deinitLock.unlock()
+ uninitLock.unlock()
}
func setRenderICCProfile(_ profile: NSColorSpace) {
if mpvRenderContext == nil { return }
guard var iccData = profile.iccProfileData else {
- log.sendWarning("Invalid ICC profile data.")
+ log.warning("Invalid ICC profile data.")
return
}
iccData.withUnsafeMutableBytes { (ptr: UnsafeMutableRawBufferPointer) in
@@ -182,69 +168,14 @@ class LibmpvHelper {
}
}
- func commandAsync(_ cmd: [String?], id: UInt64 = 1) {
- if mpvHandle == nil { return }
- var mCmd = cmd
- mCmd.append(nil)
- var cargs = mCmd.map { $0.flatMap { UnsafePointer<Int8>(strdup($0)) } }
- mpv_command_async(mpvHandle, id, &cargs)
- for ptr in cargs { free(UnsafeMutablePointer(mutating: ptr)) }
- }
-
- // Unsafe function when called while using the render API
- func command(_ cmd: String) {
- if mpvHandle == nil { return }
- mpv_command_string(mpvHandle, cmd)
- }
-
- func getBoolProperty(_ name: String) -> Bool {
- if mpvHandle == nil { return false }
- var value = Int32()
- mpv_get_property(mpvHandle, name, MPV_FORMAT_FLAG, &value)
- return value > 0
- }
-
- func getIntProperty(_ name: String) -> Int {
- if mpvHandle == nil { return 0 }
- var value = Int64()
- mpv_get_property(mpvHandle, name, MPV_FORMAT_INT64, &value)
- return Int(value)
- }
-
- func getStringProperty(_ name: String) -> String? {
- guard let mpv = mpvHandle else { return nil }
- guard let value = mpv_get_property_string(mpv, name) else { return nil }
- let str = String(cString: value)
- mpv_free(value)
- return str
- }
-
- func deinitRender() {
+ func uninit() {
mpv_render_context_set_update_callback(mpvRenderContext, nil, nil)
mp_render_context_set_control_callback(mpvRenderContext, nil, nil)
- deinitLock.lock()
+ uninitLock.lock()
mpv_render_context_free(mpvRenderContext)
mpvRenderContext = nil
- deinitLock.unlock()
- }
-
- func deinitMPV(_ destroy: Bool = false) {
- if destroy {
- mpv_destroy(mpvHandle)
- }
- ta_free(macOptsPtr)
- macOptsPtr = nil
- mpvHandle = nil
- }
-
- // *(char **) MPV_FORMAT_STRING on mpv_event_property
- class func mpvStringArrayToString(_ obj: UnsafeMutableRawPointer) -> String? {
- let cstr = UnsafeMutablePointer<UnsafeMutablePointer<Int8>>(OpaquePointer(obj))
- return String(cString: cstr[0])
- }
-
- // MPV_FORMAT_FLAG
- class func mpvFlagToBool(_ obj: UnsafeMutableRawPointer) -> Bool? {
- return UnsafePointer<Bool>(OpaquePointer(obj))?.pointee
+ mpv_destroy(mpv)
+ mpv = nil
+ uninitLock.unlock()
}
}
diff --git a/osdep/mac/log_helper.swift b/osdep/mac/log_helper.swift
new file mode 100644
index 0000000..3d349a4
--- /dev/null
+++ b/osdep/mac/log_helper.swift
@@ -0,0 +1,67 @@
+/*
+ * 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
+import os
+
+class LogHelper {
+ var log: OpaquePointer?
+ let logger = Logger(subsystem: "io.mpv", category: "mpv")
+
+ let loggerMapping: [Int:OSLogType] = [
+ MSGL_V: .debug,
+ MSGL_INFO: .info,
+ MSGL_WARN: .error,
+ MSGL_ERR: .fault,
+ ]
+
+ init(_ log: OpaquePointer? = nil) {
+ self.log = log
+ }
+
+ func verbose(_ message: String) {
+ send(message: message, type: MSGL_V)
+ }
+
+ func info(_ message: String) {
+ send(message: message, type: MSGL_INFO)
+ }
+
+ func warning(_ message: String) {
+ send(message: message, type: MSGL_WARN)
+ }
+
+ func error(_ message: String) {
+ send(message: message, type: MSGL_ERR)
+ }
+
+ func send(message: String, type: Int) {
+ guard let log = log else {
+ logger.log(level: loggerMapping[type] ?? .default, "\(message, privacy: .public)")
+ return
+ }
+
+ let args: [CVarArg] = [(message as NSString).utf8String ?? "NO MESSAGE"]
+ mp_msg_va(log, Int32(type), "%s\n", getVaList(args))
+ }
+
+ deinit {
+ // only a manual dereferencing will trigger this, cleanup properly in that case
+ ta_free(UnsafeMutablePointer(log))
+ log = nil
+ }
+}
diff --git a/osdep/mac/menu_bar.swift b/osdep/mac/menu_bar.swift
new file mode 100644
index 0000000..b58367a
--- /dev/null
+++ b/osdep/mac/menu_bar.swift
@@ -0,0 +1,397 @@
+/*
+ * 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
+
+extension MenuBar {
+ class MenuItem: NSMenuItem {
+ var config: Config?
+ }
+
+ enum `Type`: Comparable {
+ case menu
+ case menuServices
+ case separator
+ case item
+ case itemNormalSize
+ case itemHalfSize
+ case itemDoubleSize
+ case itemMinimize
+ case itemZoom
+ }
+
+ struct Config {
+ let name: String
+ let key: String
+ let modifiers: NSEvent.ModifierFlags
+ let type: Type
+ let action: Selector?
+ let target: AnyObject?
+ let command: String
+ let url: String
+ var configs: [Config]
+
+ init(
+ name: String = "",
+ key: String = "",
+ modifiers: NSEvent.ModifierFlags = .command,
+ type: Type = .item,
+ action: Selector? = nil,
+ target: AnyObject? = nil,
+ command: String = "",
+ url: String = "",
+ configs: [Config] = []
+ ) {
+ self.name = name
+ self.key = key
+ self.modifiers = modifiers
+ self.type = configs.isEmpty ? type : .menu
+ self.action = action
+ self.target = target
+ self.command = command
+ self.url = url
+ self.configs = configs
+ }
+ }
+}
+
+class MenuBar: NSObject {
+ unowned let appHub: AppHub
+ let mainMenu = NSMenu(title: "Main")
+ let servicesMenu = NSMenu(title: "Services")
+ var menuConfigs: [Config] = []
+ var dynamicMenuItems: [Type:[MenuItem]] = [:]
+ let appIcon: NSImage
+
+ @objc init(_ appHub: AppHub) {
+ self.appHub = appHub
+ UserDefaults.standard.set(false, forKey: "NSFullScreenMenuItemEverywhere")
+ UserDefaults.standard.set(true, forKey: "NSDisabledDictationMenuItem")
+ UserDefaults.standard.set(true, forKey: "NSDisabledCharacterPaletteMenuItem")
+ NSWindow.allowsAutomaticWindowTabbing = false
+ appIcon = appHub.getIcon()
+
+ super.init()
+
+ let appMenuConfigs = [
+ Config(name: "About mpv", action: #selector(about), target: self),
+ Config(type: .separator),
+ Config(
+ name: "Settings…",
+ key: ",",
+ action: #selector(settings(_:)),
+ target: self,
+ url: "mpv.conf"
+ ),
+ Config(
+ name: "Keyboard Shortcuts Config…",
+ action: #selector(settings(_:)),
+ target: self,
+ url: "input.conf"
+ ),
+ Config(type: .separator),
+ Config(name: "Services", type: .menuServices),
+ Config(type: .separator),
+ Config(name: "Hide mpv", key: "h", action: #selector(NSApp.hide(_:))),
+ Config(name: "Hide Others", key: "h", modifiers: [.command, .option], action: #selector(NSApp.hideOtherApplications(_:))),
+ Config(name: "Show All", action: #selector(NSApp.unhideAllApplications(_:))),
+ Config(type: .separator),
+ Config(name: "Quit and Remember Position", action: #selector(command(_:)), target: self, command: "quit-watch-later"),
+ Config(name: "Quit mpv", key: "q", action: #selector(command(_:)), target: self, command: "quit"),
+ ]
+
+ let fileMenuConfigs = [
+ Config(name: "Open File…", key: "o", action: #selector(openFiles), target: self),
+ Config(name: "Open URL…", key: "O", action: #selector(openUrl), target: self),
+ Config(name: "Open Playlist…", action: #selector(openPlaylist), target: self),
+ Config(type: .separator),
+ Config(name: "Close", key: "w", action: #selector(NSWindow.performClose(_:))),
+ Config(name: "Save Screenshot", action: #selector(command(_:)), target: self, command: "async screenshot"),
+ ]
+
+ let editMenuConfigs = [
+ Config(name: "Undo", key: "z", action: Selector(("undo:"))),
+ Config(name: "Redo", key: "Z", action: Selector(("redo:"))),
+ Config(type: .separator),
+ Config(name: "Cut", key: "x", action: #selector(NSText.cut(_:))),
+ Config(name: "Copy", key: "c", action: #selector(NSText.copy(_:))),
+ Config(name: "Paste", key: "v", action: #selector(NSText.paste(_:))),
+ Config(name: "Select All", key: "a", action: #selector(NSResponder.selectAll(_:))),
+ ]
+
+ var viewMenuConfigs = [
+ Config(name: "Toggle Fullscreen", action: #selector(command(_:)), target: self, command: "cycle fullscreen"),
+ Config(name: "Toggle Float on Top", action: #selector(command(_:)), target: self, command: "cycle ontop"),
+ Config(
+ name: "Toggle Visibility on All Workspaces",
+ action: #selector(command(_:)),
+ target: self,
+ command: "cycle on-all-workspaces"
+ ),
+ ]
+#if HAVE_MACOS_TOUCHBAR
+ viewMenuConfigs += [
+ Config(type: .separator),
+ Config(name: "Customize Touch Bar…", action: #selector(NSApp.toggleTouchBarCustomizationPalette(_:))),
+ ]
+#endif
+
+ let videoMenuConfigs = [
+ Config(name: "Zoom Out", action: #selector(command(_:)), target: self, command: "add panscan -0.1"),
+ Config(name: "Zoom In", action: #selector(command(_:)), target: self, command: "add panscan 0.1"),
+ Config(name: "Reset Zoom", action: #selector(command(_:)), target: self, command: "set panscan 0"),
+ Config(type: .separator),
+ Config(name: "Aspect Ratio 4:3", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"4:3\""),
+ Config(name: "Aspect Ratio 16:9", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"16:9\""),
+ Config(name: "Aspect Ratio 1.85:1", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"1.85:1\""),
+ Config(name: "Aspect Ratio 2.35:1", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"2.35:1\""),
+ Config(name: "Reset Aspect Ratio", action: #selector(command(_:)), target: self, command: "set video-aspect-override \"-1\""),
+ Config(type: .separator),
+ Config(name: "Rotate Left", action: #selector(command(_:)), target: self, command: "cycle-values video-rotate 0 270 180 90"),
+ Config(name: "Rotate Right", action: #selector(command(_:)), target: self, command: "cycle-values video-rotate 90 180 270 0"),
+ Config(name: "Reset Rotation", action: #selector(command(_:)), target: self, command: "set video-rotate 0"),
+ Config(type: .separator),
+ Config(name: "Half Size", key: "0", type: .itemHalfSize),
+ Config(name: "Normal Size", key: "1", type: .itemNormalSize),
+ Config(name: "Double Size", key: "2", type: .itemDoubleSize),
+ ]
+
+ let audioMenuConfigs = [
+ Config(name: "Next Audio Track", action: #selector(command(_:)), target: self, command: "cycle audio"),
+ Config(name: "Previous Audio Track", action: #selector(command(_:)), target: self, command: "cycle audio down"),
+ Config(type: .separator),
+ Config(name: "Toggle Mute", action: #selector(command(_:)), target: self, command: "cycle mute"),
+ Config(type: .separator),
+ Config(name: "Play Audio Later", action: #selector(command(_:)), target: self, command: "add audio-delay 0.1"),
+ Config(name: "Play Audio Earlier", action: #selector(command(_:)), target: self, command: "add audio-delay -0.1"),
+ Config(name: "Reset Audio Delay", action: #selector(command(_:)), target: self, command: "set audio-delay 0.0"),
+ ]
+
+ let subtitleMenuConfigs = [
+ Config(name: "Next Subtitle Track", action: #selector(command(_:)), target: self, command: "cycle sub"),
+ Config(name: "Previous Subtitle Track", action: #selector(command(_:)), target: self, command: "cycle sub down"),
+ Config(type: .separator),
+ Config(name: "Toggle Force Style", action: #selector(command(_:)), target: self, command: "cycle-values sub-ass-override \"force\" \"no\""),
+ Config(type: .separator),
+ Config(name: "Display Subtitles Later", action: #selector(command(_:)), target: self, command: "add sub-delay 0.1"),
+ Config(name: "Display Subtitles Earlier", action: #selector(command(_:)), target: self, command: "add sub-delay -0.1"),
+ Config(name: "Reset Subtitle Delay", action: #selector(command(_:)), target: self, command: "set sub-delay 0.0"),
+ ]
+
+ let playbackMenuConfigs = [
+ Config(name: "Toggle Pause", action: #selector(command(_:)), target: self, command: "cycle pause"),
+ Config(name: "Increase Speed", action: #selector(command(_:)), target: self, command: "add speed 0.1"),
+ Config(name: "Decrease Speed", action: #selector(command(_:)), target: self, command: "add speed -0.1"),
+ Config(name: "Reset Speed", action: #selector(command(_:)), target: self, command: "set speed 1.0"),
+ Config(type: .separator),
+ Config(name: "Show Playlist", action: #selector(command(_:)), target: self, command: "script-message osc-playlist"),
+ Config(name: "Show Chapters", action: #selector(command(_:)), target: self, command: "script-message osc-chapterlist"),
+ Config(name: "Show Tracks", action: #selector(command(_:)), target: self, command: "script-message osc-tracklist"),
+ Config(type: .separator),
+ Config(name: "Next File", action: #selector(command(_:)), target: self, command: "playlist-next"),
+ Config(name: "Previous File", action: #selector(command(_:)), target: self, command: "playlist-prev"),
+ Config(name: "Toggle Loop File", action: #selector(command(_:)), target: self, command: "cycle-values loop-file \"inf\" \"no\""),
+ Config(name: "Toggle Loop Playlist", action: #selector(command(_:)), target: self, command: "cycle-values loop-playlist \"inf\" \"no\""),
+ Config(name: "Shuffle", action: #selector(command(_:)), target: self, command: "playlist-shuffle"),
+ Config(type: .separator),
+ Config(name: "Next Chapter", action: #selector(command(_:)), target: self, command: "add chapter 1"),
+ Config(name: "Previous Chapter", action: #selector(command(_:)), target: self, command: "add chapter -1"),
+ Config(type: .separator),
+ Config(name: "Step Forward", action: #selector(command(_:)), target: self, command: "frame-step"),
+ Config(name: "Step Backward", action: #selector(command(_:)), target: self, command: "frame-back-step"),
+ ]
+
+ let windowMenuConfigs = [
+ Config(name: "Minimize", key: "m", type: .itemMinimize),
+ Config(name: "Zoom", type: .itemZoom),
+ ]
+
+ var helpMenuConfigs = [
+ Config(name: "mpv Website…", action: #selector(url(_:)), target: self, url: "https://mpv.io"),
+ Config(name: "mpv on GitHub…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv"),
+ Config(type: .separator),
+ Config(name: "Online Manual…", action: #selector(url(_:)), target: self, url: "https://mpv.io/manual/master/"),
+ Config(name: "Online Wiki…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/wiki"),
+ Config(name: "Release Notes…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/blob/master/RELEASE_NOTES"),
+ Config(name: "Keyboard Shortcuts…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/blob/master/etc/input.conf"),
+ Config(type: .separator),
+ Config(name: "Report Issue…", action: #selector(url(_:)), target: self, url: "https://github.com/mpv-player/mpv/issues/new/choose"),
+ ]
+ if ProcessInfo.processInfo.environment["MPVBUNDLE"] == "true" {
+ helpMenuConfigs += [
+ Config(name: "Show log File…", action: #selector(showFile(_:)), target: self, url: NSHomeDirectory() + "/Library/Logs/mpv.log")
+ ]
+ }
+
+ menuConfigs = [
+ Config(name: "Apple", configs: appMenuConfigs),
+ Config(name: "File", configs: fileMenuConfigs),
+ Config(name: "Edit", configs: editMenuConfigs),
+ Config(name: "View", configs: viewMenuConfigs),
+ Config(name: "Video", configs: videoMenuConfigs),
+ Config(name: "Audio", configs: audioMenuConfigs),
+ Config(name: "Subtitle", configs: subtitleMenuConfigs),
+ Config(name: "Playback", configs: playbackMenuConfigs),
+ Config(name: "Window", configs: windowMenuConfigs),
+ Config(name: "Help", configs: helpMenuConfigs),
+ ]
+
+ createMenu(parentMenu: mainMenu, configs: menuConfigs)
+ NSApp.mainMenu = mainMenu
+ NSApp.servicesMenu = servicesMenu
+ }
+
+ func createMenu(parentMenu: NSMenu, configs: [Config]) {
+ for config in configs {
+ let item = createMenuItem(parentMenu: parentMenu, config: config)
+
+ if config.type <= .menuServices {
+ let menu = config.type == .menuServices ? servicesMenu : NSMenu(title: config.name)
+ item.submenu = menu
+ createMenu(parentMenu: menu, configs: config.configs)
+ }
+
+ if config.type > Type.item {
+ dynamicMenuItems[config.type] = (dynamicMenuItems[config.type] ?? []) + [item]
+ }
+ }
+ }
+
+ func createMenuItem(parentMenu: NSMenu, config: Config) -> MenuItem {
+ var item = MenuItem(title: config.name, action: config.action, keyEquivalent: config.key)
+ item.config = config
+ item.target = config.target
+ item.keyEquivalentModifierMask = config.modifiers
+
+ if config.type == .separator {
+ item = MenuItem.separator() as? MenuItem ?? item
+ }
+ parentMenu.addItem(item)
+
+ return item
+ }
+
+ @objc func about() {
+ NSApp.orderFrontStandardAboutPanel(options: [
+ .applicationName: "mpv",
+ .applicationIcon: appIcon,
+ .applicationVersion: String(cString: swift_mpv_version),
+ .init(rawValue: "Copyright"): String(cString: swift_mpv_copyright),
+ ])
+ }
+
+ @objc func settings(_ menuItem: MenuItem) {
+ guard let menuConfig = menuItem.config else { return }
+ let configPaths: [URL] = [
+ URL(fileURLWithPath: NSHomeDirectory() + "/.config/mpv/", isDirectory: true),
+ URL(fileURLWithPath: NSHomeDirectory() + "/.mpv/", isDirectory: true),
+ ]
+
+ for path in configPaths {
+ let configFile = path.appendingPathComponent(menuConfig.url, isDirectory: false)
+
+ if FileManager.default.fileExists(atPath: configFile.path) {
+ if NSWorkspace.shared.open(configFile) {
+ return
+ }
+ NSWorkspace.shared.open(path)
+ alert(title: "No Application found to open your config file.", text: "Please open the \(menuConfig.url) file with your preferred text editor in the now open folder to edit your config.")
+ return
+ }
+
+ if NSWorkspace.shared.open(path) {
+ alert(title: "No config file found.", text: "Please create a \(menuConfig.url) file with your preferred text editor in the now open folder.")
+ return
+ }
+ }
+ }
+
+ @objc func openFiles() {
+ let panel = NSOpenPanel()
+ panel.allowsMultipleSelection = true
+ panel.canChooseDirectories = true
+
+ if panel.runModal() == .OK {
+ appHub.input.open(files: panel.urls.map { $0.path })
+ }
+ }
+
+ @objc func openPlaylist() {
+ let panel = NSOpenPanel()
+
+ if panel.runModal() == .OK, let url = panel.urls.first {
+ appHub.input.command("loadlist \"\(url.path)\"")
+ }
+ }
+
+ @objc func openUrl() {
+ let alert = NSAlert()
+ alert.messageText = "Open URL"
+ alert.icon = appIcon
+ alert.addButton(withTitle: "Ok")
+ alert.addButton(withTitle: "Cancel")
+
+ let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 300, height: 24))
+ input.placeholderString = "URL"
+ alert.accessoryView = input
+
+ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
+ input.becomeFirstResponder()
+ }
+
+ if alert.runModal() == .alertFirstButtonReturn && input.stringValue.count > 0 {
+ appHub.input.open(files: [input.stringValue])
+ }
+ }
+
+ @objc func command(_ menuItem: MenuItem) {
+ guard let menuConfig = menuItem.config else { return }
+ appHub.input.command(menuConfig.command)
+ }
+
+ @objc func url(_ menuItem: MenuItem) {
+ guard let menuConfig = menuItem.config,
+ let url = URL(string: menuConfig.url) else { return }
+ NSWorkspace.shared.open(url)
+ }
+
+ @objc func showFile(_ menuItem: MenuItem) {
+ guard let menuConfig = menuItem.config else { return }
+ let url = URL(fileURLWithPath: menuConfig.url)
+ if FileManager.default.fileExists(atPath: url.path) {
+ NSWorkspace.shared.activateFileViewerSelecting([url])
+ return
+ }
+
+ alert(title: "No log File found.", text: "You deactivated logging for the Bundle.")
+ }
+
+ func alert(title: String, text: String) {
+ let alert = NSAlert()
+ alert.messageText = title
+ alert.informativeText = text
+ alert.icon = appIcon
+ alert.addButton(withTitle: "Ok")
+ alert.runModal()
+ }
+
+ func register(_ selector: Selector, key: Type) {
+ for menuItem in dynamicMenuItems[key] ?? [] {
+ menuItem.action = selector
+ }
+ }
+}
diff --git a/osdep/meson.build b/osdep/mac/meson.build
index 21baafc..8ddbdba 100644
--- a/osdep/meson.build
+++ b/osdep/mac/meson.build
@@ -1,8 +1,8 @@
# custom swift targets
-bridge = join_paths(source_root, 'osdep/macOS_swift_bridge.h')
-header = join_paths(build_root, 'osdep/macOS_swift.h')
-module = join_paths(build_root, 'osdep/macOS_swift.swiftmodule')
-target = join_paths(build_root, 'osdep/macOS_swift.o')
+bridge = join_paths(source_root, 'osdep/mac/app_bridge_objc.h')
+header = join_paths(build_root, 'osdep/mac/swift.h')
+module = join_paths(build_root, 'osdep/mac/swift.swiftmodule')
+target = join_paths(build_root, 'osdep/mac/swift.o')
swift_flags = ['-frontend', '-c', '-sdk', macos_sdk_path,
'-enable-objc-interop', '-emit-objc-header', '-parse-as-library']
@@ -19,10 +19,22 @@ 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
-swift_compile = [swift_prog, swift_flags, '-module-name', 'macOS_swift',
+swift_compile = [swift_prog, swift_flags, '-module-name', 'swift',
'-emit-module-path', '@OUTPUT0@', '-import-objc-header', bridge,
'-emit-objc-header-path', '@OUTPUT1@', '-o', '@OUTPUT2@',
'@INPUT@', '-I.', '-I' + source_root,
@@ -31,7 +43,7 @@ swift_compile = [swift_prog, swift_flags, '-module-name', 'macOS_swift',
swift_targets = custom_target('swift_targets',
input: swift_sources,
- output: ['macOS_swift.swiftmodule', 'macOS_swift.h', 'macOS_swift.o'],
+ output: ['swift.swiftmodule', 'swift.h', 'swift.o'],
command: swift_compile,
)
sources += swift_targets
diff --git a/osdep/mac/option_helper.swift b/osdep/mac/option_helper.swift
new file mode 100644
index 0000000..c44f090
--- /dev/null
+++ b/osdep/mac/option_helper.swift
@@ -0,0 +1,74 @@
+/*
+ * 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
+
+typealias swift_wakeup_cb_fn = (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
+
+class OptionHelper {
+ var voCachePtr: UnsafeMutablePointer<m_config_cache>
+ var macCachePtr: UnsafeMutablePointer<m_config_cache>
+
+ var voPtr: UnsafeMutablePointer<mp_vo_opts>
+ { get { return UnsafeMutablePointer<mp_vo_opts>(OpaquePointer(voCachePtr.pointee.opts)) } }
+ var macPtr: UnsafeMutablePointer<macos_opts>
+ { get { return UnsafeMutablePointer<macos_opts>(OpaquePointer(macCachePtr.pointee.opts)) } }
+
+ // these computed properties return a local copy of the struct accessed:
+ // - don't use if you rely on the pointers
+ // - only for reading
+ var vo: mp_vo_opts { get { return voPtr.pointee } }
+ var mac: macos_opts { get { return macPtr.pointee } }
+
+ init(_ taParent: UnsafeMutableRawPointer, _ global: OpaquePointer?) {
+ voCachePtr = m_config_cache_alloc(taParent, global, AppHub.shared.getVoConf())
+ macCachePtr = m_config_cache_alloc(taParent, global, AppHub.shared.getMacConf())
+ }
+
+ func nextChangedOption(property: inout UnsafeMutableRawPointer?) -> Bool {
+ return m_config_cache_get_next_changed(voCachePtr, &property)
+ }
+
+ func setOption(fullscreen: Bool) {
+ voPtr.pointee.fullscreen = fullscreen
+ _ = withUnsafeMutableBytes(of: &voPtr.pointee.fullscreen) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(voCachePtr, ptr.baseAddress)
+ }
+ }
+
+ func setOption(minimized: Bool) {
+ voPtr.pointee.window_minimized = minimized
+ _ = withUnsafeMutableBytes(of: &voPtr.pointee.window_minimized) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(voCachePtr, ptr.baseAddress)
+ }
+ }
+
+ func setOption(maximized: Bool) {
+ voPtr.pointee.window_maximized = maximized
+ _ = withUnsafeMutableBytes(of: &voPtr.pointee.window_maximized) { (ptr: UnsafeMutableRawBufferPointer) in
+ m_config_cache_write_opt(voCachePtr, ptr.baseAddress)
+ }
+ }
+
+ func setMacOptionCallback(_ callback: swift_wakeup_cb_fn, context object: AnyObject) {
+ m_config_cache_set_wakeup_cb(macCachePtr, callback, TypeHelper.bridge(obj: object))
+ }
+
+ func nextChangedMacOption(property: inout UnsafeMutableRawPointer?) -> Bool {
+ return m_config_cache_get_next_changed(macCachePtr, &property)
+ }
+}
diff --git a/osdep/macos/precise_timer.swift b/osdep/mac/precise_timer.swift
index f4ad3bb..d4837f9 100644
--- a/osdep/macos/precise_timer.swift
+++ b/osdep/mac/precise_timer.swift
@@ -24,7 +24,6 @@ struct Timing {
class PreciseTimer {
unowned var common: Common
- var mpv: MPVHelper? { get { return common.mpv } }
let nanoPerSecond: Double = 1e+9
let machToNano: Double = {
@@ -59,9 +58,9 @@ class PreciseTimer {
init?(common com: Common) {
common = com
- pthread_create(&thread, &threadAttr, entryC, MPVHelper.bridge(obj: self))
+ pthread_create(&thread, &threadAttr, entryC, TypeHelper.bridge(obj: self))
if thread == nil {
- common.log.sendWarning("Couldn't create pthread for high precision timer")
+ common.log.warning("Couldn't create pthread for high precision timer")
return nil
}
@@ -85,7 +84,7 @@ class PreciseTimer {
isHighPrecision = success == KERN_SUCCESS
if !isHighPrecision {
- common.log.sendWarning("Couldn't create a high precision timer")
+ common.log.warning("Couldn't create a high precision timer")
}
}
@@ -119,7 +118,7 @@ class PreciseTimer {
let threadSignal: @convention(c) (Int32) -> () = { (sig: Int32) in }
let entryC: @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? = { (ptr: UnsafeMutableRawPointer) in
- let ptimer: PreciseTimer = MPVHelper.bridge(ptr: ptr)
+ let ptimer: PreciseTimer = TypeHelper.bridge(ptr: ptr)
ptimer.entry()
return nil
}
diff --git a/osdep/mac/presentation.swift b/osdep/mac/presentation.swift
new file mode 100644
index 0000000..c1d521a
--- /dev/null
+++ b/osdep/mac/presentation.swift
@@ -0,0 +1,56 @@
+/*
+ * 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/>.
+ */
+
+extension Presentation {
+ struct Time {
+ let cvTime: CVTimeStamp
+ var skipped: Int64 = 0
+ var time: Int64 { return mp_time_ns_from_raw_time(mp_raw_time_ns_from_mach(cvTime.hostTime)) }
+ var duration: Int64 {
+ let durationSeconds = Double(cvTime.videoRefreshPeriod) / Double(cvTime.videoTimeScale)
+ return Int64(durationSeconds * Presentation.nanoPerSecond * cvTime.rateScalar)
+ }
+
+ init(_ time: CVTimeStamp) {
+ cvTime = time
+ }
+ }
+}
+
+class Presentation {
+ unowned var common: Common
+ var times: [Time] = []
+ static let nanoPerSecond: Double = 1e+9
+
+ init(common com: Common) {
+ common = com
+ }
+
+ func add(time: CVTimeStamp) {
+ times.append(Time(time))
+ }
+
+ func next() -> Time? {
+ let now = mp_time_ns()
+ let count = times.count
+ times.removeAll(where: { $0.time <= now })
+ var time = times.first
+ time?.skipped = Int64(max(count - times.count - 1, 0))
+
+ return time
+ }
+}
diff --git a/osdep/mac/remote_command_center.swift b/osdep/mac/remote_command_center.swift
new file mode 100644
index 0000000..0e0eaeb
--- /dev/null
+++ b/osdep/mac/remote_command_center.swift
@@ -0,0 +1,202 @@
+/*
+ * 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
+import MediaPlayer
+
+extension RemoteCommandCenter {
+ typealias ConfigHandler = (MPRemoteCommandEvent) -> (MPRemoteCommandHandlerStatus)
+
+ enum KeyType {
+ case normal
+ case repeatable
+ }
+
+ struct Config {
+ let key: Int32
+ let type: KeyType
+ var state: NSEvent.EventType = .applicationDefined
+ let handler: ConfigHandler
+
+ init(key: Int32 = 0, type: KeyType = .normal, handler: @escaping ConfigHandler = { event in return .commandFailed }) {
+ self.key = key
+ self.type = type
+ self.handler = handler
+ }
+ }
+}
+
+class RemoteCommandCenter: EventSubscriber {
+ unowned let appHub: AppHub
+ var event: EventHelper? { get { return appHub.event } }
+ var input: InputHelper { get { return appHub.input } }
+ var configs: [MPRemoteCommand:Config] = [:]
+ var disabledCommands: [MPRemoteCommand] = []
+ var isPaused: Bool = false { didSet { updateInfoCenter() } }
+ var duration: Double = 0 { didSet { updateInfoCenter() } }
+ var position: Double = 0 { didSet { updateInfoCenter() } }
+ var rate: Double = 1 { didSet { updateInfoCenter() } }
+ var title: String = "" { didSet { updateInfoCenter() } }
+ var chapter: String? { didSet { updateInfoCenter() } }
+ var album: String? { didSet { updateInfoCenter() } }
+ var artist: String? { didSet { updateInfoCenter() } }
+ var cover: NSImage
+
+ var infoCenter: MPNowPlayingInfoCenter { get { return MPNowPlayingInfoCenter.default() } }
+ var commandCenter: MPRemoteCommandCenter { get { return MPRemoteCommandCenter.shared() } }
+
+ init(_ appHub: AppHub) {
+ self.appHub = appHub
+ cover = appHub.getIcon()
+
+ configs = [
+ commandCenter.pauseCommand: Config(key: MP_KEY_PAUSEONLY, handler: keyHandler),
+ commandCenter.playCommand: Config(key: MP_KEY_PLAYONLY, handler: keyHandler),
+ commandCenter.stopCommand: Config(key: MP_KEY_STOP, handler: keyHandler),
+ commandCenter.nextTrackCommand: Config(key: MP_KEY_NEXT, handler: keyHandler),
+ commandCenter.previousTrackCommand: Config(key: MP_KEY_PREV, handler: keyHandler),
+ commandCenter.togglePlayPauseCommand: Config(key: MP_KEY_PLAY, handler: keyHandler),
+ commandCenter.seekForwardCommand: Config(key: MP_KEY_FORWARD, type: .repeatable, handler: keyHandler),
+ commandCenter.seekBackwardCommand: Config(key: MP_KEY_REWIND, type: .repeatable, handler: keyHandler),
+ commandCenter.changePlaybackPositionCommand: Config(handler: seekHandler),
+ ]
+
+ disabledCommands = [
+ commandCenter.changePlaybackRateCommand,
+ commandCenter.changeRepeatModeCommand,
+ commandCenter.changeShuffleModeCommand,
+ commandCenter.skipForwardCommand,
+ commandCenter.skipBackwardCommand,
+ commandCenter.enableLanguageOptionCommand,
+ commandCenter.disableLanguageOptionCommand,
+ commandCenter.ratingCommand,
+ commandCenter.likeCommand,
+ commandCenter.dislikeCommand,
+ commandCenter.bookmarkCommand,
+ ]
+
+ for cmd in disabledCommands {
+ cmd.isEnabled = false
+ }
+ }
+
+ func registerEvents() {
+ event?.subscribe(self, event: .init(name: "duration", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "time-pos", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "speed", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "pause", format: MPV_FORMAT_FLAG))
+ event?.subscribe(self, event: .init(name: "media-title", format: MPV_FORMAT_STRING))
+ event?.subscribe(self, event: .init(name: "chapter-metadata/title", format: MPV_FORMAT_STRING))
+ event?.subscribe(self, event: .init(name: "metadata/by-key/album", format: MPV_FORMAT_STRING))
+ event?.subscribe(self, event: .init(name: "metadata/by-key/artist", format: MPV_FORMAT_STRING))
+ }
+
+ func start() {
+ for (cmd, config) in configs {
+ cmd.isEnabled = true
+ cmd.addTarget(handler: config.handler)
+ }
+
+ updateInfoCenter()
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(self.makeCurrent),
+ name: NSApplication.willBecomeActiveNotification,
+ object: nil
+ )
+ }
+
+ func stop() {
+ for (cmd, _) in configs {
+ cmd.isEnabled = false
+ cmd.removeTarget(nil)
+ }
+
+ infoCenter.nowPlayingInfo = nil
+ infoCenter.playbackState = .unknown
+
+ NotificationCenter.default.removeObserver(
+ self,
+ name: NSApplication.willBecomeActiveNotification,
+ object: nil
+ )
+ }
+
+ @objc func makeCurrent(notification: NSNotification) {
+ infoCenter.playbackState = .paused
+ infoCenter.playbackState = .playing
+ updateInfoCenter()
+ }
+
+ func updateInfoCenter() {
+ infoCenter.playbackState = isPaused ? .paused : .playing
+ infoCenter.nowPlayingInfo = (infoCenter.nowPlayingInfo ?? [:]).merging([
+ MPNowPlayingInfoPropertyMediaType: NSNumber(value: MPNowPlayingInfoMediaType.video.rawValue),
+ MPNowPlayingInfoPropertyPlaybackProgress: NSNumber(value: 0.0),
+ MPNowPlayingInfoPropertyPlaybackRate: NSNumber(value: isPaused ? 0 : rate),
+ MPNowPlayingInfoPropertyElapsedPlaybackTime: NSNumber(value: position),
+ MPMediaItemPropertyPlaybackDuration: NSNumber(value: duration),
+ MPMediaItemPropertyTitle: title,
+ MPMediaItemPropertyArtist: artist ?? chapter ?? "",
+ MPMediaItemPropertyAlbumTitle: album ?? "",
+ MPMediaItemPropertyArtwork: MPMediaItemArtwork(boundsSize: cover.size) { _ in return self.cover }
+ ]) { (_, new) in new }
+ }
+
+ lazy var keyHandler: ConfigHandler = { event in
+ guard let config = self.configs[event.command] else {
+ return .commandFailed
+ }
+
+ var state = config.state
+ if config.type == .repeatable {
+ state = config.state == .keyDown ? .keyUp : .keyDown
+ self.configs[event.command]?.state = state
+ }
+ self.input.put(key: config.key, type: state)
+
+ return .success
+ }
+
+ lazy var seekHandler: ConfigHandler = { event in
+ guard let posEvent = event as? MPChangePlaybackPositionCommandEvent else {
+ return .commandFailed
+ }
+
+ let cmd = String(format: "seek %.02f absolute", posEvent.positionTime)
+ return self.input.command(cmd) ? .success : .commandFailed
+ }
+
+ func handle(event: EventHelper.Event) {
+ switch event.name {
+ case "time-pos":
+ let newPosition = max(event.double ?? 0, 0)
+ if Int((floor(newPosition) - floor(position)) / rate) != 0 {
+ position = newPosition
+ }
+ case "pause": isPaused = event.bool ?? false
+ case "duration": duration = event.double ?? 0
+ case "speed": rate = event.double ?? 1
+ case "media-title": title = event.string ?? ""
+ case "chapter-metadata/title": chapter = event.string
+ case "metadata/by-key/album": album = event.string
+ case "metadata/by-key/artist": artist = event.string
+ default: break
+ }
+ }
+}
diff --git a/osdep/macos/swift_compat.swift b/osdep/mac/swift_compat.swift
index 83059da..83059da 100644
--- a/osdep/macos/swift_compat.swift
+++ b/osdep/mac/swift_compat.swift
diff --git a/osdep/mac/swift_extensions.swift b/osdep/mac/swift_extensions.swift
new file mode 100644
index 0000000..ed6c86c
--- /dev/null
+++ b/osdep/mac/swift_extensions.swift
@@ -0,0 +1,93 @@
+/*
+ * 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
+import IOKit.hidsystem
+
+extension NSDeviceDescriptionKey {
+ static let screenNumber = NSDeviceDescriptionKey("NSScreenNumber")
+}
+
+extension NSScreen {
+ public var displayID: CGDirectDisplayID {
+ get {
+ return deviceDescription[.screenNumber] as? CGDirectDisplayID ?? 0
+ }
+ }
+}
+
+extension NSColor {
+ convenience init(hex: String) {
+ let int = Int(hex.dropFirst(), radix: 16) ?? 0
+ let alpha = CGFloat((int >> 24) & 0x000000FF)/255
+ let red = CGFloat((int >> 16) & 0x000000FF)/255
+ let green = CGFloat((int >> 8) & 0x000000FF)/255
+ let blue = CGFloat((int) & 0x000000FF)/255
+
+ self.init(calibratedRed: red, green: green, blue: blue, alpha: alpha)
+ }
+}
+
+extension NSEvent.ModifierFlags {
+ public static var optionLeft: NSEvent.ModifierFlags = .init(rawValue: UInt(NX_DEVICELALTKEYMASK))
+ public static var optionRight: NSEvent.ModifierFlags = .init(rawValue: UInt(NX_DEVICERALTKEYMASK))
+}
+
+extension mp_keymap {
+ init(_ f: Int, _ t: Int32) {
+ self.init(from: Int32(f), to: t)
+ }
+}
+
+extension mpv_event_id: CustomStringConvertible {
+ public var description: String {
+ switch self {
+ case MPV_EVENT_NONE: return "MPV_EVENT_NONE2"
+ case MPV_EVENT_SHUTDOWN: return "MPV_EVENT_SHUTDOWN"
+ case MPV_EVENT_LOG_MESSAGE: return "MPV_EVENT_LOG_MESSAGE"
+ case MPV_EVENT_GET_PROPERTY_REPLY: return "MPV_EVENT_GET_PROPERTY_REPLY"
+ case MPV_EVENT_SET_PROPERTY_REPLY: return "MPV_EVENT_SET_PROPERTY_REPLY"
+ case MPV_EVENT_COMMAND_REPLY: return "MPV_EVENT_COMMAND_REPLY"
+ case MPV_EVENT_START_FILE: return "MPV_EVENT_START_FILE"
+ case MPV_EVENT_END_FILE: return "MPV_EVENT_END_FILE"
+ case MPV_EVENT_FILE_LOADED: return "MPV_EVENT_FILE_LOADED"
+ case MPV_EVENT_IDLE: return "MPV_EVENT_IDLE"
+ case MPV_EVENT_TICK: return "MPV_EVENT_TICK"
+ case MPV_EVENT_CLIENT_MESSAGE: return "MPV_EVENT_CLIENT_MESSAGE"
+ case MPV_EVENT_VIDEO_RECONFIG: return "MPV_EVENT_VIDEO_RECONFIG"
+ case MPV_EVENT_AUDIO_RECONFIG: return "MPV_EVENT_AUDIO_RECONFIG"
+ case MPV_EVENT_SEEK: return "MPV_EVENT_SEEK"
+ case MPV_EVENT_PLAYBACK_RESTART: return "MPV_EVENT_PLAYBACK_RESTART"
+ case MPV_EVENT_PROPERTY_CHANGE: return "MPV_EVENT_PROPERTY_CHANGE"
+ case MPV_EVENT_QUEUE_OVERFLOW: return "MPV_EVENT_QUEUE_OVERFLOW"
+ case MPV_EVENT_HOOK: return "MPV_EVENT_HOOK"
+ default: return "MPV_EVENT_" + String(self.rawValue)
+ }
+ }
+}
+
+extension Bool {
+ init(_ int32: Int32) {
+ self.init(int32 != 0)
+ }
+}
+
+extension Int32 {
+ init(_ bool: Bool) {
+ self.init(bool ? 1 : 0)
+ }
+}
diff --git a/osdep/mac/touch_bar.swift b/osdep/mac/touch_bar.swift
new file mode 100644
index 0000000..8e64c51
--- /dev/null
+++ b/osdep/mac/touch_bar.swift
@@ -0,0 +1,297 @@
+/*
+ * 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
+
+extension NSTouchBar.CustomizationIdentifier {
+ public static let customId: NSTouchBar.CustomizationIdentifier = "io.mpv.touchbar"
+}
+
+extension NSTouchBarItem.Identifier {
+ public static let seekBar = NSTouchBarItem.Identifier(custom: ".seekbar")
+ public static let play = NSTouchBarItem.Identifier(custom: ".play")
+ public static let nextItem = NSTouchBarItem.Identifier(custom: ".nextItem")
+ public static let previousItem = NSTouchBarItem.Identifier(custom: ".previousItem")
+ public static let nextChapter = NSTouchBarItem.Identifier(custom: ".nextChapter")
+ public static let previousChapter = NSTouchBarItem.Identifier(custom: ".previousChapter")
+ public static let cycleAudio = NSTouchBarItem.Identifier(custom: ".cycleAudio")
+ public static let cycleSubtitle = NSTouchBarItem.Identifier(custom: ".cycleSubtitle")
+ public static let currentPosition = NSTouchBarItem.Identifier(custom: ".currentPosition")
+ public static let timeLeft = NSTouchBarItem.Identifier(custom: ".timeLeft")
+
+ init(custom: String) {
+ self.init(NSTouchBar.CustomizationIdentifier.customId + custom)
+ }
+}
+
+extension TouchBar {
+ typealias ViewHandler = (Config) -> (NSView)
+
+ struct Config {
+ let name: String
+ let command: String
+ var item: NSCustomTouchBarItem?
+ var constraint: NSLayoutConstraint?
+ let image: NSImage
+ let imageAlt: NSImage
+ let handler: ViewHandler
+
+ init(
+ name: String = "",
+ command: String = "",
+ item: NSCustomTouchBarItem? = nil,
+ constraint: NSLayoutConstraint? = nil,
+ image: NSImage? = nil,
+ imageAlt: NSImage? = nil,
+ handler: @escaping ViewHandler = { _ in return NSButton(title: "", target: nil, action: nil) }
+ ) {
+ self.name = name
+ self.command = command
+ self.item = item
+ self.constraint = constraint
+ self.image = image ?? NSImage(size: NSSize(width: 1, height: 1))
+ self.imageAlt = imageAlt ?? NSImage(size: NSSize(width: 1, height: 1))
+ self.handler = handler
+ }
+ }
+}
+
+class TouchBar: NSTouchBar, NSTouchBarDelegate, EventSubscriber {
+ unowned let appHub: AppHub
+ var event: EventHelper? { get { return appHub.event } }
+ var input: InputHelper { get { return appHub.input } }
+ var configs: [NSTouchBarItem.Identifier:Config] = [:]
+ var isPaused: Bool = false { didSet { updatePlayButton() } }
+ var position: Double = 0 { didSet { updateTouchBarTimeItems() } }
+ var duration: Double = 0 { didSet { updateTouchBarTimeItems() } }
+ var rate: Double = 1
+
+ init(_ appHub: AppHub) {
+ self.appHub = appHub
+ super.init()
+
+ configs = [
+ .seekBar: Config(name: "Seek Bar", command: "seek %f absolute-percent", handler: createSlider),
+ .currentPosition: Config(name: "Current Position", handler: createText),
+ .timeLeft: Config(name: "Time Left", handler: createText),
+ .play: Config(
+ name: "Play Button",
+ command: "cycle pause",
+ image: .init(named: NSImage.touchBarPauseTemplateName),
+ imageAlt: .init(named: NSImage.touchBarPlayTemplateName),
+ handler: createButton
+ ),
+ .previousItem: Config(
+ name: "Previous Playlist Item",
+ command: "playlist-prev",
+ image: .init(named: NSImage.touchBarGoBackTemplateName),
+ handler: createButton
+ ),
+ .nextItem: Config(
+ name: "Next Playlist Item",
+ command: "playlist-next",
+ image: .init(named: NSImage.touchBarGoForwardTemplateName),
+ handler: createButton
+ ),
+ .previousChapter: Config(
+ name: "Previous Chapter",
+ command: "add chapter -1",
+ image: .init(named: NSImage.touchBarSkipBackTemplateName),
+ handler: createButton
+ ),
+ .nextChapter: Config(
+ name: "Next Chapter",
+ command: "add chapter 1",
+ image: .init(named: NSImage.touchBarSkipAheadTemplateName),
+ handler: createButton
+ ),
+ .cycleAudio: Config(
+ name: "Cycle Audio",
+ command: "cycle audio",
+ image: .init(named: NSImage.touchBarAudioInputTemplateName),
+ handler: createButton
+ ),
+ .cycleSubtitle: Config(
+ name: "Cycle Subtitle",
+ command: "cycle sub",
+ image: .init(named: NSImage.touchBarComposeTemplateName),
+ handler: createButton
+ )
+ ]
+
+ delegate = self
+ customizationIdentifier = .customId;
+ defaultItemIdentifiers = [.play, .previousItem, .nextItem, .seekBar]
+ customizationAllowedItemIdentifiers = [.play, .seekBar, .previousItem, .nextItem,
+ .previousChapter, .nextChapter, .cycleAudio, .cycleSubtitle, .currentPosition, .timeLeft]
+ addObserver(self, forKeyPath: "visible", options: [.new], context: nil)
+
+ event?.subscribe(self, event: .init(name: "duration", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "time-pos", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "speed", format: MPV_FORMAT_DOUBLE))
+ event?.subscribe(self, event: .init(name: "pause", format: MPV_FORMAT_FLAG))
+ event?.subscribe(self, event: .init(name: "MPV_EVENT_END_FILE"))
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItem.Identifier) -> NSTouchBarItem? {
+ guard let config = configs[identifier] else { return nil }
+
+ let item = NSCustomTouchBarItem(identifier: identifier)
+ item.view = config.handler(config)
+ item.customizationLabel = config.name
+ configs[identifier]?.item = item
+ item.addObserver(self, forKeyPath: "visible", options: [.new], context: nil)
+ return item
+ }
+
+ lazy var createButton: ViewHandler = { config in
+ return NSButton(image: config.image, target: self, action: #selector(Self.buttonAction(_:)))
+ }
+
+ lazy var createText: ViewHandler = { config in
+ let text = NSTextField(labelWithString: "0:00")
+ text.alignment = .center
+ return text
+ }
+
+ lazy var createSlider: ViewHandler = { config in
+ let slider = NSSlider(target: self, action: #selector(Self.seekbarChanged(_:)))
+ slider.minValue = 0
+ slider.maxValue = 100
+ return slider
+ }
+
+ override func observeValue(
+ forKeyPath keyPath: String?,
+ of object: Any?,
+ change: [NSKeyValueChangeKey:Any]?,
+ context: UnsafeMutableRawPointer?
+ ) {
+ guard let visible = change?[.newKey] as? Bool else { return }
+ if keyPath == "isVisible" && visible {
+ updateTouchBarTimeItems()
+ updatePlayButton()
+ }
+ }
+
+ func updateTouchBarTimeItems() {
+ if !isVisible { return }
+ updateSlider()
+ updateTimeLeft()
+ updateCurrentPosition()
+ }
+
+ func updateSlider() {
+ guard let config = configs[.seekBar], let slider = config.item?.view as? NSSlider else { return }
+ if !(config.item?.isVisible ?? false) { return }
+
+ slider.isEnabled = duration > 0
+ if !slider.isHighlighted {
+ slider.doubleValue = slider.isEnabled ? (position / duration) * 100 : 0
+ }
+ }
+
+ func updateTimeLeft() {
+ guard let config = configs[.timeLeft], let text = config.item?.view as? NSTextField else { return }
+ if !(config.item?.isVisible ?? false) { return }
+
+ removeConstraintFor(identifier: .timeLeft)
+ text.stringValue = duration > 0 ? "-" + format(time: Int(floor(duration) - floor(position))) : ""
+ if !text.stringValue.isEmpty {
+ applyConstraintFrom(string: "-" + format(time: Int(duration)), identifier: .timeLeft)
+ }
+ }
+
+ func updateCurrentPosition() {
+ guard let config = configs[.currentPosition], let text = config.item?.view as? NSTextField else { return }
+ if !(config.item?.isVisible ?? false) { return }
+
+ text.stringValue = format(time: Int(floor(position)))
+ removeConstraintFor(identifier: .currentPosition)
+ applyConstraintFrom(string: format(time: Int(duration > 0 ? duration : position)), identifier: .currentPosition)
+ }
+
+ func updatePlayButton() {
+ guard let config = configs[.play], let button = config.item?.view as? NSButton else { return }
+ if !isVisible || !(config.item?.isVisible ?? false) { return }
+ button.image = isPaused ? configs[.play]?.imageAlt : configs[.play]?.image
+ }
+
+ @objc func buttonAction(_ button: NSButton) {
+ guard let identifier = getIdentifierFrom(view: button), let command = configs[identifier]?.command else { return }
+ input.command(command)
+ }
+
+ @objc func seekbarChanged(_ slider: NSSlider) {
+ guard let identifier = getIdentifierFrom(view: slider), let command = configs[identifier]?.command else { return }
+ input.command(String(format: command, slider.doubleValue))
+ }
+
+ func format(time: Int) -> String {
+ let formatter = DateComponentsFormatter()
+ formatter.unitsStyle = .positional
+ formatter.zeroFormattingBehavior = time >= (60 * 60) ? [.dropLeading] : []
+ formatter.allowedUnits = time >= (60 * 60) ? [.hour, .minute, .second] : [.minute, .second]
+ return formatter.string(from: TimeInterval(time)) ?? "0:00"
+ }
+
+ func removeConstraintFor(identifier: NSTouchBarItem.Identifier) {
+ guard let text = configs[identifier]?.item?.view as? NSTextField,
+ let constraint = configs[identifier]?.constraint as? NSLayoutConstraint else { return }
+ text.removeConstraint(constraint)
+ }
+
+ func applyConstraintFrom(string: String, identifier: NSTouchBarItem.Identifier) {
+ guard let text = configs[identifier]?.item?.view as? NSTextField else { return }
+ let fullString = string.components(separatedBy: .decimalDigits).joined(separator: "0")
+ let textField = NSTextField(labelWithString: fullString)
+ let con = NSLayoutConstraint(item: text, attribute: .width, relatedBy: .equal, toItem: nil,
+ attribute: .notAnAttribute, multiplier: 1.1, constant: ceil(textField.frame.size.width))
+ text.addConstraint(con)
+ configs[identifier]?.constraint = con
+ }
+
+ func getIdentifierFrom(view: NSView) -> NSTouchBarItem.Identifier? {
+ for (identifier, config) in configs {
+ if config.item?.view == view {
+ return identifier
+ }
+ }
+ return nil
+ }
+
+ func handle(event: EventHelper.Event) {
+ switch event.name {
+ case "MPV_EVENT_END_FILE":
+ position = 0
+ duration = 0
+ case "time-pos":
+ let newPosition = max(event.double ?? 0, 0)
+ if Int((floor(newPosition) - floor(position)) / rate) != 0 {
+ position = newPosition
+ }
+ case "pause": isPaused = event.bool ?? false
+ case "duration": duration = event.double ?? 0
+ case "speed": rate = event.double ?? 1
+ default: break
+ }
+ }
+}
diff --git a/osdep/mac/type_helper.swift b/osdep/mac/type_helper.swift
new file mode 100644
index 0000000..bd90d0a
--- /dev/null
+++ b/osdep/mac/type_helper.swift
@@ -0,0 +1,69 @@
+/*
+ * 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/>.
+ */
+
+class TypeHelper {
+ // (__bridge void*)
+ class func bridge<T: AnyObject>(obj: T) -> UnsafeMutableRawPointer {
+ return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
+ }
+
+ // (__bridge T*)
+ class func bridge<T: AnyObject>(ptr: UnsafeRawPointer) -> T {
+ return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
+ }
+
+ class func withUnsafeMutableRawPointers(_ arguments: [Any],
+ pointers: [UnsafeMutableRawPointer?] = [],
+ closure: (_ pointers: [UnsafeMutableRawPointer?]) -> Void) {
+ if arguments.count > 0 {
+ let args = Array(arguments.dropFirst(1))
+ var newPtrs = pointers
+ var firstArg = arguments.first
+ withUnsafeMutableBytes(of: &firstArg) { (ptr: UnsafeMutableRawBufferPointer) in
+ newPtrs.append(ptr.baseAddress)
+ withUnsafeMutableRawPointers(args, pointers: newPtrs, closure: closure)
+ }
+
+ return
+ }
+
+ closure(pointers)
+ }
+
+ class func toPointer<T>(_ value: inout T) -> UnsafeMutableRawPointer? {
+ return withUnsafeMutableBytes(of: &value) { (ptr: UnsafeMutableRawBufferPointer) in
+ ptr.baseAddress
+ }
+ }
+
+ // *(char **) MPV_FORMAT_STRING
+ class func toString(_ obj: UnsafeMutableRawPointer?) -> String? {
+ guard let str = obj else { return nil }
+ let cstr = UnsafeMutablePointer<UnsafeMutablePointer<Int8>>(OpaquePointer(str))
+ return String(cString: cstr[0])
+ }
+
+ // MPV_FORMAT_FLAG
+ class func toBool(_ obj: UnsafeMutableRawPointer) -> Bool? {
+ return UnsafePointer<Bool>(OpaquePointer(obj))?.pointee
+ }
+
+ // MPV_FORMAT_DOUBLE
+ class func toDouble(_ obj: UnsafeMutableRawPointer) -> Double? {
+ return UnsafePointer<Double>(OpaquePointer(obj))?.pointee
+ }
+}
diff --git a/osdep/macos/log_helper.swift b/osdep/macos/log_helper.swift
deleted file mode 100644
index 9464075..0000000
--- a/osdep/macos/log_helper.swift
+++ /dev/null
@@ -1,47 +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
-
-class LogHelper: NSObject {
- var log: OpaquePointer?
-
- init(_ log: OpaquePointer?) {
- self.log = log
- }
-
- func sendVerbose(_ msg: String) {
- send(message: msg, type: MSGL_V)
- }
-
- func sendInfo(_ msg: String) {
- send(message: msg, type: MSGL_INFO)
- }
-
- func sendWarning(_ msg: String) {
- send(message: msg, type: MSGL_WARN)
- }
-
- func sendError(_ msg: String) {
- send(message: msg, type: MSGL_ERR)
- }
-
- func send(message msg: String, type t: Int) {
- let args: [CVarArg] = [ (msg as NSString).utf8String ?? "NO MESSAGE"]
- mp_msg_va(log, Int32(t), "%s\n", getVaList(args))
- }
-}
diff --git a/osdep/macos/mpv_helper.swift b/osdep/macos/mpv_helper.swift
deleted file mode 100644
index 3b2a716..0000000
--- a/osdep/macos/mpv_helper.swift
+++ /dev/null
@@ -1,156 +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
-
-typealias swift_wakeup_cb_fn = (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
-
-class MPVHelper {
- var log: LogHelper
- var vo: UnsafeMutablePointer<vo>
- var optsCachePtr: UnsafeMutablePointer<m_config_cache>
- var optsPtr: UnsafeMutablePointer<mp_vo_opts>
- var macOptsCachePtr: UnsafeMutablePointer<m_config_cache>
- var macOptsPtr: UnsafeMutablePointer<macos_opts>
-
- // these computed properties return a local copy of the struct accessed:
- // - don't use if you rely on the pointers
- // - only for reading
- var vout: vo { get { return vo.pointee } }
- var optsCache: m_config_cache { get { return optsCachePtr.pointee } }
- var opts: mp_vo_opts { get { return optsPtr.pointee } }
- var macOptsCache: m_config_cache { get { return macOptsCachePtr.pointee } }
- var macOpts: macos_opts { get { return macOptsPtr.pointee } }
-
- var input: OpaquePointer { get { return vout.input_ctx } }
-
- init(_ vo: UnsafeMutablePointer<vo>, _ log: LogHelper) {
- self.vo = vo
- self.log = log
-
- guard let app = NSApp as? Application,
- let cache = m_config_cache_alloc(vo, vo.pointee.global, app.getVoSubConf()) else
- {
- log.sendError("NSApp couldn't be retrieved")
- exit(1)
- }
-
- optsCachePtr = cache
- optsPtr = UnsafeMutablePointer<mp_vo_opts>(OpaquePointer(cache.pointee.opts))
-
- guard let macCache = m_config_cache_alloc(vo,
- vo.pointee.global,
- app.getMacOSConf()) else
- {
- // will never be hit, mp_get_config_group asserts for invalid groups
- exit(1)
- }
- macOptsCachePtr = macCache
- macOptsPtr = UnsafeMutablePointer<macos_opts>(OpaquePointer(macCache.pointee.opts))
- }
-
- func canBeDraggedAt(_ pos: NSPoint) -> Bool {
- let canDrag = !mp_input_test_dragging(input, Int32(pos.x), Int32(pos.y))
- return canDrag
- }
-
- func mouseEnabled() -> Bool {
- return mp_input_mouse_enabled(input)
- }
-
- func setMousePosition(_ pos: NSPoint) {
- mp_input_set_mouse_pos(input, Int32(pos.x), Int32(pos.y))
- }
-
- func putAxis(_ mpkey: Int32, delta: Double) {
- mp_input_put_wheel(input, mpkey, delta)
- }
-
- func nextChangedOption(property: inout UnsafeMutableRawPointer?) -> Bool {
- return m_config_cache_get_next_changed(optsCachePtr, &property)
- }
-
- func setOption(fullscreen: Bool) {
- optsPtr.pointee.fullscreen = fullscreen
- _ = withUnsafeMutableBytes(of: &optsPtr.pointee.fullscreen) { (ptr: UnsafeMutableRawBufferPointer) in
- m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
- }
- }
-
- func setOption(minimized: Bool) {
- optsPtr.pointee.window_minimized = minimized
- _ = withUnsafeMutableBytes(of: &optsPtr.pointee.window_minimized) { (ptr: UnsafeMutableRawBufferPointer) in
- m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
- }
- }
-
- func setOption(maximized: Bool) {
- optsPtr.pointee.window_maximized = maximized
- _ = withUnsafeMutableBytes(of: &optsPtr.pointee.window_maximized) { (ptr: UnsafeMutableRawBufferPointer) in
- m_config_cache_write_opt(optsCachePtr, ptr.baseAddress)
- }
- }
-
- func setMacOptionCallback(_ callback: swift_wakeup_cb_fn, context object: AnyObject) {
- m_config_cache_set_wakeup_cb(macOptsCachePtr, callback, MPVHelper.bridge(obj: object))
- }
-
- func nextChangedMacOption(property: inout UnsafeMutableRawPointer?) -> Bool {
- return m_config_cache_get_next_changed(macOptsCachePtr, &property)
- }
-
- func command(_ cmd: String) {
- let cCmd = UnsafePointer<Int8>(strdup(cmd))
- let mpvCmd = mp_input_parse_cmd(input, bstr0(cCmd), "")
- mp_input_queue_cmd(input, mpvCmd)
- free(UnsafeMutablePointer(mutating: cCmd))
- }
-
- // (__bridge void*)
- class func bridge<T: AnyObject>(obj: T) -> UnsafeMutableRawPointer {
- return UnsafeMutableRawPointer(Unmanaged.passUnretained(obj).toOpaque())
- }
-
- // (__bridge T*)
- class func bridge<T: AnyObject>(ptr: UnsafeRawPointer) -> T {
- return Unmanaged<T>.fromOpaque(ptr).takeUnretainedValue()
- }
-
- class func withUnsafeMutableRawPointers(_ arguments: [Any],
- pointers: [UnsafeMutableRawPointer?] = [],
- closure: (_ pointers: [UnsafeMutableRawPointer?]) -> Void) {
- if arguments.count > 0 {
- let args = Array(arguments.dropFirst(1))
- var newPtrs = pointers
- var firstArg = arguments.first
- withUnsafeMutableBytes(of: &firstArg) { (ptr: UnsafeMutableRawBufferPointer) in
- newPtrs.append(ptr.baseAddress)
- withUnsafeMutableRawPointers(args, pointers: newPtrs, closure: closure)
- }
-
- return
- }
-
- closure(pointers)
- }
-
- class func getPointer<T>(_ value: inout T) -> UnsafeMutableRawPointer? {
- return withUnsafeMutableBytes(of: &value) { (ptr: UnsafeMutableRawBufferPointer) in
- ptr.baseAddress
- }
- }
-}
diff --git a/osdep/macos/remote_command_center.swift b/osdep/macos/remote_command_center.swift
deleted file mode 100644
index 6fb2229..0000000
--- a/osdep/macos/remote_command_center.swift
+++ /dev/null
@@ -1,191 +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 MediaPlayer
-
-class RemoteCommandCenter: NSObject {
- enum KeyType {
- case normal
- case repeatable
- }
-
- var config: [MPRemoteCommand:[String:Any]] = [
- MPRemoteCommandCenter.shared().pauseCommand: [
- "mpKey": MP_KEY_PAUSEONLY,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().playCommand: [
- "mpKey": MP_KEY_PLAYONLY,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().stopCommand: [
- "mpKey": MP_KEY_STOP,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().nextTrackCommand: [
- "mpKey": MP_KEY_NEXT,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().previousTrackCommand: [
- "mpKey": MP_KEY_PREV,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().togglePlayPauseCommand: [
- "mpKey": MP_KEY_PLAY,
- "keyType": KeyType.normal
- ],
- MPRemoteCommandCenter.shared().seekForwardCommand: [
- "mpKey": MP_KEY_FORWARD,
- "keyType": KeyType.repeatable,
- "state": MP_KEY_STATE_UP
- ],
- MPRemoteCommandCenter.shared().seekBackwardCommand: [
- "mpKey": MP_KEY_REWIND,
- "keyType": KeyType.repeatable,
- "state": MP_KEY_STATE_UP
- ],
- ]
-
- var nowPlayingInfo: [String: Any] = [
- MPNowPlayingInfoPropertyMediaType: NSNumber(value: MPNowPlayingInfoMediaType.video.rawValue),
- MPNowPlayingInfoPropertyDefaultPlaybackRate: NSNumber(value: 1),
- MPNowPlayingInfoPropertyPlaybackProgress: NSNumber(value: 0.0),
- MPMediaItemPropertyPlaybackDuration: NSNumber(value: 0),
- MPMediaItemPropertyTitle: "mpv",
- MPMediaItemPropertyAlbumTitle: "mpv",
- MPMediaItemPropertyArtist: "mpv",
- ]
-
- let disabledCommands: [MPRemoteCommand] = [
- MPRemoteCommandCenter.shared().changePlaybackRateCommand,
- MPRemoteCommandCenter.shared().changeRepeatModeCommand,
- MPRemoteCommandCenter.shared().changeShuffleModeCommand,
- MPRemoteCommandCenter.shared().skipForwardCommand,
- MPRemoteCommandCenter.shared().skipBackwardCommand,
- MPRemoteCommandCenter.shared().changePlaybackPositionCommand,
- MPRemoteCommandCenter.shared().enableLanguageOptionCommand,
- MPRemoteCommandCenter.shared().disableLanguageOptionCommand,
- MPRemoteCommandCenter.shared().ratingCommand,
- MPRemoteCommandCenter.shared().likeCommand,
- MPRemoteCommandCenter.shared().dislikeCommand,
- MPRemoteCommandCenter.shared().bookmarkCommand,
- ]
-
- var mpInfoCenter: MPNowPlayingInfoCenter { get { return MPNowPlayingInfoCenter.default() } }
- var isPaused: Bool = false { didSet { updatePlaybackState() } }
-
- @objc override init() {
- super.init()
-
- for cmd in disabledCommands {
- cmd.isEnabled = false
- }
- }
-
- @objc func start() {
- for (cmd, _) in config {
- cmd.isEnabled = true
- cmd.addTarget { [unowned self] event in
- return self.cmdHandler(event)
- }
- }
-
- if let app = NSApp as? Application, let icon = app.getMPVIcon() {
- let albumArt = MPMediaItemArtwork(boundsSize: icon.size) { _ in
- return icon
- }
- nowPlayingInfo[MPMediaItemPropertyArtwork] = albumArt
- }
-
- mpInfoCenter.nowPlayingInfo = nowPlayingInfo
- mpInfoCenter.playbackState = .playing
-
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(self.makeCurrent),
- name: NSApplication.willBecomeActiveNotification,
- object: nil
- )
- }
-
- @objc func stop() {
- for (cmd, _) in config {
- cmd.isEnabled = false
- cmd.removeTarget(nil)
- }
-
- mpInfoCenter.nowPlayingInfo = nil
- mpInfoCenter.playbackState = .unknown
- }
-
- @objc func makeCurrent(notification: NSNotification) {
- mpInfoCenter.playbackState = .paused
- mpInfoCenter.playbackState = .playing
- updatePlaybackState()
- }
-
- func updatePlaybackState() {
- mpInfoCenter.playbackState = isPaused ? .paused : .playing
- }
-
- func cmdHandler(_ event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
- guard let cmdConfig = config[event.command],
- let mpKey = cmdConfig["mpKey"] as? Int32,
- let keyType = cmdConfig["keyType"] as? KeyType else
- {
- return .commandFailed
- }
-
- var state = cmdConfig["state"] as? UInt32 ?? 0
-
- if let currentState = cmdConfig["state"] as? UInt32, keyType == .repeatable {
- state = MP_KEY_STATE_DOWN
- config[event.command]?["state"] = MP_KEY_STATE_DOWN
- if currentState == MP_KEY_STATE_DOWN {
- state = MP_KEY_STATE_UP
- config[event.command]?["state"] = MP_KEY_STATE_UP
- }
- }
-
- EventsResponder.sharedInstance().handleMPKey(mpKey, withMask: Int32(state))
-
- return .success
- }
-
- @objc func processEvent(_ event: UnsafeMutablePointer<mpv_event>) {
- switch event.pointee.event_id {
- case MPV_EVENT_PROPERTY_CHANGE:
- handlePropertyChange(event)
- default:
- break
- }
- }
-
- func handlePropertyChange(_ event: UnsafeMutablePointer<mpv_event>) {
- let pData = OpaquePointer(event.pointee.data)
- guard let property = UnsafePointer<mpv_event_property>(pData)?.pointee else {
- return
- }
-
- switch String(cString: property.name) {
- case "pause" where property.format == MPV_FORMAT_FLAG:
- isPaused = LibmpvHelper.mpvFlagToBool(property.data) ?? false
- default:
- break
- }
- }
-}
diff --git a/osdep/macos/swift_extensions.swift b/osdep/macos/swift_extensions.swift
deleted file mode 100644
index 127c568..0000000
--- a/osdep/macos/swift_extensions.swift
+++ /dev/null
@@ -1,58 +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
-
-extension NSDeviceDescriptionKey {
- static let screenNumber = NSDeviceDescriptionKey("NSScreenNumber")
-}
-
-extension NSScreen {
-
- public var displayID: CGDirectDisplayID {
- get {
- return deviceDescription[.screenNumber] as? CGDirectDisplayID ?? 0
- }
- }
-}
-
-extension NSColor {
-
- convenience init(hex: String) {
- let int = Int(hex.dropFirst(), radix: 16) ?? 0
- let alpha = CGFloat((int >> 24) & 0x000000FF)/255
- let red = CGFloat((int >> 16) & 0x000000FF)/255
- let green = CGFloat((int >> 8) & 0x000000FF)/255
- let blue = CGFloat((int) & 0x000000FF)/255
-
- self.init(calibratedRed: red, green: green, blue: blue, alpha: alpha)
- }
-}
-
-extension Bool {
-
- init(_ int32: Int32) {
- self.init(int32 != 0)
- }
-}
-
-extension Int32 {
-
- init(_ bool: Bool) {
- self.init(bool ? 1 : 0)
- }
-}
diff --git a/osdep/macosx_application.m b/osdep/macosx_application.m
deleted file mode 100644
index 73503ad..0000000
--- a/osdep/macosx_application.m
+++ /dev/null
@@ -1,375 +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/macosx_application_objc.h"
-#import "osdep/macosx_events_objc.h"
-#include "osdep/threads.h"
-#include "osdep/main-fn.h"
-
-#if HAVE_MACOS_TOUCHBAR
-#import "osdep/macosx_touchbar.h"
-#endif
-#if HAVE_MACOS_COCOA_CB
-#include "osdep/macOS_swift.h"
-#endif
-
-#define MPV_PROTOCOL @"mpv://"
-
-#define OPT_BASE_STRUCT struct macos_opts
-const struct m_sub_options macos_conf = {
- .opts = (const struct m_option[]) {
- {"macos-title-bar-appearance", OPT_CHOICE(macos_title_bar_appearance,
- {"auto", 0}, {"aqua", 1}, {"darkAqua", 2},
- {"vibrantLight", 3}, {"vibrantDark", 4},
- {"aquaHighContrast", 5}, {"darkAquaHighContrast", 6},
- {"vibrantLightHighContrast", 7},
- {"vibrantDarkHighContrast", 8})},
- {"macos-title-bar-material", OPT_CHOICE(macos_title_bar_material,
- {"titlebar", 0}, {"selection", 1}, {"menu", 2},
- {"popover", 3}, {"sidebar", 4}, {"headerView", 5},
- {"sheet", 6}, {"windowBackground", 7}, {"hudWindow", 8},
- {"fullScreen", 9}, {"toolTip", 10}, {"contentBackground", 11},
- {"underWindowBackground", 12}, {"underPageBackground", 13},
- {"dark", 14}, {"light", 15}, {"mediumLight", 16},
- {"ultraDark", 17})},
- {"macos-title-bar-color", OPT_COLOR(macos_title_bar_color)},
- {"macos-fs-animation-duration",
- OPT_CHOICE(macos_fs_animation_duration, {"default", -1}),
- M_RANGE(0, 1000)},
- {"macos-force-dedicated-gpu", OPT_BOOL(macos_force_dedicated_gpu)},
- {"macos-app-activation-policy", OPT_CHOICE(macos_app_activation_policy,
- {"regular", 0}, {"accessory", 1}, {"prohibited", 2})},
- {"macos-geometry-calculation", OPT_CHOICE(macos_geometry_calculation,
- {"visible", FRAME_VISIBLE}, {"whole", FRAME_WHOLE})},
- {"macos-render-timer", OPT_CHOICE(macos_render_timer,
- {"callback", RENDER_TIMER_CALLBACK}, {"precise", RENDER_TIMER_PRECISE},
- {"system", RENDER_TIMER_SYSTEM})},
- {"cocoa-cb-sw-renderer", OPT_CHOICE(cocoa_cb_sw_renderer,
- {"auto", -1}, {"no", 0}, {"yes", 1})},
- {"cocoa-cb-10bit-context", OPT_BOOL(cocoa_cb_10bit_context)},
- {0}
- },
- .size = sizeof(struct macos_opts),
- .defaults = &(const struct macos_opts){
- .macos_title_bar_color = {0, 0, 0, 0},
- .macos_fs_animation_duration = -1,
- .cocoa_cb_sw_renderer = -1,
- .cocoa_cb_10bit_context = true
- },
-};
-
-// Whether the NSApplication singleton was created. If this is false, we are
-// running in libmpv mode, and cocoa_main() was never called.
-static bool application_instantiated;
-
-static mp_thread playback_thread_id;
-
-@interface Application ()
-{
- EventsResponder *_eventsResponder;
-}
-
-@end
-
-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];
- });
-}
-
-@implementation Application
-@synthesize menuBar = _menu_bar;
-@synthesize openCount = _open_count;
-@synthesize cocoaCB = _cocoa_cb;
-
-- (void)sendEvent:(NSEvent *)event
-{
- if ([self modalWindow] || ![_eventsResponder processKeyEvent:event])
- [super sendEvent:event];
- [_eventsResponder wakeup];
-}
-
-- (id)init
-{
- if (self = [super init]) {
- _eventsResponder = [EventsResponder sharedInstance];
-
- NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
- [em setEventHandler:self
- andSelector:@selector(getUrl:withReplyEvent:)
- forEventClass:kInternetEventClass
- andEventID:kAEGetURL];
- }
-
- return self;
-}
-
-- (void)dealloc
-{
- NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
- [em removeEventHandlerForEventClass:kInternetEventClass
- andEventID:kAEGetURL];
- [em removeEventHandlerForEventClass:kCoreEventClass
- andEventID:kAEQuitApplication];
- [super dealloc];
-}
-
-static const char macosx_icon[] =
-#include "TOOLS/osxbundle/icon.icns.inc"
-;
-
-- (NSImage *)getMPVIcon
-{
- // The C string contains a trailing null, so we strip it away
- NSData *icon_data = [NSData dataWithBytesNoCopy:(void *)macosx_icon
- length:sizeof(macosx_icon) - 1
- freeWhenDone:NO];
- return [[NSImage alloc] initWithData:icon_data];
-}
-
-#if HAVE_MACOS_TOUCHBAR
-- (NSTouchBar *)makeTouchBar
-{
- TouchBar *tBar = [[TouchBar alloc] init];
- [tBar setApp:self];
- tBar.delegate = tBar;
- tBar.customizationIdentifier = customID;
- tBar.defaultItemIdentifiers = @[play, previousItem, nextItem, seekBar];
- tBar.customizationAllowedItemIdentifiers = @[play, seekBar, previousItem,
- nextItem, previousChapter, nextChapter, cycleAudio, cycleSubtitle,
- currentPosition, timeLeft];
- return tBar;
-}
-#endif
-
-- (void)processEvent:(struct mpv_event *)event
-{
-#if HAVE_MACOS_TOUCHBAR
- [(TouchBar *)self.touchBar processEvent:event];
-#endif
- if (_cocoa_cb) {
- [_cocoa_cb processEvent:event];
- }
-}
-
-- (void)setMpvHandle:(struct mpv_handle *)ctx
-{
-#if HAVE_MACOS_COCOA_CB
- [NSApp setCocoaCB:[[CocoaCB alloc] init:ctx]];
-#endif
-}
-
-- (const struct m_sub_options *)getMacOSConf
-{
- return &macos_conf;
-}
-
-- (const struct m_sub_options *)getVoSubConf
-{
- return &vo_sub_opts;
-}
-
-- (void)queueCommand:(char *)cmd
-{
- [_eventsResponder queueCommand:cmd];
-}
-
-- (void)stopMPV:(char *)cmd
-{
- if (![_eventsResponder queueCommand:cmd])
- terminate_cocoa_application();
-}
-
-- (void)applicationWillFinishLaunching:(NSNotification *)notification
-{
- NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
- [em setEventHandler:self
- andSelector:@selector(handleQuitEvent:withReplyEvent:)
- forEventClass:kCoreEventClass
- andEventID:kAEQuitApplication];
-}
-
-- (void)handleQuitEvent:(NSAppleEventDescriptor *)event
- withReplyEvent:(NSAppleEventDescriptor *)replyEvent
-{
- [self stopMPV:"quit"];
-}
-
-- (void)getUrl:(NSAppleEventDescriptor *)event
- withReplyEvent:(NSAppleEventDescriptor *)replyEvent
-{
- NSString *url =
- [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
-
- url = [url stringByReplacingOccurrencesOfString:MPV_PROTOCOL
- withString:@""
- options:NSAnchoredSearch
- range:NSMakeRange(0, [MPV_PROTOCOL length])];
-
- url = [url stringByRemovingPercentEncoding];
- [_eventsResponder handleFilesArray:@[url]];
-}
-
-- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
-{
- if (mpv_shared_app().openCount > 0) {
- mpv_shared_app().openCount--;
- return;
- }
- [self openFiles:filenames];
-}
-
-- (void)openFiles:(NSArray *)filenames
-{
- SEL cmpsel = @selector(localizedStandardCompare:);
- NSArray *files = [filenames sortedArrayUsingSelector:cmpsel];
- [_eventsResponder handleFilesArray:files];
-}
-@end
-
-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);
- }
-}
-
-void cocoa_register_menu_item_action(MPMenuKey key, void* action)
-{
- if (application_instantiated)
- [[NSApp menuBar] registerSelector:(SEL)action forKey:key];
-}
-
-static void init_cocoa_application(bool regular)
-{
- NSApp = mpv_shared_app();
- [NSApp setDelegate:NSApp];
- [NSApp setMenuBar:[[MenuBar alloc] init]];
-
- // 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 {
- application_instantiated = true;
- [[EventsResponder sharedInstance] setIsApplication:YES];
-
- 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);
- [[EventsResponder sharedInstance] waitForInputContext];
- 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/macosx_application_objc.h b/osdep/macosx_application_objc.h
deleted file mode 100644
index 11959a8..0000000
--- a/osdep/macosx_application_objc.h
+++ /dev/null
@@ -1,40 +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/macosx_application.h"
-#import "osdep/macosx_menubar_objc.h"
-
-@class CocoaCB;
-struct mpv_event;
-struct mpv_handle;
-
-@interface Application : NSApplication
-
-- (NSImage *)getMPVIcon;
-- (void)processEvent:(struct mpv_event *)event;
-- (void)queueCommand:(char *)cmd;
-- (void)stopMPV:(char *)cmd;
-- (void)openFiles:(NSArray *)filenames;
-- (void)setMpvHandle:(struct mpv_handle *)ctx;
-- (const struct m_sub_options *)getMacOSConf;
-- (const struct m_sub_options *)getVoSubConf;
-
-@property(nonatomic, retain) MenuBar *menuBar;
-@property(nonatomic, assign) size_t openCount;
-@property(nonatomic, retain) CocoaCB *cocoaCB;
-@end
diff --git a/osdep/macosx_events.h b/osdep/macosx_events.h
deleted file mode 100644
index 9188c8b..0000000
--- a/osdep/macosx_events.h
+++ /dev/null
@@ -1,36 +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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef MACOSX_EVENTS_H
-#define MACOSX_EVENTS_H
-#include "input/keycodes.h"
-
-struct input_ctx;
-struct mpv_handle;
-
-void cocoa_put_key(int keycode);
-void cocoa_put_key_with_modifiers(int keycode, int modifiers);
-
-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);
-
-#endif
diff --git a/osdep/macosx_events.m b/osdep/macosx_events.m
deleted file mode 100644
index 627077a..0000000
--- a/osdep/macosx_events.m
+++ /dev/null
@@ -1,408 +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 <http://www.gnu.org/licenses/>.
- */
-
-// Carbon header is included but Carbon is NOT linked to mpv's binary. This
-// file only needs this include to use the keycode definitions in keymap.
-#import <Carbon/Carbon.h>
-
-// Media keys definitions
-#import <IOKit/hidsystem/ev_keymap.h>
-#import <Cocoa/Cocoa.h>
-
-#include "mpv_talloc.h"
-#include "input/event.h"
-#include "input/input.h"
-#include "player/client.h"
-#include "input/keycodes.h"
-// doesn't make much sense, but needed to access keymap functionality
-#include "video/out/vo.h"
-
-#import "osdep/macosx_events_objc.h"
-#import "osdep/macosx_application_objc.h"
-
-#include "config.h"
-
-#if HAVE_MACOS_COCOA_CB
-#include "osdep/macOS_swift.h"
-#endif
-
-@interface EventsResponder ()
-{
- struct input_ctx *_inputContext;
- struct mpv_handle *_ctx;
- BOOL _is_application;
- NSCondition *_input_lock;
-}
-
-- (NSEvent *)handleKey:(NSEvent *)event;
-- (BOOL)setMpvHandle:(struct mpv_handle *)ctx;
-- (void)readEvents;
-- (void)startMediaKeys;
-- (void)stopMediaKeys;
-- (int)mapKeyModifiers:(int)cocoaModifiers;
-- (int)keyModifierMask:(NSEvent *)event;
-@end
-
-
-#define NSLeftAlternateKeyMask (0x000020 | NSEventModifierFlagOption)
-#define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption)
-
-static bool LeftAltPressed(int mask)
-{
- return (mask & NSLeftAlternateKeyMask) == NSLeftAlternateKeyMask;
-}
-
-static bool RightAltPressed(int mask)
-{
- return (mask & NSRightAlternateKeyMask) == NSRightAlternateKeyMask;
-}
-
-static const struct mp_keymap keymap[] = {
- // special keys
- {kVK_Return, MP_KEY_ENTER}, {kVK_Escape, MP_KEY_ESC},
- {kVK_Delete, MP_KEY_BACKSPACE}, {kVK_Option, MP_KEY_BACKSPACE},
- {kVK_Control, MP_KEY_BACKSPACE}, {kVK_Shift, MP_KEY_BACKSPACE},
- {kVK_Tab, MP_KEY_TAB},
-
- // cursor keys
- {kVK_UpArrow, MP_KEY_UP}, {kVK_DownArrow, MP_KEY_DOWN},
- {kVK_LeftArrow, MP_KEY_LEFT}, {kVK_RightArrow, MP_KEY_RIGHT},
-
- // navigation block
- {kVK_Help, MP_KEY_INSERT}, {kVK_ForwardDelete, MP_KEY_DELETE},
- {kVK_Home, MP_KEY_HOME}, {kVK_End, MP_KEY_END},
- {kVK_PageUp, MP_KEY_PAGE_UP}, {kVK_PageDown, MP_KEY_PAGE_DOWN},
-
- // F-keys
- {kVK_F1, MP_KEY_F + 1}, {kVK_F2, MP_KEY_F + 2}, {kVK_F3, MP_KEY_F + 3},
- {kVK_F4, MP_KEY_F + 4}, {kVK_F5, MP_KEY_F + 5}, {kVK_F6, MP_KEY_F + 6},
- {kVK_F7, MP_KEY_F + 7}, {kVK_F8, MP_KEY_F + 8}, {kVK_F9, MP_KEY_F + 9},
- {kVK_F10, MP_KEY_F + 10}, {kVK_F11, MP_KEY_F + 11}, {kVK_F12, MP_KEY_F + 12},
- {kVK_F13, MP_KEY_F + 13}, {kVK_F14, MP_KEY_F + 14}, {kVK_F15, MP_KEY_F + 15},
- {kVK_F16, MP_KEY_F + 16}, {kVK_F17, MP_KEY_F + 17}, {kVK_F18, MP_KEY_F + 18},
- {kVK_F19, MP_KEY_F + 19}, {kVK_F20, MP_KEY_F + 20},
-
- // numpad
- {kVK_ANSI_KeypadPlus, '+'}, {kVK_ANSI_KeypadMinus, '-'},
- {kVK_ANSI_KeypadMultiply, '*'}, {kVK_ANSI_KeypadDivide, '/'},
- {kVK_ANSI_KeypadEnter, MP_KEY_KPENTER},
- {kVK_ANSI_KeypadDecimal, MP_KEY_KPDEC},
- {kVK_ANSI_Keypad0, MP_KEY_KP0}, {kVK_ANSI_Keypad1, MP_KEY_KP1},
- {kVK_ANSI_Keypad2, MP_KEY_KP2}, {kVK_ANSI_Keypad3, MP_KEY_KP3},
- {kVK_ANSI_Keypad4, MP_KEY_KP4}, {kVK_ANSI_Keypad5, MP_KEY_KP5},
- {kVK_ANSI_Keypad6, MP_KEY_KP6}, {kVK_ANSI_Keypad7, MP_KEY_KP7},
- {kVK_ANSI_Keypad8, MP_KEY_KP8}, {kVK_ANSI_Keypad9, MP_KEY_KP9},
-
- {0, 0}
-};
-
-static int convert_key(unsigned key, unsigned charcode)
-{
- int mpkey = lookup_keymap_table(keymap, key);
- if (mpkey)
- return mpkey;
- return charcode;
-}
-
-void cocoa_init_media_keys(void)
-{
- [[EventsResponder sharedInstance] startMediaKeys];
-}
-
-void cocoa_uninit_media_keys(void)
-{
- [[EventsResponder sharedInstance] stopMediaKeys];
-}
-
-void cocoa_put_key(int keycode)
-{
- [[EventsResponder sharedInstance] putKey:keycode];
-}
-
-void cocoa_put_key_with_modifiers(int keycode, int modifiers)
-{
- keycode |= [[EventsResponder sharedInstance] mapKeyModifiers:modifiers];
- cocoa_put_key(keycode);
-}
-
-void cocoa_set_input_context(struct input_ctx *input_context)
-{
- [[EventsResponder sharedInstance] setInputContext: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, "pause", MPV_FORMAT_FLAG);
- mpv_set_wakeup_callback(ctx, wakeup, NULL);
- }
-}
-
-@implementation EventsResponder
-
-@synthesize remoteCommandCenter = _remoteCommandCenter;
-
-+ (EventsResponder *)sharedInstance
-{
- static EventsResponder *responder = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- responder = [EventsResponder new];
- });
- return responder;
-}
-
-- (id)init
-{
- self = [super init];
- if (self) {
- _input_lock = [NSCondition new];
- }
- return self;
-}
-
-- (void)waitForInputContext
-{
- [_input_lock lock];
- while (!_inputContext)
- [_input_lock wait];
- [_input_lock unlock];
-}
-
-- (void)setInputContext:(struct input_ctx *)ctx
-{
- [_input_lock lock];
- _inputContext = ctx;
- [_input_lock signal];
- [_input_lock unlock];
-}
-
-- (void)wakeup
-{
- [_input_lock lock];
- if (_inputContext)
- mp_input_wakeup(_inputContext);
- [_input_lock unlock];
-}
-
-- (bool)queueCommand:(char *)cmd
-{
- bool r = false;
- [_input_lock lock];
- if (_inputContext) {
- mp_cmd_t *cmdt = mp_input_parse_cmd(_inputContext, bstr0(cmd), "");
- mp_input_queue_cmd(_inputContext, cmdt);
- r = true;
- }
- [_input_lock unlock];
- return r;
-}
-
-- (void)putKey:(int)keycode
-{
- [_input_lock lock];
- if (_inputContext)
- mp_input_put_key(_inputContext, keycode);
- [_input_lock unlock];
-}
-
-- (BOOL)useAltGr
-{
- BOOL r = YES;
- [_input_lock lock];
- if (_inputContext)
- r = mp_input_use_alt_gr(_inputContext);
- [_input_lock unlock];
- return r;
-}
-
-- (void)setIsApplication:(BOOL)isApplication
-{
- _is_application = isApplication;
-}
-
-- (BOOL)setMpvHandle:(struct mpv_handle *)ctx
-{
- if (_is_application) {
- dispatch_sync(dispatch_get_main_queue(), ^{
- _ctx = ctx;
- [NSApp setMpvHandle:ctx];
- });
- return YES;
- } else {
- mpv_destroy(ctx);
- return NO;
- }
-}
-
-- (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;
- }
- }
-}
-
-- (void)startMediaKeys
-{
-#if HAVE_MACOS_MEDIA_PLAYER
- if (_remoteCommandCenter == nil) {
- _remoteCommandCenter = [[RemoteCommandCenter alloc] init];
- }
-#endif
-
- [_remoteCommandCenter start];
-}
-
-- (void)stopMediaKeys
-{
- [_remoteCommandCenter stop];
-}
-
-- (int)mapKeyModifiers:(int)cocoaModifiers
-{
- int mask = 0;
- if (cocoaModifiers & NSEventModifierFlagShift)
- mask |= MP_KEY_MODIFIER_SHIFT;
- if (cocoaModifiers & NSEventModifierFlagControl)
- mask |= MP_KEY_MODIFIER_CTRL;
- if (LeftAltPressed(cocoaModifiers) ||
- (RightAltPressed(cocoaModifiers) && ![self useAltGr]))
- mask |= MP_KEY_MODIFIER_ALT;
- if (cocoaModifiers & NSEventModifierFlagCommand)
- mask |= MP_KEY_MODIFIER_META;
- return mask;
-}
-
-- (int)mapTypeModifiers:(NSEventType)type
-{
- NSDictionary *map = @{
- @(NSEventTypeKeyDown) : @(MP_KEY_STATE_DOWN),
- @(NSEventTypeKeyUp) : @(MP_KEY_STATE_UP),
- };
- return [map[@(type)] intValue];
-}
-
-- (int)keyModifierMask:(NSEvent *)event
-{
- return [self mapKeyModifiers:[event modifierFlags]] |
- [self mapTypeModifiers:[event type]];
-}
-
--(BOOL)handleMPKey:(int)key withMask:(int)mask
-{
- if (key > 0) {
- cocoa_put_key(key | mask);
- if (mask & MP_KEY_STATE_UP)
- cocoa_put_key(MP_INPUT_RELEASE_ALL);
- return YES;
- } else {
- return NO;
- }
-}
-
-- (NSEvent*)handleKey:(NSEvent *)event
-{
- if ([event isARepeat]) return nil;
-
- NSString *chars;
-
- if ([self useAltGr] && RightAltPressed([event modifierFlags])) {
- chars = [event characters];
- } else {
- chars = [event charactersIgnoringModifiers];
- }
-
- struct bstr t = bstr0([chars UTF8String]);
- int key = convert_key([event keyCode], bstr_decode_utf8(t, &t));
-
- if (key > -1)
- [self handleMPKey:key withMask:[self keyModifierMask:event]];
-
- return nil;
-}
-
-- (bool)processKeyEvent:(NSEvent *)event
-{
- if (event.type == NSEventTypeKeyDown || event.type == NSEventTypeKeyUp){
- if (![[NSApp mainMenu] performKeyEquivalent:event])
- [self handleKey:event];
- return true;
- }
- return false;
-}
-
-- (void)handleFilesArray:(NSArray *)files
-{
- enum mp_dnd_action action = [NSEvent modifierFlags] &
- NSEventModifierFlagShift ? DND_APPEND : DND_REPLACE;
-
- size_t num_files = [files count];
- char **files_utf8 = talloc_array(NULL, char*, num_files);
- [files enumerateObjectsUsingBlock:^(NSString *p, NSUInteger i, BOOL *_){
- if ([p hasPrefix:@"file:///.file/id="])
- p = [[NSURL URLWithString:p] path];
- char *filename = (char *)[p UTF8String];
- size_t bytes = [p lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- files_utf8[i] = talloc_memdup(files_utf8, filename, bytes + 1);
- }];
- [_input_lock lock];
- if (_inputContext)
- mp_event_drop_files(_inputContext, num_files, files_utf8, action);
- [_input_lock unlock];
- talloc_free(files_utf8);
-}
-
-@end
diff --git a/osdep/macosx_events_objc.h b/osdep/macosx_events_objc.h
deleted file mode 100644
index 9394fe7..0000000
--- a/osdep/macosx_events_objc.h
+++ /dev/null
@@ -1,45 +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 <http://www.gnu.org/licenses/>.
- */
-
-#import <Cocoa/Cocoa.h>
-#include "osdep/macosx_events.h"
-
-@class RemoteCommandCenter;
-struct input_ctx;
-
-@interface EventsResponder : NSObject
-
-+ (EventsResponder *)sharedInstance;
-- (void)setInputContext:(struct input_ctx *)ctx;
-- (void)setIsApplication:(BOOL)isApplication;
-
-/// Blocks until inputContext is present.
-- (void)waitForInputContext;
-- (void)wakeup;
-- (void)putKey:(int)keycode;
-- (void)handleFilesArray:(NSArray *)files;
-
-- (bool)queueCommand:(char *)cmd;
-- (bool)processKeyEvent:(NSEvent *)event;
-
-- (BOOL)handleMPKey:(int)key withMask:(int)mask;
-
-@property(nonatomic, retain) RemoteCommandCenter *remoteCommandCenter;
-
-@end
diff --git a/osdep/macosx_menubar.m b/osdep/macosx_menubar.m
deleted file mode 100644
index 5c6cd47..0000000
--- a/osdep/macosx_menubar.m
+++ /dev/null
@@ -1,853 +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 "config.h"
-#include "common/common.h"
-
-#import "macosx_menubar_objc.h"
-#import "osdep/macosx_application_objc.h"
-
-@implementation MenuBar
-{
- NSArray *menuTree;
-}
-
-- (id)init
-{
- if (self = [super init]) {
- NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];
- [userDefaults setBool:NO forKey:@"NSFullScreenMenuItemEverywhere"];
- [userDefaults setBool:YES forKey:@"NSDisabledDictationMenuItem"];
- [userDefaults setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"];
- [NSWindow setAllowsAutomaticWindowTabbing: NO];
-
- menuTree = @[
- @{
- @"name": @"Apple",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"About mpv",
- @"action" : @"about",
- @"key" : @"",
- @"target" : self
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Preferences…",
- @"action" : @"preferences:",
- @"key" : @",",
- @"target" : self,
- @"file" : @"mpv.conf",
- @"alertTitle1": @"No Application found to open your config file.",
- @"alertText1" : @"Please open the mpv.conf file with "
- "your preferred text editor in the now "
- "open folder to edit your config.",
- @"alertTitle2": @"No config file found.",
- @"alertText2" : @"Please create a mpv.conf file with your "
- "preferred text editor in the now open folder.",
- @"alertTitle3": @"No config path or file found.",
- @"alertText3" : @"Please create the following path ~/.config/mpv/ "
- "and a mpv.conf file within with your preferred "
- "text editor."
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Keyboard Shortcuts Config…",
- @"action" : @"preferences:",
- @"key" : @"",
- @"target" : self,
- @"file" : @"input.conf",
- @"alertTitle1": @"No Application found to open your config file.",
- @"alertText1" : @"Please open the input.conf file with "
- "your preferred text editor in the now "
- "open folder to edit your config.",
- @"alertTitle2": @"No config file found.",
- @"alertText2" : @"Please create a input.conf file with your "
- "preferred text editor in the now open folder.",
- @"alertTitle3": @"No config path or file found.",
- @"alertText3" : @"Please create the following path ~/.config/mpv/ "
- "and a input.conf file within with your preferred "
- "text editor."
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Services",
- @"key" : @"",
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Hide mpv",
- @"action" : @"hide:",
- @"key" : @"h",
- @"target" : NSApp
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Hide Others",
- @"action" : @"hideOtherApplications:",
- @"key" : @"h",
- @"modifiers" : [NSNumber numberWithUnsignedInteger:
- NSEventModifierFlagCommand |
- NSEventModifierFlagOption],
- @"target" : NSApp
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Show All",
- @"action" : @"unhideAllApplications:",
- @"key" : @"",
- @"target" : NSApp
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Quit and Remember Position",
- @"action" : @"quit:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"quit-watch-later"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Quit mpv",
- @"action" : @"quit:",
- @"key" : @"q",
- @"target" : self,
- @"cmd" : @"quit"
- }]
- ]
- },
- @{
- @"name": @"File",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Open File…",
- @"action" : @"openFile",
- @"key" : @"o",
- @"target" : self
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Open URL…",
- @"action" : @"openURL",
- @"key" : @"O",
- @"target" : self
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Open Playlist…",
- @"action" : @"openPlaylist",
- @"key" : @"",
- @"target" : self
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Close",
- @"action" : @"performClose:",
- @"key" : @"w"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Save Screenshot",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"async screenshot"
- }]
- ]
- },
- @{
- @"name": @"Edit",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Undo",
- @"action" : @"undo:",
- @"key" : @"z"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Redo",
- @"action" : @"redo:",
- @"key" : @"Z"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Cut",
- @"action" : @"cut:",
- @"key" : @"x"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Copy",
- @"action" : @"copy:",
- @"key" : @"c"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Paste",
- @"action" : @"paste:",
- @"key" : @"v"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Select All",
- @"action" : @"selectAll:",
- @"key" : @"a"
- }]
- ]
- },
- @{
- @"name": @"View",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Fullscreen",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle fullscreen"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Float on Top",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle ontop"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Visibility on All Workspaces",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle on-all-workspaces"
- }],
-#if HAVE_MACOS_TOUCHBAR
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Customize Touch Bar…",
- @"action" : @"toggleTouchBarCustomizationPalette:",
- @"key" : @"",
- @"target" : NSApp
- }]
-#endif
- ]
- },
- @{
- @"name": @"Video",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Zoom Out",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add panscan -0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Zoom In",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add panscan 0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Zoom",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set panscan 0"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Aspect Ratio 4:3",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-aspect-override \"4:3\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Aspect Ratio 16:9",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-aspect-override \"16:9\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Aspect Ratio 1.85:1",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-aspect-override \"1.85:1\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Aspect Ratio 2.35:1",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-aspect-override \"2.35:1\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Aspect Ratio",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-aspect-override \"-1\""
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Rotate Left",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle-values video-rotate 0 270 180 90"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Rotate Right",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle-values video-rotate 90 180 270 0"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Rotation",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set video-rotate 0"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Half Size",
- @"key" : @"0",
- @"cmdSpecial" : [NSNumber numberWithInt:MPM_H_SIZE]
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Normal Size",
- @"key" : @"1",
- @"cmdSpecial" : [NSNumber numberWithInt:MPM_N_SIZE]
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Double Size",
- @"key" : @"2",
- @"cmdSpecial" : [NSNumber numberWithInt:MPM_D_SIZE]
- }]
- ]
- },
- @{
- @"name": @"Audio",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Next Audio Track",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle audio"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Previous Audio Track",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle audio down"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Mute",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle mute"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Play Audio Later",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add audio-delay 0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Play Audio Earlier",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add audio-delay -0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Audio Delay",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set audio-delay 0.0 "
- }]
- ]
- },
- @{
- @"name": @"Subtitle",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Next Subtitle Track",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle sub"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Previous Subtitle Track",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle sub down"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Force Style",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle-values sub-ass-override \"force\" \"no\""
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Display Subtitles Later",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add sub-delay 0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Display Subtitles Earlier",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add sub-delay -0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Subtitle Delay",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set sub-delay 0.0"
- }]
- ]
- },
- @{
- @"name": @"Playback",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Pause",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle pause"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Increase Speed",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add speed 0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Decrease Speed",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add speed -0.1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Reset Speed",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"set speed 1.0"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Show Playlist",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"script-message osc-playlist"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Show Chapters",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"script-message osc-chapterlist"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Show Tracks",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"script-message osc-tracklist"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Next File",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"playlist-next"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Previous File",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"playlist-prev"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Loop File",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle-values loop-file \"inf\" \"no\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Toggle Loop Playlist",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"cycle-values loop-playlist \"inf\" \"no\""
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Shuffle",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"playlist-shuffle"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Next Chapter",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add chapter 1"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Previous Chapter",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"add chapter -1"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Step Forward",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"frame-step"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Step Backward",
- @"action" : @"cmd:",
- @"key" : @"",
- @"target" : self,
- @"cmd" : @"frame-back-step"
- }]
- ]
- },
- @{
- @"name": @"Window",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Minimize",
- @"key" : @"m",
- @"cmdSpecial" : [NSNumber numberWithInt:MPM_MINIMIZE]
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Zoom",
- @"key" : @"z",
- @"cmdSpecial" : [NSNumber numberWithInt:MPM_ZOOM]
- }]
- ]
- },
- @{
- @"name": @"Help",
- @"menu": @[
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"mpv Website…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://mpv.io"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"mpv on github…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://github.com/mpv-player/mpv"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Online Manual…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://mpv.io/manual/master/"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Online Wiki…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://github.com/mpv-player/mpv/wiki"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Release Notes…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://github.com/mpv-player/mpv/blob/master/RELEASE_NOTES"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Keyboard Shortcuts…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://github.com/mpv-player/mpv/blob/master/etc/input.conf"
- }],
- @{ @"name": @"separator" },
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Report Issue…",
- @"action" : @"url:",
- @"key" : @"",
- @"target" : self,
- @"url" : @"https://github.com/mpv-player/mpv/issues/new/choose"
- }],
- [NSMutableDictionary dictionaryWithDictionary:@{
- @"name" : @"Show log File…",
- @"action" : @"showFile:",
- @"key" : @"",
- @"target" : self,
- @"file" : @"~/Library/Logs/mpv.log",
- @"alertTitle" : @"No log File found.",
- @"alertText" : @"You deactivated logging for the Bundle."
- }]
- ]
- }
- ];
-
- [NSApp setMainMenu:[self mainMenu]];
- }
-
- return self;
-}
-
-- (NSMenu *)mainMenu
-{
- NSMenu *mainMenu = [[NSMenu alloc] initWithTitle:@"MainMenu"];
- [NSApp setServicesMenu:[[NSMenu alloc] init]];
- NSString* bundle = [[[NSProcessInfo processInfo] environment] objectForKey:@"MPVBUNDLE"];
-
- for(id mMenu in menuTree) {
- NSMenu *menu = [[NSMenu alloc] initWithTitle:mMenu[@"name"]];
- NSMenuItem *mItem = [mainMenu addItemWithTitle:mMenu[@"name"]
- action:nil
- keyEquivalent:@""];
- [mainMenu setSubmenu:menu forItem:mItem];
-
- for(id subMenu in mMenu[@"menu"]) {
- NSString *name = subMenu[@"name"];
- NSString *action = subMenu[@"action"];
-
-#if HAVE_MACOS_TOUCHBAR
- if ([action isEqual:@"toggleTouchBarCustomizationPalette:"]) {
- continue;
- }
-#endif
-
- if ([name isEqual:@"Show log File…"] && ![bundle isEqual:@"true"]) {
- continue;
- }
-
- if ([name isEqual:@"separator"]) {
- [menu addItem:[NSMenuItem separatorItem]];
- } else {
- NSMenuItem *iItem = [menu addItemWithTitle:name
- action:NSSelectorFromString(action)
- keyEquivalent:subMenu[@"key"]];
- [iItem setTarget:subMenu[@"target"]];
- [subMenu setObject:iItem forKey:@"menuItem"];
-
- NSNumber *m = subMenu[@"modifiers"];
- if (m) {
- [iItem setKeyEquivalentModifierMask:m.unsignedIntegerValue];
- }
-
- if ([subMenu[@"name"] isEqual:@"Services"]) {
- iItem.submenu = [NSApp servicesMenu];
- }
- }
- }
- }
-
- return mainMenu;
-}
-
-- (void)about
-{
- NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
- @"mpv", @"ApplicationName",
- [(Application *)NSApp getMPVIcon], @"ApplicationIcon",
- [NSString stringWithUTF8String:mpv_copyright], @"Copyright",
- [NSString stringWithUTF8String:mpv_version], @"ApplicationVersion",
- nil];
- [NSApp orderFrontStandardAboutPanelWithOptions:options];
-}
-
-- (void)preferences:(NSMenuItem *)menuItem
-{
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSMutableDictionary *mItemDict = [self getDictFromMenuItem:menuItem];
- NSArray *configPaths = @[
- [NSString stringWithFormat:@"%@/.mpv/", NSHomeDirectory()],
- [NSString stringWithFormat:@"%@/.config/mpv/", NSHomeDirectory()]];
-
- for (id path in configPaths) {
- NSString *fileP = [path stringByAppendingString:mItemDict[@"file"]];
- if ([fileManager fileExistsAtPath:fileP]){
- if ([workspace openFile:fileP])
- return;
- [workspace openFile:path];
- [self alertWithTitle:mItemDict[@"alertTitle1"]
- andText:mItemDict[@"alertText1"]];
- return;
- }
- if ([workspace openFile:path]) {
- [self alertWithTitle:mItemDict[@"alertTitle2"]
- andText:mItemDict[@"alertText2"]];
- return;
- }
- }
-
- [self alertWithTitle:mItemDict[@"alertTitle3"]
- andText:mItemDict[@"alertText3"]];
-}
-
-- (void)quit:(NSMenuItem *)menuItem
-{
- NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"];
- [(Application *)NSApp stopMPV:(char *)[cmd UTF8String]];
-}
-
-- (void)openFile
-{
- NSOpenPanel *panel = [[NSOpenPanel alloc] init];
- [panel setCanChooseDirectories:YES];
- [panel setAllowsMultipleSelection:YES];
-
- if ([panel runModal] == NSModalResponseOK){
- NSMutableArray *fileArray = [[NSMutableArray alloc] init];
- for (id url in [panel URLs])
- [fileArray addObject:[url path]];
- [(Application *)NSApp openFiles:fileArray];
- }
-}
-
-- (void)openPlaylist
-{
- NSOpenPanel *panel = [[NSOpenPanel alloc] init];
-
- if ([panel runModal] == NSModalResponseOK){
- NSString *pl = [NSString stringWithFormat:@"loadlist \"%@\"",
- [panel URLs][0].path];
- [(Application *)NSApp queueCommand:(char *)[pl UTF8String]];
- }
-}
-
-- (void)openURL
-{
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:@"Open URL"];
- [alert addButtonWithTitle:@"Ok"];
- [alert addButtonWithTitle:@"Cancel"];
- [alert setIcon:[(Application *)NSApp getMPVIcon]];
-
- NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 300, 24)];
- [input setPlaceholderString:@"URL"];
- [alert setAccessoryView:input];
-
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
- [input becomeFirstResponder];
- });
-
- if ([alert runModal] == NSAlertFirstButtonReturn && [input stringValue].length > 0) {
- NSArray *url = [NSArray arrayWithObjects:[input stringValue], nil];
- [(Application *)NSApp openFiles:url];
- }
-}
-
-- (void)cmd:(NSMenuItem *)menuItem
-{
- NSString *cmd = [self getDictFromMenuItem:menuItem][@"cmd"];
- [(Application *)NSApp queueCommand:(char *)[cmd UTF8String]];
-}
-
-- (void)url:(NSMenuItem *)menuItem
-{
- NSString *url = [self getDictFromMenuItem:menuItem][@"url"];
- [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
-}
-
-- (void)showFile:(NSMenuItem *)menuItem
-{
- NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
- NSFileManager *fileManager = [NSFileManager defaultManager];
- NSMutableDictionary *mItemDict = [self getDictFromMenuItem:menuItem];
- NSString *file = [mItemDict[@"file"] stringByExpandingTildeInPath];
-
- if ([fileManager fileExistsAtPath:file]){
- NSURL *url = [NSURL fileURLWithPath:file];
- NSArray *urlArray = [NSArray arrayWithObjects:url, nil];
-
- [workspace activateFileViewerSelectingURLs:urlArray];
- return;
- }
-
- [self alertWithTitle:mItemDict[@"alertTitle"]
- andText:mItemDict[@"alertText"]];
-}
-
-- (void)alertWithTitle:(NSString *)title andText:(NSString *)text
-{
- NSAlert *alert = [[NSAlert alloc] init];
- [alert setMessageText:title];
- [alert setInformativeText:text];
- [alert addButtonWithTitle:@"Ok"];
- [alert setIcon:[(Application *)NSApp getMPVIcon]];
- [alert runModal];
-}
-
-- (NSMutableDictionary *)getDictFromMenuItem:(NSMenuItem *)menuItem
-{
- for(id mMenu in menuTree) {
- for(id subMenu in mMenu[@"menu"]) {
- if([subMenu[@"menuItem"] isEqual:menuItem])
- return subMenu;
- }
- }
-
- return nil;
-}
-
-- (void)registerSelector:(SEL)action forKey:(MPMenuKey)key
-{
- for(id mMenu in menuTree) {
- for(id subMenu in mMenu[@"menu"]) {
- if([subMenu[@"cmdSpecial"] isEqual:[NSNumber numberWithInt:key]]) {
- [subMenu[@"menuItem"] setAction:action];
- return;
- }
- }
- }
-}
-
-@end
diff --git a/osdep/macosx_touchbar.h b/osdep/macosx_touchbar.h
deleted file mode 100644
index a03b68c..0000000
--- a/osdep/macosx_touchbar.h
+++ /dev/null
@@ -1,46 +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>
-#import "osdep/macosx_application_objc.h"
-
-#define BASE_ID @"io.mpv.touchbar"
-static NSTouchBarCustomizationIdentifier customID = BASE_ID;
-static NSTouchBarItemIdentifier seekBar = BASE_ID ".seekbar";
-static NSTouchBarItemIdentifier play = BASE_ID ".play";
-static NSTouchBarItemIdentifier nextItem = BASE_ID ".nextItem";
-static NSTouchBarItemIdentifier previousItem = BASE_ID ".previousItem";
-static NSTouchBarItemIdentifier nextChapter = BASE_ID ".nextChapter";
-static NSTouchBarItemIdentifier previousChapter = BASE_ID ".previousChapter";
-static NSTouchBarItemIdentifier cycleAudio = BASE_ID ".cycleAudio";
-static NSTouchBarItemIdentifier cycleSubtitle = BASE_ID ".cycleSubtitle";
-static NSTouchBarItemIdentifier currentPosition = BASE_ID ".currentPosition";
-static NSTouchBarItemIdentifier timeLeft = BASE_ID ".timeLeft";
-
-struct mpv_event;
-
-@interface TouchBar : NSTouchBar <NSTouchBarDelegate>
-
--(void)processEvent:(struct mpv_event *)event;
-
-@property(nonatomic, retain) Application *app;
-@property(nonatomic, retain) NSDictionary *touchbarItems;
-@property(nonatomic, assign) double duration;
-@property(nonatomic, assign) double position;
-@property(nonatomic, assign) int pause;
-
-@end
diff --git a/osdep/macosx_touchbar.m b/osdep/macosx_touchbar.m
deleted file mode 100644
index ccce8f7..0000000
--- a/osdep/macosx_touchbar.m
+++ /dev/null
@@ -1,334 +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 "player/client.h"
-#import "macosx_touchbar.h"
-
-@implementation TouchBar
-
-@synthesize app = _app;
-@synthesize touchbarItems = _touchbar_items;
-@synthesize duration = _duration;
-@synthesize position = _position;
-@synthesize pause = _pause;
-
-- (id)init
-{
- if (self = [super init]) {
- self.touchbarItems = @{
- seekBar: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"slider",
- @"name": @"Seek Bar",
- @"cmd": @"seek %f absolute-percent"
- }],
- play: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Play Button",
- @"cmd": @"cycle pause",
- @"image": [NSImage imageNamed:NSImageNameTouchBarPauseTemplate],
- @"imageAlt": [NSImage imageNamed:NSImageNameTouchBarPlayTemplate]
- }],
- previousItem: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Previous Playlist Item",
- @"cmd": @"playlist-prev",
- @"image": [NSImage imageNamed:NSImageNameTouchBarGoBackTemplate]
- }],
- nextItem: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Next Playlist Item",
- @"cmd": @"playlist-next",
- @"image": [NSImage imageNamed:NSImageNameTouchBarGoForwardTemplate]
- }],
- previousChapter: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Previous Chapter",
- @"cmd": @"add chapter -1",
- @"image": [NSImage imageNamed:NSImageNameTouchBarSkipBackTemplate]
- }],
- nextChapter: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Next Chapter",
- @"cmd": @"add chapter 1",
- @"image": [NSImage imageNamed:NSImageNameTouchBarSkipAheadTemplate]
- }],
- cycleAudio: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Cycle Audio",
- @"cmd": @"cycle audio",
- @"image": [NSImage imageNamed:NSImageNameTouchBarAudioInputTemplate]
- }],
- cycleSubtitle: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"button",
- @"name": @"Cycle Subtitle",
- @"cmd": @"cycle sub",
- @"image": [NSImage imageNamed:NSImageNameTouchBarComposeTemplate]
- }],
- currentPosition: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"text",
- @"name": @"Current Position"
- }],
- timeLeft: [NSMutableDictionary dictionaryWithDictionary:@{
- @"type": @"text",
- @"name": @"Time Left"
- }]
- };
-
- [self addObserver:self forKeyPath:@"visible" options:0 context:nil];
- }
- return self;
-}
-
-- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
- makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
-{
- if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"slider"]) {
- NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
- NSSlider *slider = [NSSlider sliderWithTarget:self action:@selector(seekbarChanged:)];
- slider.minValue = 0.0f;
- slider.maxValue = 100.0f;
- tbItem.view = slider;
- tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
- [self.touchbarItems[identifier] setObject:slider forKey:@"view"];
- [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
- [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
- return tbItem;
- } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"button"]) {
- NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
- NSImage *tbImage = self.touchbarItems[identifier][@"image"];
- NSButton *tbButton = [NSButton buttonWithImage:tbImage target:self action:@selector(buttonAction:)];
- tbItem.view = tbButton;
- tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
- [self.touchbarItems[identifier] setObject:tbButton forKey:@"view"];
- [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
- [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
- return tbItem;
- } else if ([self.touchbarItems[identifier][@"type"] isEqualToString:@"text"]) {
- NSCustomTouchBarItem *tbItem = [[NSCustomTouchBarItem alloc] initWithIdentifier:identifier];
- NSTextField *tbText = [NSTextField labelWithString:@"0:00"];
- tbText.alignment = NSTextAlignmentCenter;
- tbItem.view = tbText;
- tbItem.customizationLabel = self.touchbarItems[identifier][@"name"];
- [self.touchbarItems[identifier] setObject:tbText forKey:@"view"];
- [self.touchbarItems[identifier] setObject:tbItem forKey:@"tbItem"];
- [tbItem addObserver:self forKeyPath:@"visible" options:0 context:nil];
- return tbItem;
- }
-
- return nil;
-}
-
-- (void)observeValueForKeyPath:(NSString *)keyPath
- ofObject:(id)object
- change:(NSDictionary<NSKeyValueChangeKey,id> *)change
- context:(void *)context {
- if ([keyPath isEqualToString:@"visible"]) {
- NSNumber *visible = [object valueForKey:@"visible"];
- if (visible.boolValue) {
- [self updateTouchBarTimeItems];
- [self updatePlayButton];
- }
- }
-}
-
-- (void)updateTouchBarTimeItems
-{
- if (!self.isVisible)
- return;
-
- [self updateSlider];
- [self updateTimeLeft];
- [self updateCurrentPosition];
-}
-
-- (void)updateSlider
-{
- NSCustomTouchBarItem *tbItem = self.touchbarItems[seekBar][@"tbItem"];
- if (!tbItem.visible)
- return;
-
- NSSlider *seekSlider = self.touchbarItems[seekBar][@"view"];
-
- if (self.duration <= 0) {
- seekSlider.enabled = NO;
- seekSlider.doubleValue = 0;
- } else {
- seekSlider.enabled = YES;
- if (!seekSlider.highlighted)
- seekSlider.doubleValue = (self.position/self.duration)*100;
- }
-}
-
-- (void)updateTimeLeft
-{
- NSCustomTouchBarItem *tbItem = self.touchbarItems[timeLeft][@"tbItem"];
- if (!tbItem.visible)
- return;
-
- NSTextField *timeLeftItem = self.touchbarItems[timeLeft][@"view"];
-
- [self removeConstraintForIdentifier:timeLeft];
- if (self.duration <= 0) {
- timeLeftItem.stringValue = @"";
- } else {
- int left = (int)(floor(self.duration)-floor(self.position));
- NSString *leftFormat = [self formatTime:left];
- NSString *durFormat = [self formatTime:self.duration];
- timeLeftItem.stringValue = [NSString stringWithFormat:@"-%@", leftFormat];
- [self applyConstraintFromString:[NSString stringWithFormat:@"-%@", durFormat]
- forIdentifier:timeLeft];
- }
-}
-
-- (void)updateCurrentPosition
-{
- NSCustomTouchBarItem *tbItem = self.touchbarItems[currentPosition][@"tbItem"];
- if (!tbItem.visible)
- return;
-
- NSTextField *curPosItem = self.touchbarItems[currentPosition][@"view"];
- NSString *posFormat = [self formatTime:(int)floor(self.position)];
- curPosItem.stringValue = posFormat;
-
- [self removeConstraintForIdentifier:currentPosition];
- if (self.duration <= 0) {
- [self applyConstraintFromString:[self formatTime:self.position]
- forIdentifier:currentPosition];
- } else {
- NSString *durFormat = [self formatTime:self.duration];
- [self applyConstraintFromString:durFormat forIdentifier:currentPosition];
- }
-}
-
-- (void)updatePlayButton
-{
- NSCustomTouchBarItem *tbItem = self.touchbarItems[play][@"tbItem"];
- if (!self.isVisible || !tbItem.visible)
- return;
-
- NSButton *playButton = self.touchbarItems[play][@"view"];
- if (self.pause) {
- playButton.image = self.touchbarItems[play][@"imageAlt"];
- } else {
- playButton.image = self.touchbarItems[play][@"image"];
- }
-}
-
-- (void)buttonAction:(NSButton *)sender
-{
- NSString *identifier = [self getIdentifierFromView:sender];
- [self.app queueCommand:(char *)[self.touchbarItems[identifier][@"cmd"] UTF8String]];
-}
-
-- (void)seekbarChanged:(NSSlider *)slider
-{
- NSString *identifier = [self getIdentifierFromView:slider];
- NSString *seek = [NSString stringWithFormat:
- self.touchbarItems[identifier][@"cmd"], slider.doubleValue];
- [self.app queueCommand:(char *)[seek UTF8String]];
-}
-
-- (NSString *)formatTime:(int)time
-{
- int seconds = time % 60;
- int minutes = (time / 60) % 60;
- int hours = time / (60 * 60);
-
- NSString *stime = hours > 0 ? [NSString stringWithFormat:@"%d:", hours] : @"";
- stime = (stime.length > 0 || minutes > 9) ?
- [NSString stringWithFormat:@"%@%02d:", stime, minutes] :
- [NSString stringWithFormat:@"%d:", minutes];
- stime = [NSString stringWithFormat:@"%@%02d", stime, seconds];
-
- return stime;
-}
-
-- (void)removeConstraintForIdentifier:(NSTouchBarItemIdentifier)identifier
-{
- NSTextField *field = self.touchbarItems[identifier][@"view"];
- [field removeConstraint:self.touchbarItems[identifier][@"constrain"]];
-}
-
-- (void)applyConstraintFromString:(NSString *)string
- forIdentifier:(NSTouchBarItemIdentifier)identifier
-{
- NSTextField *field = self.touchbarItems[identifier][@"view"];
- if (field) {
- NSString *fString = [[string componentsSeparatedByCharactersInSet:
- [NSCharacterSet decimalDigitCharacterSet]] componentsJoinedByString:@"0"];
- NSTextField *textField = [NSTextField labelWithString:fString];
- NSSize size = [textField frame].size;
-
- NSLayoutConstraint *con =
- [NSLayoutConstraint constraintWithItem:field
- attribute:NSLayoutAttributeWidth
- relatedBy:NSLayoutRelationEqual
- toItem:nil
- attribute:NSLayoutAttributeNotAnAttribute
- multiplier:1.0
- constant:(int)ceil(size.width*1.1)];
- [field addConstraint:con];
- [self.touchbarItems[identifier] setObject:con forKey:@"constrain"];
- }
-}
-
-- (NSString *)getIdentifierFromView:(id)view
-{
- NSString *identifier;
- for (identifier in self.touchbarItems)
- if([self.touchbarItems[identifier][@"view"] isEqual:view])
- break;
- return identifier;
-}
-
-- (void)processEvent:(struct mpv_event *)event
-{
- switch (event->event_id) {
- case MPV_EVENT_END_FILE: {
- self.position = 0;
- self.duration = 0;
- break;
- }
- case MPV_EVENT_PROPERTY_CHANGE: {
- [self handlePropertyChange:(mpv_event_property *)event->data];
- break;
- }
- }
-}
-
-- (void)handlePropertyChange:(struct mpv_event_property *)property
-{
- NSString *name = [NSString stringWithUTF8String:property->name];
- mpv_format format = property->format;
-
- if ([name isEqualToString:@"time-pos"] && format == MPV_FORMAT_DOUBLE) {
- double newPosition = *(double *)property->data;
- newPosition = newPosition < 0 ? 0 : newPosition;
- if ((int)(floor(newPosition) - floor(self.position)) != 0) {
- self.position = newPosition;
- [self updateTouchBarTimeItems];
- }
- } else if ([name isEqualToString:@"duration"] && format == MPV_FORMAT_DOUBLE) {
- self.duration = *(double *)property->data;
- [self updateTouchBarTimeItems];
- } else if ([name isEqualToString:@"pause"] && format == MPV_FORMAT_FLAG) {
- self.pause = *(int *)property->data;
- [self updatePlayButton];
- }
-}
-
-@end
diff --git a/osdep/macosx_menubar.h b/video/out/win32/menu.h
index 509083d..8b1fe72 100644
--- a/osdep/macosx_menubar.h
+++ b/video/out/win32/menu.h
@@ -15,16 +15,18 @@
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef MPV_MACOSX_MENU
-#define MPV_MACOSX_MENU
+#ifndef MP_WIN32_MENU_H
+#define MP_WIN32_MENU_H
-// Menu Keys identifying menu items
-typedef enum {
- MPM_H_SIZE,
- MPM_N_SIZE,
- MPM_D_SIZE,
- MPM_MINIMIZE,
- MPM_ZOOM,
-} MPMenuKey;
+#include <windows.h>
-#endif /* MPV_MACOSX_MENU */
+struct mpv_node;
+struct menu_ctx;
+
+struct menu_ctx *mp_win32_menu_init(void);
+void mp_win32_menu_uninit(struct menu_ctx *ctx);
+void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd);
+void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data);
+const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id);
+
+#endif