/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see .
*/
import Cocoa
class MacCommon: Common {
@objc var layer: MetalLayer?
var presentation: Presentation?
var timer: PreciseTimer?
var swapTime: UInt64 = 0
let swapLock: NSCondition = NSCondition()
@objc init(_ vo: UnsafeMutablePointer) {
let log = LogHelper(mp_log_new(vo, vo.pointee.log, "mac"))
let option = OptionHelper(vo, vo.pointee.global)
super.init(option, log)
eventsLock.withLock { self.vo = vo }
input = InputHelper(vo.pointee.input_ctx, option)
presentation = Presentation(common: self)
timer = PreciseTimer(common: self)
DispatchQueue.main.sync {
layer = MetalLayer(common: self)
initMisc(vo)
}
}
@objc func config(_ vo: UnsafeMutablePointer) -> Bool {
eventsLock.withLock { self.vo = vo }
DispatchQueue.main.sync {
let previousActiveApp = getActiveApp()
initApp()
let (_, wr, _) = getInitProperties(vo)
guard let layer = self.layer else {
log.error("Something went wrong, no MetalLayer was initialized")
exit(1)
}
if window == nil {
initView(vo, layer)
initWindow(vo, previousActiveApp)
initWindowState()
}
if option.vo.auto_window_resize {
window?.updateSize(wr.size)
}
if option.vo.focus_on == 2 {
NSApp.activate(ignoringOtherApps: true)
}
windowDidResize()
updateICCProfile()
}
return true
}
@objc func uninit(_ vo: UnsafeMutablePointer) {
window?.waitForAnimation()
timer?.terminate()
DispatchQueue.main.sync {
window?.delegate = nil
window?.close()
uninitCommon()
}
}
@objc func swapBuffer() {
if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
swapLock.lock()
while swapTime < 1 {
swapLock.wait()
}
swapTime = 0
swapLock.unlock()
}
}
@objc func fillVsync(info: UnsafeMutablePointer) {
if option.mac.macos_render_timer != RENDER_TIMER_PRESENTATION_FEEDBACK { return }
let next = presentation?.next()
info.pointee.vsync_duration = next?.duration ?? -1
info.pointee.skipped_vsyncs = next?.skipped ?? -1
info.pointee.last_queue_display_time = next?.time ?? -1
}
override func displayLinkCallback(_ displayLink: CVDisplayLink,
_ inNow: UnsafePointer,
_ inOutputTime: UnsafePointer,
_ flagsIn: CVOptionFlags,
_ flagsOut: UnsafeMutablePointer) -> CVReturn {
let signalSwap = {
self.swapLock.lock()
self.swapTime += 1
self.swapLock.signal()
self.swapLock.unlock()
}
if option.mac.macos_render_timer > RENDER_TIMER_SYSTEM {
if let timer = self.timer, option.mac.macos_render_timer == RENDER_TIMER_PRECISE {
timer.scheduleAt(time: inOutputTime.pointee.hostTime, closure: signalSwap)
return kCVReturnSuccess
}
signalSwap()
return kCVReturnSuccess
}
if option.mac.macos_render_timer == RENDER_TIMER_PRESENTATION_FEEDBACK {
presentation?.add(time: inOutputTime.pointee)
}
return kCVReturnSuccess
}
override func startDisplayLink(_ vo: UnsafeMutablePointer) {
super.startDisplayLink(vo)
timer?.updatePolicy(periodSeconds: 1 / currentFps())
}
override func updateDisplaylink() {
super.updateDisplaylink()
timer?.updatePolicy(periodSeconds: 1 / currentFps())
}
override func lightSensorUpdate() {
flagEvents(VO_EVENT_AMBIENT_LIGHTING_CHANGED)
}
override func updateICCProfile() {
flagEvents(VO_EVENT_ICC_PROFILE_CHANGED)
}
override func windowDidResize() {
flagEvents(VO_EVENT_RESIZE | VO_EVENT_EXPOSE)
}
override func windowDidChangeScreenProfile() {
updateICCProfile()
}
override func windowDidChangeBackingProperties() {
layer?.contentsScale = window?.backingScaleFactor ?? 1
windowDidResize()
}
}