mirror of
https://github.com/morgan9e/VirtualDisplay
synced 2026-04-14 00:04:05 +09:00
Add source
This commit is contained in:
165
src/AppDelegate.swift
Normal file
165
src/AppDelegate.swift
Normal file
@@ -0,0 +1,165 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user