Files
VirtualDisplay/src/AppDelegate.swift
2026-04-11 03:50:41 +09:00

166 lines
5.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Cocoa
final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
private var statusItem: NSStatusItem!
private let menu = NSMenu()
private struct Physical {
let name: String
let pixelWidth: Int
let pixelHeight: Int
let refreshRate: Int
}
private var lastPhysical: Physical?
// lifecycle
func applicationDidFinishLaunching(_ notification: Notification) {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem.button {
if let img = NSImage(systemSymbolName: "rectangle.on.rectangle",
accessibilityDescription: "VirtualDisplay") {
img.isTemplate = true
button.image = img
} else {
button.title = "VD"
}
}
menu.delegate = self
menu.autoenablesItems = false
statusItem.menu = menu
ProcessInfo.processInfo.disableAutomaticTermination("VirtualDisplay running")
}
func applicationWillTerminate(_ notification: Notification) {
DisplayManager.shared.disable()
}
// NSMenuDelegate
func menuNeedsUpdate(_ menu: NSMenu) {
guard menu === self.menu else { return }
rebuild()
}
private func rebuild() {
menu.removeAllItems()
guard let phys = physicalDisplay() else {
addDisabled("No display detected")
menu.addItem(.separator())
addQuit()
return
}
addDisabled("\(phys.name) (\(phys.pixelWidth)×\(phys.pixelHeight) @ \(phys.refreshRate)Hz)")
menu.addItem(.separator())
let activeScale = DisplayManager.shared.currentScale
for s in availableScales(physicalPixelWidth: phys.pixelWidth,
physicalPixelHeight: phys.pixelHeight) {
let item = NSMenuItem(title: "\(s)%",
action: #selector(chooseScale(_:)),
keyEquivalent: "")
item.target = self
item.tag = s
item.state = (activeScale == s) ? .on : .off
menu.addItem(item)
}
if activeScale != nil {
menu.addItem(.separator())
addAction("Open Display Settings…", #selector(openDisplaySettings(_:)))
menu.addItem(.separator())
addAction("Disable", #selector(disable(_:)))
}
menu.addItem(.separator())
addQuit()
}
// Menu helpers
private func addDisabled(_ title: String) {
let item = NSMenuItem(title: title, action: nil, keyEquivalent: "")
item.isEnabled = false
menu.addItem(item)
}
private func addAction(_ title: String, _ action: Selector) {
let item = NSMenuItem(title: title, action: action, keyEquivalent: "")
item.target = self
menu.addItem(item)
}
private func addQuit() {
menu.addItem(NSMenuItem(title: "Quit",
action: #selector(NSApplication.terminate(_:)),
keyEquivalent: "q"))
}
// Physical display detection
private func physicalDisplay() -> Physical? {
if let live = liveLookup() { lastPhysical = live }
return lastPhysical
}
private func liveLookup() -> Physical? {
let ourID = DisplayManager.shared.virtualDisplayID
var count: UInt32 = 0
guard CGGetActiveDisplayList(0, nil, &count) == .success, count > 0 else {
return nil
}
var ids = [CGDirectDisplayID](repeating: 0, count: Int(count))
guard CGGetActiveDisplayList(count, &ids, &count) == .success,
let id = ids.first(where: { $0 != ourID }),
let mode = CGDisplayCopyDisplayMode(id) else {
return nil
}
let key = NSDeviceDescriptionKey("NSScreenNumber")
let name = NSScreen.screens.first {
($0.deviceDescription[key] as? NSNumber)?.uint32Value == id
}?.localizedName ?? lastPhysical?.name ?? "Display"
let hz = mode.refreshRate > 0 ? Int(mode.refreshRate.rounded()) : 60
return Physical(
name: name,
pixelWidth: mode.pixelWidth,
pixelHeight: mode.pixelHeight,
refreshRate: hz
)
}
// Actions
@objc private func chooseScale(_ sender: NSMenuItem) {
guard let phys = physicalDisplay() else { return }
let cfg = vdConfig(physicalPixelWidth: phys.pixelWidth,
physicalPixelHeight: phys.pixelHeight,
refreshRate: phys.refreshRate,
scalePercent: sender.tag)
DisplayManager.shared.setScale(sender.tag, config: cfg)
}
@objc private func openDisplaySettings(_ sender: NSMenuItem) {
let urls = [
"x-apple.systempreferences:com.apple.Displays-Settings.extension",
"x-apple.systempreferences:com.apple.preference.displays",
]
for str in urls {
if let url = URL(string: str), NSWorkspace.shared.open(url) {
return
}
}
}
@objc private func disable(_ sender: NSMenuItem) {
DisplayManager.shared.disable()
}
}