You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
59 lines
2.0 KiB
59 lines
2.0 KiB
import AppKit
|
|
import AVFoundation
|
|
|
|
/// A borderless window covering one screen, above the menu bar, that shows a video layer.
|
|
@MainActor
|
|
public final class OutputWindow: NSWindow {
|
|
public let sink: VideoSink
|
|
/// Called when the operator presses Esc on the output.
|
|
public var onCancel: (() -> Void)?
|
|
|
|
public init(screen: NSScreen) {
|
|
let view = VideoView()
|
|
sink = VideoSink(renderer: view.displayLayer.sampleBufferRenderer)
|
|
super.init(contentRect: screen.frame, styleMask: .borderless, backing: .buffered, defer: false)
|
|
|
|
contentView = view
|
|
setFrame(screen.frame, display: false)
|
|
level = .screenSaver
|
|
collectionBehavior = [.canJoinAllSpaces, .stationary, .ignoresCycle, .fullScreenNone]
|
|
backgroundColor = .black
|
|
isOpaque = true
|
|
hasShadow = false
|
|
isReleasedWhenClosed = false
|
|
animationBehavior = .none
|
|
}
|
|
|
|
public override var canBecomeKey: Bool { true }
|
|
|
|
public override func cancelOperation(_ sender: Any?) {
|
|
onCancel?()
|
|
}
|
|
}
|
|
|
|
/// Layer-hosting view backed by `AVSampleBufferDisplayLayer`; hides the cursor while it is inside.
|
|
@MainActor
|
|
final class VideoView: NSView {
|
|
let displayLayer = AVSampleBufferDisplayLayer()
|
|
|
|
init() {
|
|
super.init(frame: .zero)
|
|
displayLayer.videoGravity = .resizeAspect
|
|
displayLayer.backgroundColor = NSColor.black.cgColor
|
|
layer = displayLayer
|
|
wantsLayer = true
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) { fatalError() }
|
|
|
|
override func updateTrackingAreas() {
|
|
super.updateTrackingAreas()
|
|
trackingAreas.forEach(removeTrackingArea)
|
|
addTrackingArea(NSTrackingArea(rect: .zero, options: [.mouseEnteredAndExited, .mouseMoved, .activeAlways, .inVisibleRect], owner: self))
|
|
}
|
|
|
|
override func mouseEntered(with event: NSEvent) { NSCursor.setHiddenUntilMouseMoves(true) }
|
|
override func mouseMoved(with event: NSEvent) { NSCursor.setHiddenUntilMouseMoves(true) }
|
|
}
|