Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Crisp/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
10 changes: 10 additions & 0 deletions Crisp/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@
}
}
},
"%@ (Crisp)" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%@(Crisp)"
}
}
}
},
"%@ color" : {
"localizations" : {
"zh-Hans" : {
Expand Down
6 changes: 6 additions & 0 deletions Crisp/Services/DisplayManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down
331 changes: 331 additions & 0 deletions Crisp/Services/MirroredModeService.swift

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions Crisp/Services/ResolutionService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
51 changes: 46 additions & 5 deletions Crisp/Views/DisplayModeListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<String>()
return display.availableModes
var ladder = display.availableModes
.filter {
guard DisplayModeGeometry.hasSameOrientation(
width: $0.width, height: $0.height, as: nativeW, nativeH
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
246 changes: 246 additions & 0 deletions scripts/mirror-hidpi-probe.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
// 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 <WxH> [displayID]
// <WxH> logical ("looks like") size, e.g. 3840x1080 on a 5K2K panel
// [displayID] physical display to mirror; default: first external, else main
//
// 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

let args = CommandLine.arguments
guard args.count >= 2 else {
fail("usage: swift scripts/mirror-hidpi-probe.swift <WxH> [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")
}
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") }

// 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<AnyObject> {
let imp = class_getMethodImplementation(object_getClass(cls), NSSelectorFromString("alloc"))
let fn = unsafeBitCast(imp, to: (@convention(c) (AnyClass, Selector) -> Unmanaged<AnyObject>).self)
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<AnyObject>).self)
return fn(alloc(modeCls).takeUnretainedValue(), sel, UInt(pixelW), UInt(pixelH), hz).takeRetainedValue()
}

let desc = descCls.init()
// 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")
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 settings = settingsCls.init()
settings.setValue(true, forKey: "hiDPI")
var modeObjs: [AnyObject] = []
// 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)) // half-size pixel mode
}
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<AnyObject>?).self)
// Kept in a global so teardown() can release it; releasing destroys the display.
var virtualDisplay: AnyObject? = initFn(alloc(displayCls).takeUnretainedValue(), 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: - 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 {
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 CGDisplayMirrorsDisplay(physical) != kCGNullDirectDisplay,
!setMirror(physical, master: kCGNullDirectDisplay) { print(" unmirror FAILED") }
virtualDisplay = nil // last strong reference: WindowServer removes the display
usleep(1_500_000)
print(onlineDisplays().contains(vdID)
? " virtual display STILL ONLINE (stale CLI cache is possible; verify in System Settings)"
: " 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 asynchronously; retry briefly.
var hidpiMode: CGDisplayMode?
for attempt in 0..<20 {
if attempt > 0 { usleep(500_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()
Loading