Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ DerivedData/
Crisp.xcodeproj/
Crisp.dmg
.superpowers/
.worktrees/
Crisp-bin

# Working/internal docs kept local-only (research, release-note drafts)
Expand Down
37 changes: 37 additions & 0 deletions Crisp/Models/DDCOperationGeneration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import CoreGraphics

struct DDCOperationGeneration {
struct Token: Equatable {
let topology: UInt64
let request: UInt64
}

private var topologies: [CGDirectDisplayID: UInt64] = [:]
private var requests: [CGDirectDisplayID: UInt64] = [:]

func currentToken(for displayID: CGDirectDisplayID) -> Token {
Token(
topology: topologies[displayID, default: 0],
request: requests[displayID, default: 0]
)
}

mutating func nextRequest(for displayID: CGDirectDisplayID) -> Token {
requests[displayID, default: 0] &+= 1
return currentToken(for: displayID)
}

mutating func invalidate(displayID: CGDirectDisplayID) {
topologies[displayID, default: 0] &+= 1
requests[displayID, default: 0] &+= 1
}

func isCurrentTopology(_ token: Token, for displayID: CGDirectDisplayID) -> Bool {
token.topology == topologies[displayID, default: 0]
}

func isLatestRequest(_ token: Token, for displayID: CGDirectDisplayID) -> Bool {
isCurrentTopology(token, for: displayID)
&& token.request == requests[displayID, default: 0]
}
}
22 changes: 22 additions & 0 deletions Crisp/Models/DDCOperationQueuePool.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Foundation
import CoreGraphics

/// Keeps DDC operations serial per physical display without coupling displays.
final class DDCOperationQueuePool: @unchecked Sendable {
private let lock = NSLock()
// ponytail: keep this small map for the process lifetime so a reused display ID
// cannot overlap an in-flight operation on a second queue.
private var queues: [CGDirectDisplayID: DispatchQueue] = [:]

func queue(for displayID: CGDirectDisplayID) -> DispatchQueue {
lock.withLock {
if let queue = queues[displayID] { return queue }
let queue = DispatchQueue(
label: "com.crisp.ddc.\(displayID)",
qos: .userInitiated
)
queues[displayID] = queue
return queue
}
}
}
21 changes: 18 additions & 3 deletions Crisp/Models/DDCServiceMatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,20 @@ enum DDCServiceMatcher {
/// A display's vendor/product/serial identity mirrors the IORegistry
/// `ProductAttributes` (`LegacyManufacturerID` / `ProductID` / `SerialNumber`)
/// which line up with `CGDisplayVendorNumber` / `CGDisplayModelNumber` /
/// `CGDisplaySerialNumber` for the same physical display.
/// `CGDisplaySerialNumber` for the same physical display. Location is the
/// stable CoreDisplay/IORegistry path when macOS exposes it.
struct Identity: Equatable {
let vendor: UInt32
let product: UInt32
let serial: UInt32
let location: String?

init(vendor: UInt32, product: UInt32, serial: UInt32, location: String? = nil) {
self.vendor = vendor
self.product = product
self.serial = serial
self.location = location
}
}

/// The outcome of a matching pass.
Expand Down Expand Up @@ -53,11 +62,17 @@ enum DDCServiceMatcher {
var usedDisplays = Set<CGDirectDisplayID>()
var unmatched: [Int] = []

// Strategy 1: identity matching (vendor+product+serial, then vendor+product).
// Strategy 1: stable location, then non-zero serial, then model identity.
for i in services.indices {
guard let idty = services[i] else { unmatched.append(i); continue }
let exact = displays.first {
let byLocation = displays.first {
guard let location = idty.location, !location.isEmpty else { return false }
return !usedDisplays.contains($0.id) && $0.identity.location == location
}
let exact = byLocation ?? displays.first {
!usedDisplays.contains($0.id)
&& idty.serial != 0
&& $0.identity.serial != 0
&& $0.identity.vendor == idty.vendor
&& $0.identity.product == idty.product
&& $0.identity.serial == idty.serial
Expand Down
Loading