summaryrefslogtreecommitdiffstats
path: root/video/out/cocoa-cb/window.swift
diff options
context:
space:
mode:
authorder richter <der.richter@gmx.de>2020-06-27 11:12:46 +0200
committerder richter <der.richter@gmx.de>2020-08-22 14:22:49 +0200
commitf79a591ae471c4da6fbb40c1da4bc4220f90aba4 (patch)
treedd1825ff6fe72a3fa33d335eff047f5de7f4f683 /video/out/cocoa-cb/window.swift
parentb16a6cb6c62657d3efbe69bd854287da149b59a1 (diff)
downloadmpv-f79a591ae471c4da6fbb40c1da4bc4220f90aba4.tar.bz2
mpv-f79a591ae471c4da6fbb40c1da4bc4220f90aba4.tar.xz
cocoa-cb: generalisation of backend independent parts
move all backend independent code parts in their own folder and files, to simplify adding new backends. the goal is to only extend one class and add the backend dependent parts there. usually only the (un)init, config and related parts need to be implemented per backend. furthermore all needed windowing and related events are propagated and can be overwritten. the other backend dependent part is usually the surface for rendering, for example the opengl oder metal layer. in the best case a new backend can be added with only a few hundred lines.
Diffstat (limited to 'video/out/cocoa-cb/window.swift')
-rw-r--r--video/out/cocoa-cb/window.swift543
1 files changed, 0 insertions, 543 deletions
diff --git a/video/out/cocoa-cb/window.swift b/video/out/cocoa-cb/window.swift
deleted file mode 100644
index ae1c7cf276..0000000000
--- a/video/out/cocoa-cb/window.swift
+++ /dev/null
@@ -1,543 +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 Window: NSWindow, NSWindowDelegate {
-
- weak var cocoaCB: CocoaCB! = nil
- var mpv: MPVHelper? { get { return cocoaCB.mpv } }
- var libmpv: LibmpvHelper { get { return cocoaCB.libmpv } }
-
- var targetScreen: NSScreen?
- var previousScreen: NSScreen?
- var currentScreen: NSScreen?
- var unfScreen: NSScreen?
-
- var unfsContentFrame: NSRect?
- var isInFullscreen: Bool = false
- var isAnimating: Bool = false
- var isMoving: Bool = false
- var forceTargetScreen: Bool = false
-
- var keepAspect: Bool = true {
- didSet {
- if let contentViewFrame = contentView?.frame, !isInFullscreen {
- unfsContentFrame = convertToScreen(contentViewFrame)
- }
-
- if keepAspect {
- contentAspectRatio = unfsContentFrame?.size ?? contentAspectRatio
- } else {
- resizeIncrements = NSSize(width: 1.0, height: 1.0)
- }
- }
- }
-
- var border: Bool = true {
- didSet { if !border { cocoaCB.titleBar?.hide() } }
- }
-
- override var canBecomeKey: Bool { return true }
- override var canBecomeMain: Bool { return true }
-
- override var styleMask: NSWindow.StyleMask {
- get { return super.styleMask }
- set {
- let responder = firstResponder
- let windowTitle = title
- super.styleMask = newValue
- makeFirstResponder(responder)
- title = windowTitle
- }
- }
-
- convenience init(contentRect: NSRect, screen: NSScreen?, view: NSView, cocoaCB ccb: CocoaCB) {
- self.init(contentRect: contentRect,
- styleMask: [.titled, .closable, .miniaturizable, .resizable],
- backing: .buffered, defer: false, screen: screen)
-
- // workaround for an AppKit bug where the NSWindow can't be placed on a
- // none Main screen NSScreen outside the Main screen's frame bounds
- if let wantedScreen = screen, screen != NSScreen.main {
- var absoluteWantedOrigin = contentRect.origin
- absoluteWantedOrigin.x += wantedScreen.frame.origin.x
- absoluteWantedOrigin.y += wantedScreen.frame.origin.y
-
- if !NSEqualPoints(absoluteWantedOrigin, self.frame.origin) {
- self.setFrameOrigin(absoluteWantedOrigin)
- }
- }
-
- cocoaCB = ccb
- title = cocoaCB.title
- minSize = NSMakeSize(160, 90)
- collectionBehavior = .fullScreenPrimary
- delegate = self
-
- if let cView = contentView {
- cView.addSubview(view)
- view.frame = cView.frame
- unfsContentFrame = convertToScreen(cView.frame)
- }
-
- targetScreen = screen
- currentScreen = screen
- unfScreen = screen
-
- if let app = NSApp as? Application {
- app.menuBar.register(#selector(setHalfWindowSize), for: MPM_H_SIZE)
- app.menuBar.register(#selector(setNormalWindowSize), for: MPM_N_SIZE)
- app.menuBar.register(#selector(setDoubleWindowSize), for: MPM_D_SIZE)
- app.menuBar.register(#selector(performMiniaturize(_:)), for: MPM_MINIMIZE)
- app.menuBar.register(#selector(performZoom(_:)), for: MPM_ZOOM)
- }
- }
-
- override func toggleFullScreen(_ sender: Any?) {
- if isAnimating {
- return
- }
-
- isAnimating = true
-
- targetScreen = cocoaCB.getTargetScreen(forFullscreen: !isInFullscreen)
- if targetScreen == nil && previousScreen == nil {
- targetScreen = screen
- } else if targetScreen == nil {
- targetScreen = previousScreen
- previousScreen = nil
- } else {
- previousScreen = screen
- }
-
- if let contentViewFrame = contentView?.frame, !isInFullscreen {
- unfsContentFrame = convertToScreen(contentViewFrame)
- unfScreen = screen
- }
- // move window to target screen when going to fullscreen
- if let tScreen = targetScreen, !isInFullscreen && (tScreen != screen) {
- let frame = calculateWindowPosition(for: tScreen, withoutBounds: false)
- setFrame(frame, display: true)
- }
-
- if Bool(mpv?.opts.native_fs ?? 1) {
- super.toggleFullScreen(sender)
- } else {
- if !isInFullscreen {
- setToFullScreen()
- }
- else {
- setToWindow()
- }
- }
- }
-
- func customWindowsToEnterFullScreen(for window: NSWindow) -> [NSWindow]? {
- return [window]
- }
-
- func customWindowsToExitFullScreen(for window: NSWindow) -> [NSWindow]? {
- return [window]
- }
-
- func window(_ window: NSWindow, startCustomAnimationToEnterFullScreenWithDuration duration: TimeInterval) {
- guard let tScreen = targetScreen else { return }
- cocoaCB.view?.layerContentsPlacement = .scaleProportionallyToFit
- cocoaCB.titleBar?.hide()
- NSAnimationContext.runAnimationGroup({ (context) -> Void in
- context.duration = getFsAnimationDuration(duration - 0.05)
- window.animator().setFrame(tScreen.frame, display: true)
- }, completionHandler: { })
- }
-
- func window(_ window: NSWindow, startCustomAnimationToExitFullScreenWithDuration duration: TimeInterval) {
- guard let tScreen = targetScreen, let currentScreen = screen else { return }
- let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: tScreen == screen)
- let intermediateFrame = aspectFit(rect: newFrame, in: currentScreen.frame)
- cocoaCB.view?.layerContentsPlacement = .scaleProportionallyToFill
- cocoaCB.titleBar?.hide()
- styleMask.remove(.fullScreen)
- setFrame(intermediateFrame, display: true)
-
- NSAnimationContext.runAnimationGroup({ (context) -> Void in
- context.duration = getFsAnimationDuration(duration - 0.05)
- window.animator().setFrame(newFrame, display: true)
- }, completionHandler: { })
- }
-
- func windowDidEnterFullScreen(_ notification: Notification) {
- isInFullscreen = true
- cocoaCB.mpv?.setConfigProperty(fullscreen: isInFullscreen)
- cocoaCB.updateCursorVisibility()
- endAnimation(frame)
- cocoaCB.titleBar?.show()
- }
-
- func windowDidExitFullScreen(_ notification: Notification) {
- guard let tScreen = targetScreen else { return }
- isInFullscreen = false
- cocoaCB.mpv?.setConfigProperty(fullscreen: isInFullscreen)
- endAnimation(calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen))
- cocoaCB.view?.layerContentsPlacement = .scaleProportionallyToFit
- }
-
- func windowDidFailToEnterFullScreen(_ window: NSWindow) {
- guard let tScreen = targetScreen else { return }
- let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen)
- setFrame(newFrame, display: true)
- endAnimation()
- }
-
- func windowDidFailToExitFullScreen(_ window: NSWindow) {
- guard let targetFrame = targetScreen?.frame else { return }
- setFrame(targetFrame, display: true)
- endAnimation()
- cocoaCB.view?.layerContentsPlacement = .scaleProportionallyToFit
- }
-
- func endAnimation(_ newFrame: NSRect = NSZeroRect) {
- if !NSEqualRects(newFrame, NSZeroRect) && isAnimating {
- NSAnimationContext.runAnimationGroup({ (context) -> Void in
- context.duration = 0.01
- self.animator().setFrame(newFrame, display: true)
- }, completionHandler: nil )
- }
-
- isAnimating = false
- cocoaCB.layer?.update()
- cocoaCB.checkShutdown()
- }
-
- func setToFullScreen() {
- guard let targetFrame = targetScreen?.frame else { return }
- styleMask.insert(.fullScreen)
- NSApp.presentationOptions = [.autoHideMenuBar, .autoHideDock]
- setFrame(targetFrame, display: true)
- endAnimation()
- isInFullscreen = true
- cocoaCB.mpv?.setConfigProperty(fullscreen: isInFullscreen)
- cocoaCB.layer?.update()
- }
-
- func setToWindow() {
- guard let tScreen = targetScreen else { return }
- let newFrame = calculateWindowPosition(for: tScreen, withoutBounds: targetScreen == screen)
- NSApp.presentationOptions = []
- setFrame(newFrame, display: true)
- styleMask.remove(.fullScreen)
- endAnimation()
- isInFullscreen = false
- cocoaCB.mpv?.setConfigProperty(fullscreen: isInFullscreen)
- cocoaCB.layer?.update()
- }
-
- func getFsAnimationDuration(_ def: Double) -> Double {
- let duration = libmpv.macOpts.macos_fs_animation_duration
- if duration < 0 {
- return def
- } else {
- return Double(duration)/1000
- }
- }
-
- func setOnTop(_ state: Bool, _ ontopLevel: Int) {
- if state {
- switch ontopLevel {
- case -1:
- level = .floating
- case -2:
- level = .statusBar + 1
- default:
- level = NSWindow.Level(ontopLevel)
- }
- collectionBehavior.remove(.transient)
- collectionBehavior.insert(.managed)
- } else {
- level = .normal
- }
- }
-
- func setMinimized(_ stateWanted: Bool) {
- if isMiniaturized == stateWanted { return }
-
- if stateWanted {
- performMiniaturize(self)
- } else {
- deminiaturize(self)
- }
- }
-
- func setMaximized(_ stateWanted: Bool) {
- if isZoomed == stateWanted { return }
-
- zoom(self)
- }
-
- func updateMovableBackground(_ pos: NSPoint) {
- if !isInFullscreen {
- isMovableByWindowBackground = mpv?.canBeDraggedAt(pos) ?? true
- } else {
- isMovableByWindowBackground = false
- }
- }
-
- func updateFrame(_ rect: NSRect) {
- if rect != frame {
- let cRect = frameRect(forContentRect: rect)
- unfsContentFrame = rect
- setFrame(cRect, display: true)
- cocoaCB.layer?.update(force: true)
- }
- }
-
- func updateSize(_ size: NSSize) {
- if let currentSize = contentView?.frame.size, size != currentSize {
- let newContentFrame = centeredContentSize(for: frame, size: size)
- if !isInFullscreen {
- updateFrame(newContentFrame)
- } else {
- unfsContentFrame = newContentFrame
- }
- }
- }
-
- override func setFrame(_ frameRect: NSRect, display flag: Bool) {
- if frameRect.width < minSize.width || frameRect.height < minSize.height {
- mpv?.sendVerbose("tried to set too small window size: \(frameRect.size)")
- return
- }
-
- super.setFrame(frameRect, display: flag)
-
- if let size = unfsContentFrame?.size, keepAspect {
- contentAspectRatio = size
- }
- }
-
- func centeredContentSize(for rect: NSRect, size sz: NSSize) -> NSRect {
- let cRect = contentRect(forFrameRect: rect)
- let dx = (cRect.size.width - sz.width) / 2
- let dy = (cRect.size.height - sz.height) / 2
- return NSInsetRect(cRect, dx, dy)
- }
-
- func aspectFit(rect r: NSRect, in rTarget: NSRect) -> NSRect {
- var s = rTarget.width / r.width
- if r.height*s > rTarget.height {
- s = rTarget.height / r.height
- }
- let w = r.width * s
- let h = r.height * s
- return NSRect(x: rTarget.midX - w/2, y: rTarget.midY - h/2, width: w, height: h)
- }
-
- func calculateWindowPosition(for tScreen: NSScreen, withoutBounds: Bool) -> NSRect {
- guard let contentFrame = unfsContentFrame, let screen = unfScreen else {
- return frame
- }
- var newFrame = frameRect(forContentRect: contentFrame)
- let targetFrame = tScreen.frame
- let targetVisibleFrame = tScreen.visibleFrame
- let unfsScreenFrame = screen.frame
- let visibleWindow = NSIntersectionRect(unfsScreenFrame, newFrame)
-
- // calculate visible area of every side
- let left = newFrame.origin.x - unfsScreenFrame.origin.x
- let right = unfsScreenFrame.size.width -
- (newFrame.origin.x - unfsScreenFrame.origin.x + newFrame.size.width)
- let bottom = newFrame.origin.y - unfsScreenFrame.origin.y
- let top = unfsScreenFrame.size.height -
- (newFrame.origin.y - unfsScreenFrame.origin.y + newFrame.size.height)
-
- // normalize visible areas, decide which one to take horizontal/vertical
- var xPer = (unfsScreenFrame.size.width - visibleWindow.size.width)
- var yPer = (unfsScreenFrame.size.height - visibleWindow.size.height)
- if xPer != 0 { xPer = (left >= 0 || right < 0 ? left : right) / xPer }
- if yPer != 0 { yPer = (bottom >= 0 || top < 0 ? bottom : top) / yPer }
-
- // calculate visible area for every side for target screen
- let xNewLeft = targetFrame.origin.x +
- (targetFrame.size.width - visibleWindow.size.width) * xPer
- let xNewRight = targetFrame.origin.x + targetFrame.size.width -
- (targetFrame.size.width - visibleWindow.size.width) * xPer - newFrame.size.width
- let yNewBottom = targetFrame.origin.y +
- (targetFrame.size.height - visibleWindow.size.height) * yPer
- let yNewTop = targetFrame.origin.y + targetFrame.size.height -
- (targetFrame.size.height - visibleWindow.size.height) * yPer - newFrame.size.height
-
- // calculate new coordinates, decide which one to take horizontal/vertical
- newFrame.origin.x = left >= 0 || right < 0 ? xNewLeft : xNewRight
- newFrame.origin.y = bottom >= 0 || top < 0 ? yNewBottom : yNewTop
-
- // don't place new window on top of a visible menubar
- let topMar = targetFrame.size.height -
- (newFrame.origin.y - targetFrame.origin.y + newFrame.size.height)
- let menuBarHeight = targetFrame.size.height -
- (targetVisibleFrame.size.height + targetVisibleFrame.origin.y)
- if topMar < menuBarHeight {
- newFrame.origin.y -= top - menuBarHeight
- }
-
- if withoutBounds {
- return newFrame
- }
-
- // screen bounds right and left
- if newFrame.origin.x + newFrame.size.width > targetFrame.origin.x + targetFrame.size.width {
- newFrame.origin.x = targetFrame.origin.x + targetFrame.size.width - newFrame.size.width
- }
- if newFrame.origin.x < targetFrame.origin.x {
- newFrame.origin.x = targetFrame.origin.x
- }
-
- // screen bounds top and bottom
- if newFrame.origin.y + newFrame.size.height > targetFrame.origin.y + targetFrame.size.height {
- newFrame.origin.y = targetFrame.origin.y + targetFrame.size.height - newFrame.size.height
- }
- if newFrame.origin.y < targetFrame.origin.y {
- newFrame.origin.y = targetFrame.origin.y
- }
- return newFrame
- }
-
- override func constrainFrameRect(_ frameRect: NSRect, to tScreen: NSScreen?) -> NSRect {
- if (isAnimating && !isInFullscreen) || (!isAnimating && isInFullscreen) {
- return frameRect
- }
-
- guard let ts: NSScreen = tScreen ?? screen ?? NSScreen.main else {
- return frameRect
- }
- var nf: NSRect = frameRect
- let of: NSRect = frame
- let vf: NSRect = (isAnimating ? (targetScreen ?? ts) : ts).visibleFrame
- let ncf: NSRect = contentRect(forFrameRect: nf)
-
- // screen bounds top and bottom
- if NSMaxY(nf) > NSMaxY(vf) {
- nf.origin.y = NSMaxY(vf) - NSHeight(nf)
- }
- if NSMaxY(ncf) < NSMinY(vf) {
- nf.origin.y = NSMinY(vf) + NSMinY(ncf) - NSMaxY(ncf)
- }
-
- // screen bounds right and left
- if NSMinX(nf) > NSMaxX(vf) {
- nf.origin.x = NSMaxX(vf) - NSWidth(nf)
- }
- if NSMaxX(nf) < NSMinX(vf) {
- nf.origin.x = NSMinX(vf)
- }
-
- if NSHeight(nf) < NSHeight(vf) && NSHeight(of) > NSHeight(vf) && !isInFullscreen {
- // If the window height is smaller than the visible frame, but it was
- // bigger previously recenter the smaller window vertically. This is
- // needed to counter the 'snap to top' behaviour.
- nf.origin.y = (NSHeight(vf) - NSHeight(nf)) / 2
- }
- return nf
- }
-
- @objc func setNormalWindowSize() { setWindowScale(1.0) }
- @objc func setHalfWindowSize() { setWindowScale(0.5) }
- @objc func setDoubleWindowSize() { setWindowScale(2.0) }
-
- func setWindowScale(_ scale: Double) {
- mpv?.command("set window-scale \(scale)")
- }
-
- func addWindowScale(_ scale: Double) {
- if !isInFullscreen {
- mpv?.command("add window-scale \(scale)")
- }
- }
-
- func windowDidChangeScreen(_ notification: Notification) {
- if screen == nil {
- return
- }
- if !isAnimating && (currentScreen != screen) {
- previousScreen = screen
- }
- if currentScreen != screen {
- cocoaCB.updateDisplaylink()
- cocoaCB.layer?.update(force: true)
- }
- currentScreen = screen
- }
-
- func windowDidChangeScreenProfile(_ notification: Notification) {
- cocoaCB.layer?.needsICCUpdate = true
- }
-
- func windowDidChangeBackingProperties(_ notification: Notification) {
- cocoaCB.layer?.contentsScale = backingScaleFactor
- cocoaCB.flagEvents(VO_EVENT_DPI)
- }
-
- func windowWillStartLiveResize(_ notification: Notification) {
- cocoaCB.layer?.inLiveResize = true
- }
-
- func windowDidEndLiveResize(_ notification: Notification) {
- cocoaCB.layer?.inLiveResize = false
- cocoaCB.mpv?.setConfigProperty(maximized: isZoomed)
-
- if let contentViewFrame = contentView?.frame,
- !isAnimating && !isInFullscreen
- {
- unfsContentFrame = convertToScreen(contentViewFrame)
- }
- }
-
- func windowShouldClose(_ sender: NSWindow) -> Bool {
- cocoa_put_key(MP_KEY_CLOSE_WIN)
- return false
- }
-
- func windowDidMiniaturize(_ notification: Notification) {
- cocoaCB.mpv?.setConfigProperty(minimized: true)
- }
-
- func windowDidDeminiaturize(_ notification: Notification) {
- cocoaCB.mpv?.setConfigProperty(minimized: false)
- }
-
- func windowDidResignKey(_ notification: Notification) {
- cocoaCB.setCursorVisiblility(true)
- }
-
- func windowDidBecomeKey(_ notification: Notification) {
- cocoaCB.updateCursorVisibility()
- }
-
- func windowDidChangeOcclusionState(_ notification: Notification) {
- if occlusionState.contains(.visible) {
- cocoaCB.layer?.update(force: true)
- cocoaCB.updateCursorVisibility()
- }
- }
-
- func windowWillMove(_ notification: Notification) {
- isMoving = true
- }
-
- func windowDidMove(_ notification: Notification) {
- cocoaCB.mpv?.setConfigProperty(maximized: isZoomed)
- }
-}