0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00
mpv/video/out/mac/view.swift
der richter 8a61929eb8 cocoa-cb: add support for macOS color space transformation (EDR/HDR)
by default utilises the color space of the screen on which the window is
located. if a specific value is defined, it will instead be utilised.

depending on the chosen color space the macOS EDR (HDR) support is
activated and that OS's transformation (tone mapping) is used.

Fixes #7341
2024-05-05 19:02:50 +02:00

219 lines
7.1 KiB
Swift

/*
* 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 View: NSView, CALayerDelegate {
unowned var common: Common
var input: InputHelper? { return common.input }
var tracker: NSTrackingArea?
var hasMouseDown: Bool = false
override var isFlipped: Bool { return true }
override var acceptsFirstResponder: Bool { return true }
init(frame: NSRect, common com: Common) {
common = com
super.init(frame: frame)
autoresizingMask = [.width, .height]
wantsBestResolutionOpenGLSurface = true
wantsExtendedDynamicRangeOpenGLSurface = true
registerForDraggedTypes([ .fileURL, .URL, .string ])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func updateTrackingAreas() {
if let tracker = self.tracker {
removeTrackingArea(tracker)
}
tracker = NSTrackingArea(rect: bounds,
options: [.activeAlways, .mouseEnteredAndExited, .mouseMoved, .enabledDuringMouseDrag],
owner: self, userInfo: nil)
// here tracker is guaranteed to be none-nil
addTrackingArea(tracker!)
if containsMouseLocation() {
input?.put(key: SWIFT_KEY_MOUSE_LEAVE)
}
}
override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation {
guard let types = sender.draggingPasteboard.types else { return [] }
if types.contains(.fileURL) || types.contains(.URL) || types.contains(.string) {
return .copy
}
return []
}
func isURL(_ str: String) -> Bool {
guard let regex = try? NSRegularExpression(pattern: "^(https?|ftp)://[^\\s/$.?#].[^\\s]*$",
options: .caseInsensitive) else {
return false
}
let isURL = regex.numberOfMatches(in: str,
options: [],
range: NSRange(location: 0, length: str.count))
return isURL > 0
}
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
let pb = sender.draggingPasteboard
guard let types = pb.types else { return false }
var files: [String] = []
if types.contains(.fileURL) || types.contains(.URL) {
guard let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL] else { return false }
files = urls.map { $0.absoluteString }
} else if types.contains(.string) {
guard let str = pb.string(forType: .string) else { return false }
files = str.components(separatedBy: "\n").compactMap {
let url = $0.trimmingCharacters(in: .whitespacesAndNewlines)
let path = (url as NSString).expandingTildeInPath
if isURL(url) { return url }
if path.starts(with: "/") { return path }
return nil
}
}
if files.isEmpty { return false }
input?.open(files: files)
return true
}
override func acceptsFirstMouse(for event: NSEvent?) -> Bool {
return true
}
override func becomeFirstResponder() -> Bool {
return true
}
override func resignFirstResponder() -> Bool {
return true
}
override func mouseEntered(with event: NSEvent) {
if input?.mouseEnabled() ?? true {
input?.put(key: SWIFT_KEY_MOUSE_ENTER)
}
common.updateCursorVisibility()
}
override func mouseExited(with event: NSEvent) {
if input?.mouseEnabled() ?? true {
input?.put(key: SWIFT_KEY_MOUSE_LEAVE)
}
common.titleBar?.hide()
common.setCursorVisibility(true)
}
override func mouseMoved(with event: NSEvent) {
signalMouseMovement(event)
common.titleBar?.show()
}
override func mouseDragged(with event: NSEvent) {
signalMouseMovement(event)
}
override func mouseDown(with event: NSEvent) {
hasMouseDown = event.clickCount <= 1
input?.processMouse(event: event)
}
override func mouseUp(with event: NSEvent) {
hasMouseDown = false
common.window?.isMoving = false
input?.processMouse(event: event)
}
override func rightMouseDown(with event: NSEvent) {
hasMouseDown = event.clickCount <= 1
input?.processMouse(event: event)
}
override func rightMouseUp(with event: NSEvent) {
hasMouseDown = false
input?.processMouse(event: event)
}
override func otherMouseDown(with event: NSEvent) {
hasMouseDown = event.clickCount <= 1
input?.processMouse(event: event)
}
override func otherMouseUp(with event: NSEvent) {
hasMouseDown = false
input?.processMouse(event: event)
}
override func magnify(with event: NSEvent) {
common.window?.isAnimating = event.phase != .ended
event.phase == .ended ? common.windowDidEndLiveResize() : common.windowWillStartLiveResize()
common.window?.addWindowScale(Double(event.magnification))
}
func signalMouseMovement(_ event: NSEvent) {
var point = convert(event.locationInWindow, from: nil)
point = convertToBacking(point)
point.y = -point.y
common.window?.updateMovableBackground(point)
if !(common.window?.isMoving ?? false) {
input?.setMouse(position: point)
}
}
override func scrollWheel(with event: NSEvent) {
input?.processWheel(event: event)
}
func containsMouseLocation() -> Bool {
var topMargin: CGFloat = 0.0
let menuBarHeight = NSApp.mainMenu?.menuBarHeight ?? 23.0
guard let window = common.window else { return false }
guard var vF = window.screen?.frame else { return false }
if window.isInFullscreen && (menuBarHeight > 0) {
topMargin = TitleBar.height + 1 + menuBarHeight
}
vF.size.height -= topMargin
let vFW = window.convertFromScreen(vF)
let vFV = convert(vFW, from: nil)
let pt = convert(window.mouseLocationOutsideOfEventStream, from: nil)
var clippedBounds = bounds.intersection(vFV)
if !window.isInFullscreen {
clippedBounds.origin.y += TitleBar.height
clippedBounds.size.height -= TitleBar.height
}
return clippedBounds.contains(pt)
}
func canHideCursor() -> Bool {
guard let window = common.window else { return false }
return !hasMouseDown && containsMouseLocation() && window.isKeyWindow
}
}