From f4718414192a3bab06d9f68ed7a1b4a156d78beb Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 19 Aug 2026 00:41:17 +0200 Subject: [PATCH 1/7] feat: standalone probe for virtual-display mirror HiDPI (#65) --- scripts/mirror-hidpi-probe.swift | 222 +++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 scripts/mirror-hidpi-probe.swift diff --git a/scripts/mirror-hidpi-probe.swift b/scripts/mirror-hidpi-probe.swift new file mode 100644 index 0000000..1f86310 --- /dev/null +++ b/scripts/mirror-hidpi-probe.swift @@ -0,0 +1,222 @@ +// Mirror-HiDPI probe for issue #65: WindowServer refuses scaled backings wider +// than ~6720px on 5K2K panels, so HiDPI sizes above looks-like ~3360x945 never +// enumerate. The plan is a virtual display that carries the big HiDPI mode (its +// framebuffer is rendered, not scanned out, so the cap should not apply), with +// the physical panel hardware-mirroring it and downscaling on scanout. This +// probe tests exactly that mechanism, standalone, before any app wiring: +// 1. create a CGVirtualDisplay with a 2x backing for the requested size +// 2. verify the looks-like HiDPI mode enumerates and becomes current +// 3. mirror the physical display onto it (virtual = master) +// 4. report the resulting state of both displays +// 5. tear down on Enter or Ctrl-C (unmirror, destroy, verify it is gone) +// +// Run: swift scripts/mirror-hidpi-probe.swift [displayID] +// logical ("looks like") size, e.g. 3840x1080 on a 5K2K panel +// [displayID] physical display to mirror; default: first external, else main +// +// Creating the display pops macOS's "What do you want to show?" picker; ignore +// it, the probe configures the mirror itself. If the screen goes wrong, Ctrl-C +// restores it; worst case, quitting the process kills the virtual display. +import AppKit + +func fail(_ msg: String) -> Never { print("FAIL: \(msg)"); exit(1) } + +// MARK: - Args + +let args = CommandLine.arguments +guard args.count >= 2 else { + fail("usage: swift scripts/mirror-hidpi-probe.swift [displayID]") +} +let sizeParts = args[1].lowercased().split(separator: "x") +guard sizeParts.count == 2, let logicalW = Int(sizeParts[0]), let logicalH = Int(sizeParts[1]), + logicalW > 0, logicalH > 0 else { + fail("bad size '\(args[1])', expected e.g. 3840x1080") +} + +var displayCount: UInt32 = 0 +CGGetOnlineDisplayList(0, nil, &displayCount) +var onlineIDs = [CGDirectDisplayID](repeating: 0, count: Int(displayCount)) +CGGetOnlineDisplayList(displayCount, &onlineIDs, &displayCount) + +let physical: CGDirectDisplayID +if args.count >= 3 { + guard let want = UInt32(args[2]), onlineIDs.contains(want) else { + fail("display \(args[2]) not online (online: \(onlineIDs))") + } + physical = want +} else { + physical = onlineIDs.first { CGDisplayIsBuiltin($0) == 0 } ?? CGMainDisplayID() +} + +// MARK: - Reporting helpers + +func modeString(_ m: CGDisplayMode) -> String { + let kind = m.pixelWidth > m.width ? " HiDPI" : "" + return "\(m.width)x\(m.height)\(kind) (px \(m.pixelWidth)x\(m.pixelHeight)) @\(Int(m.refreshRate.rounded()))Hz" +} + +func allModes(_ id: CGDirectDisplayID) -> [CGDisplayMode] { + let opts = [kCGDisplayShowDuplicateLowResolutionModes as String: true] as CFDictionary + return (CGDisplayCopyAllDisplayModes(id, opts) as? [CGDisplayMode]) ?? [] +} + +func report(_ label: String, _ id: CGDirectDisplayID) { + let cur = CGDisplayCopyDisplayMode(id).map(modeString) ?? "no mode" + let mirrors = CGDisplayMirrorsDisplay(id) + let mirrorStr = mirrors == kCGNullDirectDisplay ? "not mirroring" : "mirrors \(mirrors)" + print("\(label) \(id): \(cur) | \(mirrorStr) | hwMirrorSet=\(CGDisplayIsInHWMirrorSet(id) != 0) primary=\(CGDisplayPrimaryDisplay(id))") +} + +let physName = NSScreen.screens.first { + $0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID == physical +}?.localizedName ?? "?" +let physModes = allModes(physical) +let hidpiTop = physModes.filter { $0.pixelWidth > $0.width }.map(\.width).max() ?? 0 +print("Physical: \(physName) (\(physical)), \(physModes.count) modes, HiDPI ladder top \(hidpiTop)px wide") +report(" before:", physical) +print("Target: looks like \(logicalW)x\(logicalH), backing \(logicalW * 2)x\(logicalH * 2)") + +// MARK: - CGVirtualDisplay via the ObjC runtime (no bridging header in scripts) + +guard let descCls = NSClassFromString("CGVirtualDisplayDescriptor") as? NSObject.Type, + let modeCls: AnyClass = NSClassFromString("CGVirtualDisplayMode"), + let settingsCls = NSClassFromString("CGVirtualDisplaySettings") as? NSObject.Type, + let displayCls: AnyClass = NSClassFromString("CGVirtualDisplay") +else { fail("CGVirtualDisplay private API unavailable") } + +func alloc(_ cls: AnyClass) -> AnyObject { + let imp = class_getMethodImplementation(object_getClass(cls), NSSelectorFromString("alloc")) + let fn = unsafeBitCast(imp, to: (@convention(c) (AnyClass, Selector) -> Unmanaged).self) + return fn(cls, NSSelectorFromString("alloc")).takeRetainedValue() +} + +func makeMode(_ pixelW: Int, _ pixelH: Int, _ hz: Double) -> AnyObject { + let sel = NSSelectorFromString("initWithWidth:height:refreshRate:") + let imp = class_getMethodImplementation(modeCls, sel) + let fn = unsafeBitCast(imp, to: (@convention(c) (AnyObject, Selector, UInt, UInt, Double) -> Unmanaged).self) + return fn(alloc(modeCls), sel, UInt(pixelW), UInt(pixelH), hz).takeRetainedValue() +} + +let desc = descCls.init() +// Report the physical panel's own physical size so macOS computes a sane PPI. +let mm = CGDisplayScreenSize(physical) +desc.setValue(NSValue(size: NSSize(width: mm.width, height: mm.height)), forKey: "sizeInMillimeters") +desc.setValue(UInt32(logicalW * 2), forKey: "maxPixelsWide") +desc.setValue(UInt32(logicalH * 2), forKey: "maxPixelsHigh") +desc.setValue("Crisp Mirror Probe", forKey: "name") +desc.setValue(UInt32(0xEEEE), forKey: "vendorID") // Crisp's virtual-display stamp +desc.setValue(UInt32(0x50524F42), forKey: "productID") // "PROB" +desc.setValue(UInt32(1), forKey: "serialNum") + +let physRate = CGDisplayCopyDisplayMode(physical)?.refreshRate ?? 60 +var rates: [Double] = [60] +if physRate > 0, abs(physRate - 60) >= 1 { rates.append(physRate) } + +let settings = settingsCls.init() +settings.setValue(true, forKey: "hiDPI") +var modeObjs: [AnyObject] = [] +for hz in rates { + modeObjs.append(makeMode(logicalW * 2, logicalH * 2, hz)) // the 2x backing + modeObjs.append(makeMode(logicalW, logicalH, hz)) // 1x fallback +} +settings.setValue(modeObjs as NSArray, forKey: "modes") + +let initSel = NSSelectorFromString("initWithDescriptor:") +let initImp = class_getMethodImplementation(displayCls, initSel) +let initFn = unsafeBitCast(initImp, to: (@convention(c) (AnyObject, Selector, AnyObject) -> Unmanaged?).self) +// Kept in a global so teardown() can release it; releasing destroys the display. +var virtualDisplay: AnyObject? = initFn(alloc(displayCls), initSel, desc)?.takeRetainedValue() +guard let vd = virtualDisplay else { fail("CGVirtualDisplay init returned nil") } + +let applySel = NSSelectorFromString("applySettings:") +let applyImp = class_getMethodImplementation(displayCls, applySel) +let applyFn = unsafeBitCast(applyImp, to: (@convention(c) (AnyObject, Selector, AnyObject) -> Bool).self) +guard applyFn(vd, applySel, settings) else { fail("applySettings failed") } + +guard let vdID = (vd as? NSObject)?.value(forKey: "displayID") as? CGDirectDisplayID, + vdID != kCGNullDirectDisplay else { fail("virtual display has no displayID") } +print("Virtual display created: id \(vdID)") + +// MARK: - Mirror config + teardown + +func setMirror(_ display: CGDirectDisplayID, master: CGDirectDisplayID) -> Bool { + var cfg: CGDisplayConfigRef? + guard CGBeginDisplayConfiguration(&cfg) == .success, let c = cfg else { return false } + CGConfigureDisplayMirrorOfDisplay(c, display, master) + if CGCompleteDisplayConfiguration(c, .forSession) != .success { + CGCancelDisplayConfiguration(c) + return false + } + return true +} + +func teardown() { + print("\nTearing down: unmirror -> destroy virtual display") + if !setMirror(physical, master: kCGNullDirectDisplay) { print(" unmirror FAILED") } + virtualDisplay = nil // last strong reference: WindowServer removes the display + usleep(1_500_000) + var n: UInt32 = 0 + CGGetOnlineDisplayList(0, nil, &n) + var ids = [CGDirectDisplayID](repeating: 0, count: Int(n)) + CGGetOnlineDisplayList(n, &ids, &n) + print(ids.contains(vdID) ? " virtual display STILL ONLINE (check after exit)" : " virtual display gone") + report(" physical after:", physical) +} + +signal(SIGINT, SIG_IGN) +let sigSrc = DispatchSource.makeSignalSource(signal: SIGINT, queue: .global()) +sigSrc.setEventHandler { teardown(); exit(0) } +sigSrc.resume() + +// MARK: - Drive the virtual display to the looks-like HiDPI mode + +// WindowServer finishes bringing the display up (and picks its own default +// mode) asynchronously; retry briefly like VirtualDisplayService does. +var hidpiMode: CGDisplayMode? +for attempt in 0..<10 { + if attempt > 0 { usleep(300_000) } + let modes = allModes(vdID) + if modes.isEmpty { continue } + if hidpiMode == nil { + print("Virtual modes (\(modes.count)):") + for m in modes { print(" \(modeString(m))") } + } + hidpiMode = modes.first { $0.width == logicalW && $0.height == logicalH && $0.pixelWidth == logicalW * 2 } + if hidpiMode != nil { break } +} +guard let target = hidpiMode else { + teardown() + fail("looks-like \(logicalW)x\(logicalH) HiDPI mode never enumerated on the virtual display — the cap may apply to virtual framebuffers too") +} + +var cfg: CGDisplayConfigRef? +guard CGBeginDisplayConfiguration(&cfg) == .success, let c = cfg, + CGConfigureDisplayWithDisplayMode(c, vdID, target, nil) == .success, + CGCompleteDisplayConfiguration(c, .forSession) == .success else { + teardown() + fail("could not set the virtual display to \(modeString(target))") +} +usleep(500_000) +report("Virtual", vdID) + +// MARK: - Mirror the physical onto it + +print("Mirroring physical \(physical) onto virtual \(vdID)...") +guard setMirror(physical, master: vdID) else { + teardown() + fail("mirror configuration failed") +} +usleep(1_000_000) +print("--- Mirrored state ---") +report("Physical", physical) +report("Virtual ", vdID) +if CGDisplayMirrorsDisplay(physical) == vdID { + print("SUCCESS: physical is mirroring the virtual display.") + print("Look at the screen: is it the \(logicalW)x\(logicalH) desktop, sharp, full-screen?") + print("Drag a window, check refresh feel, then judge text sharpness up close.") +} else { + print("MIRROR DID NOT STICK — WindowServer reports no mirror on the physical display.") +} +print("\nPress Enter (or Ctrl-C) to tear down and restore.") +_ = readLine() +teardown() From b514baf360816d1865c6b81a9bebc33e24939e91 Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 19 Aug 2026 00:55:59 +0200 Subject: [PATCH 2/7] fix: probe survives CLI display-cache quirk, proves mirror HiDPI (#65) Three findings baked in: alloc/init ownership must stay Unmanaged (claiming both over-releases), the looks-like HiDPI mode only materializes when both the 2x backing and the half-size pixel mode are declared, and a bare CLI's CG display-info cache never refreshes (create first, query after). Verified end to end on a 2560x1440 external: hardware mirror set, 165Hz preserved. --- scripts/mirror-hidpi-probe.swift | 160 ++++++++++++++++++------------- 1 file changed, 92 insertions(+), 68 deletions(-) diff --git a/scripts/mirror-hidpi-probe.swift b/scripts/mirror-hidpi-probe.swift index 1f86310..b4647c9 100644 --- a/scripts/mirror-hidpi-probe.swift +++ b/scripts/mirror-hidpi-probe.swift @@ -14,11 +14,23 @@ // logical ("looks like") size, e.g. 3840x1080 on a 5K2K panel // [displayID] physical display to mirror; default: first external, else main // -// Creating the display pops macOS's "What do you want to show?" picker; ignore -// it, the probe configures the mirror itself. If the screen goes wrong, Ctrl-C -// restores it; worst case, quitting the process kills the virtual display. +// CLI quirk (cost hours, do not reorder): CG keeps a per-process display-info +// cache that refreshes via runloop-delivered notifications, and those are never +// delivered to a bare CLI (reconfiguration callbacks do not fire either, even +// when pumping the runloop). A cache warmed BEFORE the virtual display exists +// therefore never learns its modes: CGDisplayCopyAllDisplayModes returns nil +// forever. So the virtual display is created FIRST, before any display query; +// the first query then builds a cache that contains both displays. The app has +// a live runloop and reconfig callbacks, so none of this applies there. +// +// Creating the display pops macOS's "What do you want to show?" picker (at most +// once per identity); ignore it, the probe configures the mirror itself. If the +// screen goes wrong, Ctrl-C restores it; worst case, quitting the process kills +// the virtual display. import AppKit +setvbuf(stdout, nil, _IONBF, 0) // line output survives even if we die mid-run + func fail(_ msg: String) -> Never { print("FAIL: \(msg)"); exit(1) } // MARK: - Args @@ -32,48 +44,6 @@ guard sizeParts.count == 2, let logicalW = Int(sizeParts[0]), let logicalH = Int logicalW > 0, logicalH > 0 else { fail("bad size '\(args[1])', expected e.g. 3840x1080") } - -var displayCount: UInt32 = 0 -CGGetOnlineDisplayList(0, nil, &displayCount) -var onlineIDs = [CGDirectDisplayID](repeating: 0, count: Int(displayCount)) -CGGetOnlineDisplayList(displayCount, &onlineIDs, &displayCount) - -let physical: CGDirectDisplayID -if args.count >= 3 { - guard let want = UInt32(args[2]), onlineIDs.contains(want) else { - fail("display \(args[2]) not online (online: \(onlineIDs))") - } - physical = want -} else { - physical = onlineIDs.first { CGDisplayIsBuiltin($0) == 0 } ?? CGMainDisplayID() -} - -// MARK: - Reporting helpers - -func modeString(_ m: CGDisplayMode) -> String { - let kind = m.pixelWidth > m.width ? " HiDPI" : "" - return "\(m.width)x\(m.height)\(kind) (px \(m.pixelWidth)x\(m.pixelHeight)) @\(Int(m.refreshRate.rounded()))Hz" -} - -func allModes(_ id: CGDirectDisplayID) -> [CGDisplayMode] { - let opts = [kCGDisplayShowDuplicateLowResolutionModes as String: true] as CFDictionary - return (CGDisplayCopyAllDisplayModes(id, opts) as? [CGDisplayMode]) ?? [] -} - -func report(_ label: String, _ id: CGDirectDisplayID) { - let cur = CGDisplayCopyDisplayMode(id).map(modeString) ?? "no mode" - let mirrors = CGDisplayMirrorsDisplay(id) - let mirrorStr = mirrors == kCGNullDirectDisplay ? "not mirroring" : "mirrors \(mirrors)" - print("\(label) \(id): \(cur) | \(mirrorStr) | hwMirrorSet=\(CGDisplayIsInHWMirrorSet(id) != 0) primary=\(CGDisplayPrimaryDisplay(id))") -} - -let physName = NSScreen.screens.first { - $0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID == physical -}?.localizedName ?? "?" -let physModes = allModes(physical) -let hidpiTop = physModes.filter { $0.pixelWidth > $0.width }.map(\.width).max() ?? 0 -print("Physical: \(physName) (\(physical)), \(physModes.count) modes, HiDPI ladder top \(hidpiTop)px wide") -report(" before:", physical) print("Target: looks like \(logicalW)x\(logicalH), backing \(logicalW * 2)x\(logicalH * 2)") // MARK: - CGVirtualDisplay via the ObjC runtime (no bridging header in scripts) @@ -84,23 +54,31 @@ guard let descCls = NSClassFromString("CGVirtualDisplayDescriptor") as? NSObject let displayCls: AnyClass = NSClassFromString("CGVirtualDisplay") else { fail("CGVirtualDisplay private API unavailable") } -func alloc(_ cls: AnyClass) -> AnyObject { +// Ownership: alloc returns +1 which the init call CONSUMES, so the alloc'd +// reference must never be claimed by Swift (hence Unmanaged + takeUnretained +// at the call site); only the init RESULT is claimed, with takeRetained. +// Claiming both over-releases and crashes in objc_release. +func alloc(_ cls: AnyClass) -> Unmanaged { let imp = class_getMethodImplementation(object_getClass(cls), NSSelectorFromString("alloc")) let fn = unsafeBitCast(imp, to: (@convention(c) (AnyClass, Selector) -> Unmanaged).self) - return fn(cls, NSSelectorFromString("alloc")).takeRetainedValue() + return fn(cls, NSSelectorFromString("alloc")) } func makeMode(_ pixelW: Int, _ pixelH: Int, _ hz: Double) -> AnyObject { let sel = NSSelectorFromString("initWithWidth:height:refreshRate:") let imp = class_getMethodImplementation(modeCls, sel) let fn = unsafeBitCast(imp, to: (@convention(c) (AnyObject, Selector, UInt, UInt, Double) -> Unmanaged).self) - return fn(alloc(modeCls), sel, UInt(pixelW), UInt(pixelH), hz).takeRetainedValue() + return fn(alloc(modeCls).takeUnretainedValue(), sel, UInt(pixelW), UInt(pixelH), hz).takeRetainedValue() } let desc = descCls.init() -// Report the physical panel's own physical size so macOS computes a sane PPI. -let mm = CGDisplayScreenSize(physical) -desc.setValue(NSValue(size: NSSize(width: mm.width, height: mm.height)), forKey: "sizeInMillimeters") +// Pure math, no CG query allowed yet (see the CLI quirk above): report a size +// at ~110 PPI like VirtualDisplayService. Only affects PPI cosmetics; the +// looks-like mode is forced explicitly below. +let ppi = 110.0 +desc.setValue(NSValue(size: NSSize(width: Double(logicalW * 2) / ppi * 25.4, + height: Double(logicalH * 2) / ppi * 25.4)), + forKey: "sizeInMillimeters") desc.setValue(UInt32(logicalW * 2), forKey: "maxPixelsWide") desc.setValue(UInt32(logicalH * 2), forKey: "maxPixelsHigh") desc.setValue("Crisp Mirror Probe", forKey: "name") @@ -108,16 +86,17 @@ desc.setValue(UInt32(0xEEEE), forKey: "vendorID") // Crisp's virtual-display s desc.setValue(UInt32(0x50524F42), forKey: "productID") // "PROB" desc.setValue(UInt32(1), forKey: "serialNum") -let physRate = CGDisplayCopyDisplayMode(physical)?.refreshRate ?? 60 -var rates: [Double] = [60] -if physRate > 0, abs(physRate - 60) >= 1 { rates.append(physRate) } - let settings = settingsCls.init() settings.setValue(true, forKey: "hiDPI") var modeObjs: [AnyObject] = [] -for hz in rates { +// The looks-like HiDPI mode only materializes when BOTH the 2x backing and the +// half-size pixel mode are declared (found empirically: backing alone gets 1x +// modes plus retina twins of the auto-added smaller sizes, never of the max). +// Fixed rate ladder, since reading the panel's rate pre-creation is forbidden; +// WindowServer keeps what it supports. +for hz in [60.0, 75.0, 100.0, 120.0, 144.0, 165.0] { modeObjs.append(makeMode(logicalW * 2, logicalH * 2, hz)) // the 2x backing - modeObjs.append(makeMode(logicalW, logicalH, hz)) // 1x fallback + modeObjs.append(makeMode(logicalW, logicalH, hz)) // half-size pixel mode } settings.setValue(modeObjs as NSArray, forKey: "modes") @@ -125,7 +104,7 @@ let initSel = NSSelectorFromString("initWithDescriptor:") let initImp = class_getMethodImplementation(displayCls, initSel) let initFn = unsafeBitCast(initImp, to: (@convention(c) (AnyObject, Selector, AnyObject) -> Unmanaged?).self) // Kept in a global so teardown() can release it; releasing destroys the display. -var virtualDisplay: AnyObject? = initFn(alloc(displayCls), initSel, desc)?.takeRetainedValue() +var virtualDisplay: AnyObject? = initFn(alloc(displayCls).takeUnretainedValue(), initSel, desc)?.takeRetainedValue() guard let vd = virtualDisplay else { fail("CGVirtualDisplay init returned nil") } let applySel = NSSelectorFromString("applySettings:") @@ -137,6 +116,53 @@ guard let vdID = (vd as? NSObject)?.value(forKey: "displayID") as? CGDirectDispl vdID != kCGNullDirectDisplay else { fail("virtual display has no displayID") } print("Virtual display created: id \(vdID)") +// MARK: - Now the display queries are safe; pick and report the physical + +func onlineDisplays() -> [CGDirectDisplayID] { + var n: UInt32 = 0 + CGGetOnlineDisplayList(0, nil, &n) + var ids = [CGDirectDisplayID](repeating: 0, count: Int(n)) + CGGetOnlineDisplayList(n, &ids, &n) + return ids +} + +func modeString(_ m: CGDisplayMode) -> String { + let kind = m.pixelWidth > m.width ? " HiDPI" : "" + return "\(m.width)x\(m.height)\(kind) (px \(m.pixelWidth)x\(m.pixelHeight)) @\(Int(m.refreshRate.rounded()))Hz" +} + +func allModes(_ id: CGDirectDisplayID) -> [CGDisplayMode] { + let opts = [kCGDisplayShowDuplicateLowResolutionModes as String: true] as CFDictionary + return (CGDisplayCopyAllDisplayModes(id, opts) as? [CGDisplayMode]) ?? [] +} + +func report(_ label: String, _ id: CGDirectDisplayID) { + let cur = CGDisplayCopyDisplayMode(id).map(modeString) ?? "no mode" + let mirrors = CGDisplayMirrorsDisplay(id) + let mirrorStr = mirrors == kCGNullDirectDisplay ? "not mirroring" : "mirrors \(mirrors)" + print("\(label) \(id): \(cur) | \(mirrorStr) | hwMirrorSet=\(CGDisplayIsInHWMirrorSet(id) != 0) primary=\(CGDisplayPrimaryDisplay(id))") +} + +let candidates = onlineDisplays().filter { $0 != vdID } +let physical: CGDirectDisplayID +if args.count >= 3 { + guard let want = UInt32(args[2]), candidates.contains(want) else { + virtualDisplay = nil + fail("display \(args[2]) not online (online: \(candidates))") + } + physical = want +} else { + physical = candidates.first { CGDisplayIsBuiltin($0) == 0 } ?? CGMainDisplayID() +} + +let physName = NSScreen.screens.first { + $0.deviceDescription[NSDeviceDescriptionKey("NSScreenNumber")] as? CGDirectDisplayID == physical +}?.localizedName ?? "?" +let physModes = allModes(physical) +let hidpiTop = physModes.filter { $0.pixelWidth > $0.width }.map(\.width).max() ?? 0 +print("Physical: \(physName) (\(physical)), \(physModes.count) modes, HiDPI ladder top \(hidpiTop)px wide") +report(" before:", physical) + // MARK: - Mirror config + teardown func setMirror(_ display: CGDirectDisplayID, master: CGDirectDisplayID) -> Bool { @@ -152,14 +178,13 @@ func setMirror(_ display: CGDirectDisplayID, master: CGDirectDisplayID) -> Bool func teardown() { print("\nTearing down: unmirror -> destroy virtual display") - if !setMirror(physical, master: kCGNullDirectDisplay) { print(" unmirror FAILED") } + if CGDisplayMirrorsDisplay(physical) != kCGNullDirectDisplay, + !setMirror(physical, master: kCGNullDirectDisplay) { print(" unmirror FAILED") } virtualDisplay = nil // last strong reference: WindowServer removes the display usleep(1_500_000) - var n: UInt32 = 0 - CGGetOnlineDisplayList(0, nil, &n) - var ids = [CGDirectDisplayID](repeating: 0, count: Int(n)) - CGGetOnlineDisplayList(n, &ids, &n) - print(ids.contains(vdID) ? " virtual display STILL ONLINE (check after exit)" : " virtual display gone") + print(onlineDisplays().contains(vdID) + ? " virtual display STILL ONLINE (stale CLI cache is possible; verify in System Settings)" + : " virtual display gone") report(" physical after:", physical) } @@ -170,11 +195,10 @@ sigSrc.resume() // MARK: - Drive the virtual display to the looks-like HiDPI mode -// WindowServer finishes bringing the display up (and picks its own default -// mode) asynchronously; retry briefly like VirtualDisplayService does. +// WindowServer finishes bringing the display up asynchronously; retry briefly. var hidpiMode: CGDisplayMode? -for attempt in 0..<10 { - if attempt > 0 { usleep(300_000) } +for attempt in 0..<20 { + if attempt > 0 { usleep(500_000) } let modes = allModes(vdID) if modes.isEmpty { continue } if hidpiMode == nil { From 482499b2321a43018f7cb4018911d61480bcdf3e Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 19 Aug 2026 01:05:01 +0200 Subject: [PATCH 3/7] feat: MirroredModeService, beyond-cap HiDPI via mirrored virtual display (#65) Lazy lifecycle: first apply creates a hidden virtual display (stable per-monitor identity, MIRR serial marker, backing-only mode ladder for every beyond-cap stop), drives it to the looks-like HiDPI mode and hardware-mirrors the physical panel onto it; later applies just switch the virtual's mode. restore() unmirrors first, then destroys. Quit teardown unmirrors synchronously before the virtual displays die. --- Crisp/App/AppDelegate.swift | 3 + Crisp/Services/MirroredModeService.swift | 238 +++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 Crisp/Services/MirroredModeService.swift diff --git a/Crisp/App/AppDelegate.swift b/Crisp/App/AppDelegate.swift index b9bc7a2..331c7cf 100644 --- a/Crisp/App/AppDelegate.swift +++ b/Crisp/App/AppDelegate.swift @@ -239,6 +239,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate { // so no monitor is left bright with no boost and no DDC control. BrightnessBoostService.shared.prepareForTermination() // GammaService already handles CGDisplayRestoreColorSyncSettings via willTerminateNotification observer. + // Unmirror before the virtual displays die, so no panel is left showing + // a mirror of a display that just vanished. + MirroredModeService.shared.teardownAll() VirtualDisplayService.shared.destroyAll() } diff --git a/Crisp/Services/MirroredModeService.swift b/Crisp/Services/MirroredModeService.swift new file mode 100644 index 0000000..3745d86 --- /dev/null +++ b/Crisp/Services/MirroredModeService.swift @@ -0,0 +1,238 @@ +import CoreGraphics +import Foundation + +/// True HiDPI past WindowServer's scaled-backing cap (issue #65). On 5K2K +/// ultrawides macOS refuses scaled backings wider than ~6720px, so looks-like +/// sizes between the ladder top (~3360 wide) and native never enumerate as +/// HiDPI on the physical display. This service delivers them anyway: it creates +/// a hidden virtual display whose framebuffer is rendered, not scanned out (the +/// cap does not apply there), drives it to the wanted looks-like HiDPI mode, +/// and hardware-mirrors the physical panel onto it; the scanout engine +/// downscales. The findings behind the recipe (mode declaration rules, the +/// ~400-object applySettings ceiling, unmirror-before-destroy order) live in +/// scripts/mirror-hidpi-probe.swift and its commit history. +/// +/// Lazy lifecycle: the virtual display exists only while a beyond-cap size is +/// active; `restore` unmirrors first, then destroys. Nothing is persisted, a +/// relaunch comes up unmirrored. Rotated panels are unverified with mirroring. +@MainActor +final class MirroredModeService: ObservableObject { + static let shared = MirroredModeService() + private init() {} + + /// Live CGVirtualDisplay per mirrored physical display. Releasing a value + /// is what destroys its virtual display, so this dictionary IS the state. + private var active: [CGDirectDisplayID: CGVirtualDisplay] = [:] + + /// Published mirror of `active`'s keys so views can observe activity. + @Published private(set) var activePhysicalIDs: Set = [] + + /// Serial-number marker stamped on every mirror virtual ("MIRR"), alongside + /// the shared 0xEEEE vendor stamp (which keeps every existing + /// isVirtualDisplay filter treating these as virtual). Lets launch recovery + /// recognize a stray mirror virtual left by a crash. + static let mirrorSerialMarker: UInt32 = 0x4D49_5252 + + // MARK: - Queries + + func isActive(for physicalID: CGDirectDisplayID) -> Bool { + active[physicalID] != nil + } + + func virtualDisplayID(for physicalID: CGDirectDisplayID) -> CGDirectDisplayID? { + active[physicalID]?.displayID + } + + /// The looks-like size currently rendered for a mirrored physical display + /// (read from the virtual master's active mode), or nil when not mirrored. + func currentLooksLike(for physicalID: CGDirectDisplayID) -> (width: Int, height: Int)? { + guard let vdID = active[physicalID]?.displayID, + let cur = CGDisplayCopyDisplayMode(vdID) else { return nil } + return (cur.width, cur.height) + } + + // MARK: - Apply / Restore + + /// Puts `display` on a beyond-cap looks-like size: first call creates the + /// mirror virtual and enables the mirror; subsequent calls only switch the + /// virtual's mode. Returns false with everything unwound on failure, so a + /// failed attempt never leaves a half-built mirror. + @discardableResult + func apply(display: DisplayInfo, width: Int, height: Int) async -> Bool { + guard !display.isBuiltin else { return false } + let physicalID = display.displayID + + if let vdID = active[physicalID]?.displayID { + return await setLooksLike(width: width, height: height, on: vdID) + } + + guard let virtualDisplay = await createMirrorVirtual(for: display, + mustInclude: (width, height)) + else { return false } + active[physicalID] = virtualDisplay + activePhysicalIDs.insert(physicalID) + + guard await setLooksLike(width: width, height: height, on: virtualDisplay.displayID), + await MirrorService.shared.enableMirror(source: virtualDisplay.displayID, + target: physicalID) else { + await restore(physicalID: physicalID) + return false + } + return true + } + + /// Leaves mirror mode: unmirrors the physical display, then destroys the + /// virtual. The caller applies whatever real mode it wants afterwards. + /// Order matters: destroying the master of a live mirror is undefined, so + /// always unmirror first (the probe's verified-safe order). + @discardableResult + func restore(display: DisplayInfo) async -> Bool { + await restore(physicalID: display.displayID) + } + + @discardableResult + func restore(physicalID: CGDirectDisplayID) async -> Bool { + guard let vdID = active[physicalID]?.displayID else { return true } + let unmirrored = await MirrorService.shared.disableMirror(displayID: physicalID) + // Dropping the last reference starts WindowServer's async teardown. + active.removeValue(forKey: physicalID) + activePhysicalIDs.remove(physicalID) + await waitForDisplayOffline(vdID) + return unmirrored + } + + /// Quit-path teardown. applicationWillTerminate cannot await, so the + /// unmirror runs as a direct synchronous transaction; a rare WindowServer + /// hang at quit beats leaving the panel mirrored. (Process death would + /// also collapse the mirror set, this just makes it orderly.) + func teardownAll() { + for physicalID in active.keys { + var config: CGDisplayConfigRef? + guard CGBeginDisplayConfiguration(&config) == .success, let cfg = config else { continue } + CGConfigureDisplayMirrorOfDisplay(cfg, physicalID, kCGNullDirectDisplay) + if CGCompleteDisplayConfiguration(cfg, .forSession) != .success { + CGCancelDisplayConfiguration(cfg) + } + } + active.removeAll() + activePhysicalIDs.removeAll() + } + + // MARK: - Creation + + /// Builds the mirror virtual for a physical display: stable identity (so + /// macOS's "what do you want to show" picker appears at most once per + /// monitor and its answer is remembered), the panel's physical size (sane + /// PPI), and backing-only mode declarations for every beyond-cap stop. + /// Backing-only because a dense backing set makes WindowServer mint the + /// looks-like HiDPI twins itself, and declaring the half-size twins too + /// would run into the ~400-object ceiling where applySettings rejects the + /// whole set (both found empirically with the probe). + private func createMirrorVirtual(for display: DisplayInfo, + mustInclude: (width: Int, height: Int)) async -> CGVirtualDisplay? { + let (nativeW, nativeH) = display.nativeResolution + guard nativeW > 0, nativeH > 0 else { return nil } + + // The new-display registration pops macOS's picker, which steals key + // focus and would trip the panel's auto-dismiss; same suppression as + // VirtualDisplayService.create. + PanelOpenGuard.suppressAutoDismiss = true + defer { + Task { @MainActor in + try? await Task.sleep(nanoseconds: 1_500_000_000) + PanelOpenGuard.suppressAutoDismiss = false + } + } + + let descriptor = CGVirtualDisplayDescriptor() + let mm = CGDisplayScreenSize(display.displayID) + descriptor.sizeInMillimeters = mm.width > 0 ? mm + : CGSize(width: Double(nativeW) / 110.0 * 25.4, height: Double(nativeH) / 110.0 * 25.4) + descriptor.maxPixelsWide = UInt32(nativeW * 2) + descriptor.maxPixelsHigh = UInt32(nativeH * 2) + descriptor.name = display.name + descriptor.vendorID = VirtualDisplayService.crispVirtualVendorID + // Stable per monitor; the serial carries the mirror marker. Two + // identical monitors mirroring at once would collide, accepted edge. + let panelIdentity = display.vendorNumber ^ display.modelNumber + descriptor.productID = panelIdentity != 0 ? panelIdentity : 0x4D52 + descriptor.serialNum = Self.mirrorSerialMarker + + // Every beyond-cap stop on the smooth-scaling grid, in the same + // (rotated) space as availableModes and the slider; the requested size + // is force-included in case it sits off that grid. + let hidpiTop = display.availableModes.filter { $0.isHiDPI }.map(\.width).max() ?? 0 + var stops = HiDPIService.shared + .smoothScaledLogicalSizes(nativeWidth: nativeW, nativeHeight: nativeH) + .filter { $0.width > hidpiTop && $0.width < nativeW } + if !stops.contains(where: { $0.width == mustInclude.width && $0.height == mustInclude.height }) { + stops.append((width: mustInclude.width, height: mustInclude.height)) + } + + // The panel's own refresh rate plus 60 as the safe floor. + let panelRate = display.currentDisplayMode?.refreshRate ?? 60 + var rates: [Double] = [60] + if panelRate > 0, abs(panelRate - 60) >= 1 { rates.append(panelRate) } + + var modes: [CGVirtualDisplayMode] = [] + for stop in stops where stop.width >= 1 && stop.height >= 1 { + for rate in rates { + modes.append(CGVirtualDisplayMode(width: UInt(stop.width * 2), + height: UInt(stop.height * 2), + refreshRate: rate)) + } + } + guard !modes.isEmpty else { return nil } + + let settings = CGVirtualDisplaySettings() + settings.hiDPI = true + settings.modes = modes + + guard let virtualDisplay = CGVirtualDisplay(descriptor: descriptor) else { return nil } + // apply blocks on WindowServer IPC; off-main with a timeout like every + // CG transaction (same as VirtualDisplayService.create). + let vd = virtualDisplay + let s = settings + let applied: Bool = await CGHelpers.runWithTimeout(seconds: 10, fallback: false) { + vd.apply(s) + } + guard applied, virtualDisplay.displayID != kCGNullDirectDisplay else { return nil } + return virtualDisplay + } + + // MARK: - Helpers + + /// Drives the virtual display to the looks-like HiDPI mode, retrying while + /// WindowServer finishes enumerating the fresh display. Prefers the highest + /// refresh rate offered at that size (the panel's own rate when kept). + private func setLooksLike(width: Int, height: Int, on virtualID: CGDirectDisplayID) async -> Bool { + let options = [kCGDisplayShowDuplicateLowResolutionModes as String: true] as CFDictionary + for attempt in 0..<10 { + if attempt > 0 { + await ReconfigEvents.shared.next(for: virtualID, + matching: [.setModeFlag, .addFlag], timeout: 0.4) + } + if let cur = CGDisplayCopyDisplayMode(virtualID), + cur.width == width, cur.height == height, cur.pixelWidth == width * 2 { return true } + guard let modes = CGDisplayCopyAllDisplayModes(virtualID, options) as? [CGDisplayMode], + let target = modes.filter({ + $0.width == width && $0.height == height && $0.pixelWidth == width * 2 + }).max(by: { $0.refreshRate < $1.refreshRate }) + else { continue } + if await ResolutionService.applyModeSync(target, on: virtualID) { return true } + } + return false + } + + /// Waits (bounded) for a torn-down virtual display to leave the online + /// list; same event-driven pattern as VirtualDisplayService, duplicated + /// because both keep it private to their own teardown story. + private func waitForDisplayOffline(_ displayID: CGDirectDisplayID) async { + var count: UInt32 = 0 + CGGetOnlineDisplayList(0, nil, &count) + var ids = [CGDirectDisplayID](repeating: 0, count: Int(count)) + CGGetOnlineDisplayList(count, &ids, &count) + guard ids.contains(displayID) else { return } + await ReconfigEvents.shared.next(for: displayID, matching: .removeFlag, timeout: 1.5) + } +} From 4c9189d39b446e63ab3c479bc7ae7acb705e19f7 Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 19 Aug 2026 01:09:18 +0200 Subject: [PATCH 4/7] feat: beyond-cap slider stops route through the mirrored virtual (#65) Synthetic stops (negative ids, same 16px grid) fill the gap between the enumerable HiDPI ladder top and native on capped panels. switchTo routes them to MirroredModeService and unmirrors before applying any real mode, so every entry point (slider, resolution row, refresh row) handles the mirror lifecycle through the one serialized path. Readback needs no new code: while mirrored the physical reports the virtual's looks-like mode, which matches the synthetic stop by size. --- Crisp/Views/DisplayModeListView.swift | 51 ++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/Crisp/Views/DisplayModeListView.swift b/Crisp/Views/DisplayModeListView.swift index 3e55991..d4bebc4 100644 --- a/Crisp/Views/DisplayModeListView.swift +++ b/Crisp/Views/DisplayModeListView.swift @@ -247,10 +247,25 @@ final class DisplayModeController: ObservableObject { isSwitching = true let displayID = display.displayID Task { @MainActor in - var success = await ResolutionService.shared.setDisplayMode(mode, for: displayID) - if !success { - try? await Task.sleep(nanoseconds: 200_000_000) + var success: Bool + if mode.id < 0 { + // Synthetic beyond-cap stop (#65, negative id): no CG mode exists + // on the physical display; MirroredModeService renders the size on + // a hidden virtual display the panel hardware-mirrors. + success = await MirroredModeService.shared.apply( + display: display, width: mode.width, height: mode.height) + } else { + // Leaving a mirrored stop for a real mode: unmirror and destroy + // first, otherwise the physical display is still a mirror target + // and the mode change would be redirected to the virtual source. + if MirroredModeService.shared.isActive(for: displayID) { + await MirroredModeService.shared.restore(display: display) + } success = await ResolutionService.shared.setDisplayMode(mode, for: displayID) + if !success { + try? await Task.sleep(nanoseconds: 200_000_000) + success = await ResolutionService.shared.setDisplayMode(mode, for: displayID) + } } if success { // Optimistic: the reconfiguration callback's setModeFlag branch @@ -318,7 +333,7 @@ final class DisplayModeController: ObservableObject { // native exists, so the dedup below keeps the crisp one for the "More Space" end. let hasNativeDefault = display.availableModes.contains { !$0.isHiDPI && $0.width == nativeW && $0.height == nativeH } var seen = Set() - return display.availableModes + var ladder = display.availableModes .filter { guard DisplayModeGeometry.hasSameOrientation( width: $0.width, height: $0.height, as: nativeW, nativeH @@ -338,7 +353,28 @@ final class DisplayModeController: ObservableObject { return $0.refreshRate > $1.refreshRate } .filter { seen.insert("\($0.width)x\($0.height)").inserted } - .sorted { $0.width == $1.width ? $0.height < $1.height : $0.width < $1.width } + // Beyond-cap synthetic stops (#65): WindowServer refuses scaled backings + // above a per-display cap, so on 5K2K ultrawides the sizes between the + // enumerable ladder top (~looks-like 3360) and native exist as no HiDPI + // mode at all. Mint slider stops for them on the same 16px grid, with + // NEGATIVE ids so they can never collide with a real ioDisplayModeID or + // reach the CG apply path: switchTo routes them to MirroredModeService + // (a hidden virtual display renders the 2x backing, the panel hardware- + // mirrors it and downscales on scanout). Gated on the dense ladder being + // live, like the rest of smooth scaling; on uncapped panels the filter + // yields nothing and the slider is exactly what it was. + if !display.isBuiltin, smoothModesPresent { + let hidpiTop = display.availableModes.filter { $0.isHiDPI }.map(\.width).max() ?? 0 + if hidpiTop > 0 { + ladder += HiDPIService.shared + .smoothScaledLogicalSizes(nativeWidth: nativeW, nativeHeight: nativeH) + .filter { $0.width > hidpiTop && $0.width < nativeW } + .map { DisplayMode(id: -Int32($0.width), width: $0.width, height: $0.height, + pixelWidth: $0.width * 2, pixelHeight: $0.height * 2, + refreshRate: 0, isHiDPI: true, isNative: false) } + } + } + return ladder.sorted { $0.width == $1.width ? $0.height < $1.height : $0.width < $1.width } } /// Subtitle for the row while off: what smooth scaling does (the decision point), plus the @@ -422,6 +458,11 @@ final class DisplayModeController: ObservableObject { let i = Int(sliderIndex.rounded()) guard modes.indices.contains(i) else { return } let target = modes[i] + // Already rendering this synthetic size? Nothing to do. The id guard + // below can't catch it: while mirrored, currentMode carries the virtual + // display's real (positive) mode id, never the synthetic negative one. + if target.id < 0, let cur = currentMode, + cur.width == target.width, cur.height == target.height { return } // Keep the current refresh rate at that logical size and scaling kind when offered. // Tolerant match: CG reports fractional rates (59.94) where the CGS-surfaced modes // carry whole Hz. From 04442c1518eaba6f2ae4e06d7f3fae1c197e014c Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 19 Aug 2026 01:11:38 +0200 Subject: [PATCH 5/7] feat: mirror-mode teardown and recovery paths (#65) Unplugging a mirrored panel or losing the virtual master drops the bookkeeping (and orphan virtual) via refreshDisplays; a stray mirror virtual from a crashed session gets its panel unmirrored on every refresh (MIRR serial marker identifies it); wake's saved-mode reapply skips mirrored displays so it cannot fight the mirror; and a re-apply re-arms a mirror that a wake or WindowServer reset silently dropped. --- Crisp/Services/DisplayManager.swift | 6 ++++ Crisp/Services/MirroredModeService.swift | 46 +++++++++++++++++++++++- Crisp/Services/ResolutionService.swift | 5 +++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/Crisp/Services/DisplayManager.swift b/Crisp/Services/DisplayManager.swift index 256ed6d..0976c3f 100644 --- a/Crisp/Services/DisplayManager.swift +++ b/Crisp/Services/DisplayManager.swift @@ -160,6 +160,9 @@ class DisplayManager: ObservableObject { GammaService.shared.invalidate(for: $0) BrightnessBoostService.shared.invalidate(for: $0) VolumeService.shared.invalidate(for: $0) + // A mirrored physical unplugged, or its virtual master dying, must + // drop the mirror bookkeeping (and the orphan virtual with it). + MirroredModeService.shared.handleDisplayRemoval($0) } // Diff-based refresh: keep existing DisplayInfo objects (preserves @Published state) @@ -241,6 +244,9 @@ class DisplayManager: ObservableObject { // skips any display whose soft reconnect is still mid-blink, so this can't race a // toggle's own retry loop even though the blink's reconfig events land here mid-toggle. Task { await PhysicalDisplayToggleService.shared.recoverStrandedSoftReconnect() } + // Same idea for mirror-mode strays: unmirror any panel a crashed session + // left mirroring one of our virtual displays. Cheap no-op otherwise. + MirroredModeService.shared.recoverStrandedMirrors() // A physical unplug bypasses disconnect()'s last-screen guard: internal disabled via // Crisp + external cable pulled = zero active displays, all black. Bring one back. PhysicalDisplayToggleService.shared.restoreIfNoActiveDisplay() diff --git a/Crisp/Services/MirroredModeService.swift b/Crisp/Services/MirroredModeService.swift index 3745d86..233f85c 100644 --- a/Crisp/Services/MirroredModeService.swift +++ b/Crisp/Services/MirroredModeService.swift @@ -63,7 +63,13 @@ final class MirroredModeService: ObservableObject { let physicalID = display.displayID if let vdID = active[physicalID]?.displayID { - return await setLooksLike(width: width, height: height, on: vdID) + guard await setLooksLike(width: width, height: height, on: vdID) else { return false } + // Re-arm the mirror if something dropped it under us (a wake or a + // WindowServer reset can collapse a mirror set without telling us). + if CGDisplayMirrorsDisplay(physicalID) != vdID { + return await MirrorService.shared.enableMirror(source: vdID, target: physicalID) + } + return true } guard let virtualDisplay = await createMirrorVirtual(for: display, @@ -101,6 +107,44 @@ final class MirroredModeService: ObservableObject { return unmirrored } + /// Reacts to a display leaving the online list (called from + /// DisplayManager.refreshDisplays). Two cases matter: the mirrored physical + /// was unplugged (nothing to unmirror anymore, let the orphan virtual die), + /// or our virtual died without us (WindowServer collapses the mirror set + /// itself when a master disappears; drop the stale entry so the state stays + /// truthful and the next slider move takes the normal create path). + func handleDisplayRemoval(_ removedID: CGDirectDisplayID) { + if active[removedID] != nil { + active.removeValue(forKey: removedID) + activePhysicalIDs.remove(removedID) + return + } + if let physicalID = active.first(where: { $0.value.displayID == removedID })?.key { + active.removeValue(forKey: physicalID) + activePhysicalIDs.remove(physicalID) + } + } + + /// Frees any physical display left mirroring a STRAY Crisp mirror virtual + /// (vendor stamp + MIRR serial) that this process does not own, i.e. one a + /// crashed session left behind. We hold no object for it so we cannot + /// destroy it, but unmirroring gives the panel its desktop back; the ghost + /// display stays hidden from the UI by the vendor-stamp filters. Called on + /// every refreshDisplays; a cheap no-op when nothing is stray. + func recoverStrandedMirrors() { + var count: UInt32 = 0 + CGGetOnlineDisplayList(0, nil, &count) + guard count > 0 else { return } + var ids = [CGDirectDisplayID](repeating: 0, count: Int(count)) + CGGetOnlineDisplayList(count, &ids, &count) + for id in ids where CGDisplayVendorNumber(id) == VirtualDisplayService.crispVirtualVendorID + && CGDisplaySerialNumber(id) == Self.mirrorSerialMarker + && !active.values.contains(where: { $0.displayID == id }) { + guard let target = MirrorService.shared.mirrorTargets(of: id) else { continue } + Task { await MirrorService.shared.disableMirror(displayID: target) } + } + } + /// Quit-path teardown. applicationWillTerminate cannot await, so the /// unmirror runs as a direct synchronous transaction; a rare WindowServer /// hang at quit beats leaving the panel mirrored. (Process death would diff --git a/Crisp/Services/ResolutionService.swift b/Crisp/Services/ResolutionService.swift index 13383c0..ceee698 100644 --- a/Crisp/Services/ResolutionService.swift +++ b/Crisp/Services/ResolutionService.swift @@ -49,6 +49,11 @@ final class ResolutionService: @unchecked Sendable { /// longer exists (mode list rebuilt, display swapped) it does nothing rather than forcing an /// off-aspect fallback. (w18z) func reapplySavedModeIfNeeded(for displayID: CGDirectDisplayID) { + // A mirrored beyond-cap size (#65) is not a saved mode: the physical + // reports the virtual master's looks-like mode, and a "correction" here + // would redirect to the virtual and fight the mirror. That state is + // MirroredModeService's to restore, not ours. + guard !MirroredModeService.shared.isActive(for: displayID) else { return } guard let saved = savedModes["\(displayID)"] else { return } // Already at the saved resolution? Nothing to do. From 26aa4d31e0b518b330cec06d0792a199ecc39e90 Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Tue, 25 Aug 2026 17:26:59 +0200 Subject: [PATCH 6/7] fix: declare backing + half-size mode pairs on the mirror virtual (#65) Backing-only declarations get WindowServer to mint looks-like twins that enumerate but then fail every apply; hit live on a 5K2K Dell U4919DW, where every beyond-cap stop errored out. Declaring the pair per stop, which is what the probe always did, gives twins that actually become current. Dropping to one refresh rate keeps the dense ladder under the ~400-object applySettings ceiling. Also log every mirror lifecycle step (create, reuse, restore, failure branches) under mirroredmode, since this path only misbehaves on real hardware. --- Crisp/Services/MirroredModeService.swift | 85 ++++++++++++++++++------ 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/Crisp/Services/MirroredModeService.swift b/Crisp/Services/MirroredModeService.swift index 233f85c..9536daf 100644 --- a/Crisp/Services/MirroredModeService.swift +++ b/Crisp/Services/MirroredModeService.swift @@ -1,5 +1,6 @@ import CoreGraphics import Foundation +import os.log /// True HiDPI past WindowServer's scaled-backing cap (issue #65). On 5K2K /// ultrawides macOS refuses scaled backings wider than ~6720px, so looks-like @@ -20,6 +21,11 @@ final class MirroredModeService: ObservableObject { static let shared = MirroredModeService() private init() {} + /// Mirror mode only misbehaves on live hardware, so every failure branch + /// logs; `log stream --predicate 'subsystem == "com.crisp.app"'` while + /// reproducing tells which step broke without a debug build. + private static let log = Logger(subsystem: "com.crisp.app", category: "mirroredmode") + /// Live CGVirtualDisplay per mirrored physical display. Releasing a value /// is what destroys its virtual display, so this dictionary IS the state. private var active: [CGDirectDisplayID: CGVirtualDisplay] = [:] @@ -63,27 +69,41 @@ final class MirroredModeService: ObservableObject { let physicalID = display.displayID if let vdID = active[physicalID]?.displayID { - guard await setLooksLike(width: width, height: height, on: vdID) else { return false } + guard await setLooksLike(width: width, height: height, on: vdID) else { + Self.log.error("apply \(width)x\(height): setLooksLike failed on existing virtual \(vdID)") + return false + } // Re-arm the mirror if something dropped it under us (a wake or a // WindowServer reset can collapse a mirror set without telling us). if CGDisplayMirrorsDisplay(physicalID) != vdID { + Self.log.info("apply \(width)x\(height): reused virtual \(vdID), re-arming mirror") return await MirrorService.shared.enableMirror(source: vdID, target: physicalID) } + Self.log.info("apply \(width)x\(height): reused virtual \(vdID)") return true } guard let virtualDisplay = await createMirrorVirtual(for: display, mustInclude: (width, height)) - else { return false } + else { + Self.log.error("apply \(width)x\(height): createMirrorVirtual failed") + return false + } active[physicalID] = virtualDisplay activePhysicalIDs.insert(physicalID) - guard await setLooksLike(width: width, height: height, on: virtualDisplay.displayID), - await MirrorService.shared.enableMirror(source: virtualDisplay.displayID, + guard await setLooksLike(width: width, height: height, on: virtualDisplay.displayID) else { + Self.log.error("apply \(width)x\(height): setLooksLike failed on fresh virtual \(virtualDisplay.displayID)") + await restore(physicalID: physicalID) + return false + } + guard await MirrorService.shared.enableMirror(source: virtualDisplay.displayID, target: physicalID) else { + Self.log.error("apply \(width)x\(height): enableMirror failed (virtual \(virtualDisplay.displayID) -> physical \(physicalID))") await restore(physicalID: physicalID) return false } + Self.log.info("apply \(width)x\(height): mirrored physical \(physicalID) onto virtual \(virtualDisplay.displayID)") return true } @@ -99,6 +119,7 @@ final class MirroredModeService: ObservableObject { @discardableResult func restore(physicalID: CGDirectDisplayID) async -> Bool { guard let vdID = active[physicalID]?.displayID else { return true } + Self.log.info("restore: unmirroring physical \(physicalID), destroying virtual \(vdID)") let unmirrored = await MirrorService.shared.disableMirror(displayID: physicalID) // Dropping the last reference starts WindowServer's async teardown. active.removeValue(forKey: physicalID) @@ -167,11 +188,12 @@ final class MirroredModeService: ObservableObject { /// Builds the mirror virtual for a physical display: stable identity (so /// macOS's "what do you want to show" picker appears at most once per /// monitor and its answer is remembered), the panel's physical size (sane - /// PPI), and backing-only mode declarations for every beyond-cap stop. - /// Backing-only because a dense backing set makes WindowServer mint the - /// looks-like HiDPI twins itself, and declaring the half-size twins too - /// would run into the ~400-object ceiling where applySettings rejects the - /// whole set (both found empirically with the probe). + /// PPI), and a backing + half-size mode PAIR for every beyond-cap stop. + /// The pair is mandatory: backing-only declarations get WindowServer to + /// mint enumerable looks-like twins, but those twins fail every apply + /// (verified live on a 5K2K panel). One refresh rate keeps the dense + /// ladder under the ~400-object ceiling where applySettings rejects the + /// whole set. private func createMirrorVirtual(for display: DisplayInfo, mustInclude: (width: Int, height: Int)) async -> CGVirtualDisplay? { let (nativeW, nativeH) = display.nativeResolution @@ -213,26 +235,40 @@ final class MirroredModeService: ObservableObject { stops.append((width: mustInclude.width, height: mustInclude.height)) } - // The panel's own refresh rate plus 60 as the safe floor. + // One rate only (the panel's own, 60 when unreadable): every stop costs + // TWO mode objects below, and a second rate would put a dense ladder + // past the ~400-object ceiling where applySettings rejects the set. let panelRate = display.currentDisplayMode?.refreshRate ?? 60 - var rates: [Double] = [60] - if panelRate > 0, abs(panelRate - 60) >= 1 { rates.append(panelRate) } + let rate: Double = panelRate > 0 ? panelRate : 60 + // Declare BOTH the 2x backing and the half-size pixel mode per stop + // (the probe's recipe). Backing-only declarations look sufficient, + // WindowServer mints enumerable looks-like twins for them, but those + // twins refuse to apply: CGConfigureDisplayWithDisplayMode fails on + // every attempt (found live on a 5K2K panel, 2026-08-25). Only the + // declared pair yields a twin that can actually become current. var modes: [CGVirtualDisplayMode] = [] for stop in stops where stop.width >= 1 && stop.height >= 1 { - for rate in rates { - modes.append(CGVirtualDisplayMode(width: UInt(stop.width * 2), - height: UInt(stop.height * 2), - refreshRate: rate)) - } + modes.append(CGVirtualDisplayMode(width: UInt(stop.width * 2), + height: UInt(stop.height * 2), + refreshRate: rate)) + modes.append(CGVirtualDisplayMode(width: UInt(stop.width), + height: UInt(stop.height), + refreshRate: rate)) + } + guard !modes.isEmpty else { + Self.log.error("createMirrorVirtual: no beyond-cap stops (ladder top \(hidpiTop), native \(nativeW)x\(nativeH))") + return nil } - guard !modes.isEmpty else { return nil } let settings = CGVirtualDisplaySettings() settings.hiDPI = true settings.modes = modes - guard let virtualDisplay = CGVirtualDisplay(descriptor: descriptor) else { return nil } + guard let virtualDisplay = CGVirtualDisplay(descriptor: descriptor) else { + Self.log.error("createMirrorVirtual: CGVirtualDisplay init returned nil") + return nil + } // apply blocks on WindowServer IPC; off-main with a timeout like every // CG transaction (same as VirtualDisplayService.create). let vd = virtualDisplay @@ -240,7 +276,11 @@ final class MirroredModeService: ObservableObject { let applied: Bool = await CGHelpers.runWithTimeout(seconds: 10, fallback: false) { vd.apply(s) } - guard applied, virtualDisplay.displayID != kCGNullDirectDisplay else { return nil } + guard applied, virtualDisplay.displayID != kCGNullDirectDisplay else { + Self.log.error("createMirrorVirtual: applySettings \(applied ? "ok but null displayID" : "failed") (\(modes.count) modes)") + return nil + } + Self.log.info("createMirrorVirtual: virtual \(virtualDisplay.displayID) up, \(modes.count) modes declared") return virtualDisplay } @@ -265,6 +305,11 @@ final class MirroredModeService: ObservableObject { else { continue } if await ResolutionService.applyModeSync(target, on: virtualID) { return true } } + // Distinguish "twin never enumerated" from "apply kept failing". + let options2 = [kCGDisplayShowDuplicateLowResolutionModes as String: true] as CFDictionary + let seen = (CGDisplayCopyAllDisplayModes(virtualID, options2) as? [CGDisplayMode]) ?? [] + let hasTwin = seen.contains { $0.width == width && $0.height == height && $0.pixelWidth == width * 2 } + Self.log.error("setLooksLike \(width)x\(height) on \(virtualID): gave up after 10 attempts, \(seen.count) modes enumerated, HiDPI twin \(hasTwin ? "present (apply failed)" : "never minted")") return false } From a3f1926fe868916f752e16aa6047abe0fc76b0be Mon Sep 17 00:00:00 2001 From: Didrik Galteland Date: Wed, 26 Aug 2026 00:04:16 +0200 Subject: [PATCH 7/7] fix: name the mirror virtual " (Crisp)" (#65) While mirrored, Control Center and the first-run Extend picker list the hidden virtual. It carried the panel's own name, so macOS showed a confusing duplicate ("Dell U4919DW (2)"). A marker makes the entry read as deliberate, and makes "choose Extend for the entry marked Crisp" an unambiguous instruction for the one prompt users must answer. --- Crisp/Resources/Localizable.xcstrings | 10 ++++++++++ Crisp/Services/MirroredModeService.swift | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Crisp/Resources/Localizable.xcstrings b/Crisp/Resources/Localizable.xcstrings index 5f790e5..a8c6432 100644 --- a/Crisp/Resources/Localizable.xcstrings +++ b/Crisp/Resources/Localizable.xcstrings @@ -44,6 +44,16 @@ } } }, + "%@ (Crisp)" : { + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@(Crisp)" + } + } + } + }, "%@ color" : { "localizations" : { "zh-Hans" : { diff --git a/Crisp/Services/MirroredModeService.swift b/Crisp/Services/MirroredModeService.swift index 9536daf..1c4dfe7 100644 --- a/Crisp/Services/MirroredModeService.swift +++ b/Crisp/Services/MirroredModeService.swift @@ -216,7 +216,11 @@ final class MirroredModeService: ObservableObject { : CGSize(width: Double(nativeW) / 110.0 * 25.4, height: Double(nativeH) / 110.0 * 25.4) descriptor.maxPixelsWide = UInt32(nativeW * 2) descriptor.maxPixelsHigh = UInt32(nativeH * 2) - descriptor.name = display.name + // Panel name plus a marker: system UI lists the virtual while + // mirrored (Control Center, the first-run Extend picker), and a + // distinct name reads as a feature where a duplicate "Name (2)" + // reads as a glitch. + descriptor.name = String(localized: "\(display.name) (Crisp)") descriptor.vendorID = VirtualDisplayService.crispVirtualVendorID // Stable per monitor; the serial carries the mirror marker. Two // identical monitors mirroring at once would collide, accepted edge.