Files
luma/Luma/VirtualMachines/VirtualMachineFullScreen.swift

80 lines
2.5 KiB
Swift

import LumaCore
import SwiftUI
#if canImport(AppKit)
import AppKit
/// A guest shown on its own, filling the screen. The panel keeps its row, and
/// gets the display back when the window goes away.
@MainActor
enum VirtualMachineFullScreen {
static func show(_ display: VirtualMachineDisplay, named name: String) {
let window = NSWindow(
contentRect: NSScreen.main?.frame ?? NSRect(x: 0, y: 0, width: 1280, height: 800),
styleMask: [.titled, .closable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false
)
window.title = name
window.backgroundColor = .black
window.collectionBehavior = [.fullScreenPrimary]
window.contentView = NSHostingView(rootView: VirtualMachineFullScreenView(display: display))
window.isReleasedWhenClosed = false
window.makeKeyAndOrderFront(nil)
window.toggleFullScreen(nil)
windows[name] = window
watchForEscape()
}
static func dismiss(named name: String) {
guard let window = windows.removeValue(forKey: name) else { return }
if window.styleMask.contains(.fullScreen) {
window.toggleFullScreen(nil)
}
window.close()
if windows.isEmpty, let escapeWatch {
NSEvent.removeMonitor(escapeWatch)
self.escapeWatch = nil
}
}
/// The pointer belongs to the guest while it is grabbed, which puts the
/// menu bar and the window's own buttons out of reach.
private static func watchForEscape() {
guard escapeWatch == nil else { return }
escapeWatch = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
guard event.keyCode == escapeKeyCode else { return event }
MainActor.assumeIsolated {
for name in windows.keys where windows[name]?.isKeyWindow == true {
dismiss(named: name)
}
}
return nil
}
}
private static let escapeKeyCode: UInt16 = 53
private static var windows: [String: NSWindow] = [:]
private static var escapeWatch: Any?
}
private struct VirtualMachineFullScreenView: View {
let display: VirtualMachineDisplay
var body: some View {
ZStack {
Color.black
.ignoresSafeArea()
VirtualMachineDisplayView(display: display, capturing: .onAppear, dismissal: "Escape leaves full screen")
}
}
}
#endif