diff options
author | der richter <der.richter@gmx.de> | 2024-02-27 16:55:29 +0100 |
---|---|---|
committer | der richter <der.richter@gmx.de> | 2024-02-28 15:52:47 +0100 |
commit | 86fa9b18a3619a379a597ca0902c23dc053cafc0 (patch) | |
tree | f187f85a97744bd0468290975e5ec4d10054b25b /osdep/mac/remote_command_center.swift | |
parent | 661f45377a17635125ae36f0b8a3487ae13cf606 (diff) | |
download | mpv-86fa9b18a3619a379a597ca0902c23dc053cafc0.tar.bz2 mpv-86fa9b18a3619a379a597ca0902c23dc053cafc0.tar.xz |
osdep/mac: make mac naming of files, folders and function consistent
rename all macOS namings (osx, macosx, macOS, macos, apple) to mac, to
make naming consistent.
Diffstat (limited to 'osdep/mac/remote_command_center.swift')
-rw-r--r-- | osdep/mac/remote_command_center.swift | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/osdep/mac/remote_command_center.swift b/osdep/mac/remote_command_center.swift new file mode 100644 index 0000000000..f6613a1f9a --- /dev/null +++ b/osdep/mac/remote_command_center.swift @@ -0,0 +1,214 @@ +/* + * 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 + +extension RemoteCommandCenter { + typealias ConfigHandler = (MPRemoteCommandEvent) -> (MPRemoteCommandHandlerStatus) + + enum KeyType { + case normal + case repeatable + } + + struct Config { + let key: Int32 + let type: KeyType + var state: UInt32 = 0 + 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: NSObject { + 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 = 0 { 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 = NSImage(size: NSSize(width: 256, height: 256)) + + var infoCenter: MPNowPlayingInfoCenter { get { return MPNowPlayingInfoCenter.default() } } + var commandCenter: MPRemoteCommandCenter { get { return MPRemoteCommandCenter.shared() } } + + @objc override init() { + super.init() + + 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, + ] + + cover = (NSApp as? Application)?.getMPVIcon() ?? cover + + for cmd in disabledCommands { + cmd.isEnabled = false + } + } + + @objc 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 + ) + } + + @objc 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 == MP_KEY_STATE_DOWN ? MP_KEY_STATE_UP : MP_KEY_STATE_DOWN + self.configs[event.command]?.state = state + } + + EventsResponder.sharedInstance().handleMPKey(config.key, withMask: Int32(state)) + + return .success + } + + lazy var seekHandler: ConfigHandler = { event in + guard let posEvent = event as? MPChangePlaybackPositionCommandEvent else { + return .commandFailed + } + + let success = String(format: "seek %.02f absolute", posEvent.positionTime).withCString { + EventsResponder.sharedInstance().queueCommand(UnsafeMutablePointer<Int8>(mutating: $0)) + } + + return success ? .success : .commandFailed + } + + @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 + case "time-pos" where property.format == MPV_FORMAT_DOUBLE: + let newPosition = max(LibmpvHelper.mpvDoubleToDouble(property.data) ?? 0, 0) + if Int((floor(newPosition) - floor(position)) / rate) != 0 { + position = newPosition + } + case "duration" where property.format == MPV_FORMAT_DOUBLE: + duration = LibmpvHelper.mpvDoubleToDouble(property.data) ?? 0 + case "speed" where property.format == MPV_FORMAT_DOUBLE: + rate = LibmpvHelper.mpvDoubleToDouble(property.data) ?? 1 + case "media-title" where [MPV_FORMAT_STRING, MPV_FORMAT_NONE].contains(property.format): + title = LibmpvHelper.mpvStringArrayToString(property.data) ?? "" + case "chapter-metadata/title" where [MPV_FORMAT_STRING, MPV_FORMAT_NONE].contains(property.format): + chapter = LibmpvHelper.mpvStringArrayToString(property.data) + case "metadata/by-key/album" where [MPV_FORMAT_STRING, MPV_FORMAT_NONE].contains(property.format): + album = LibmpvHelper.mpvStringArrayToString(property.data) + case "metadata/by-key/artist" where [MPV_FORMAT_STRING, MPV_FORMAT_NONE].contains(property.format): + artist = LibmpvHelper.mpvStringArrayToString(property.data) + default: + break + } + } +} |