Ariver
2026-06-13 6983a4ed2137bf1a8377e5d1cf82183e02e617f8
C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
@@ -162,16 +162,12 @@
        let privateActivationOutcome = activateViaPrivateWindowServerAPI(window)
        let didPrivatelyActivate = privateActivationOutcome?.succeeded == true
        let axWindow = AXWindowMetadataReader
            .metadata(appCategorizer: appCategorizer)
            .first { metadata in
                if let windowID = metadata.windowID {
                    return windowID == window.id
                }
                return metadata.processIdentifier == window.app.processIdentifier
                    && metadata.title == window.title
            }
        let axWindow = axWindow(
            for: window,
            in: AXWindowMetadataReader.metadata(appCategorizer: appCategorizer),
            operation: "windowActivation",
            matchAttempt: "initial"
        )
        DevelopmentDiagnostics.log("windowActivation.activate.axLookup", [
            "windowID": window.id,
@@ -273,6 +269,15 @@
        var matchedAXWindow = axWindow(for: window, in: axMetadata)
        var finalAXMetadata = axMetadata
        let shouldPreferFocusedPageClose = matchedAXWindow?.windowID != window.id
        if shouldPreferFocusedPageClose,
           let focusedPageCloseResult = closeFocusedPageViaCommandW(
            window: window,
            reason: matchedAXWindow == nil ? "axMissing" : "nonDirectAXMatch"
           ) {
            return focusedPageCloseResult
        }
        if matchedAXWindow == nil {
            Thread.sleep(forTimeInterval: 0.12)
            finalAXMetadata = AXWindowMetadataReader.metadata(appCategorizer: appCategorizer)
@@ -289,6 +294,14 @@
                "matchingSizeCandidateCount": matchingAXSizeCandidateCount(for: window, in: finalAXMetadata)
            ])
            return .windowNotFound
        }
        if matchedAXWindow.windowID != window.id,
           let focusedPageCloseResult = closeFocusedPageViaCommandW(
            window: window,
            reason: "retryNonDirectAXMatch"
           ) {
            return focusedPageCloseResult
        }
        guard let closeButton = closeButton(for: matchedAXWindow) else {
@@ -364,13 +377,82 @@
        }
    }
    private func closeFocusedPageViaCommandW(
        window: AlignerWindow,
        reason: String
    ) -> WindowCloseResult? {
        guard window.identifierSource == .cgWindow,
              let processIdentifier = window.app.processIdentifier
        else {
            DevelopmentDiagnostics.log("windowClose.commandW.skipped", [
                "windowID": window.id,
                "reason": reason,
                "identifierSource": String(describing: window.identifierSource),
                "hasPID": window.app.processIdentifier != nil
            ])
            return nil
        }
        guard let activationOutcome = activateViaPrivateWindowServerAPI(window),
              activationOutcome.succeeded
        else {
            DevelopmentDiagnostics.log("windowClose.commandW.activationFailed", [
                "windowID": window.id,
                "pid": processIdentifier,
                "reason": reason
            ])
            return nil
        }
        Thread.sleep(forTimeInterval: 0.08)
        guard postCommandW(to: processIdentifier) else {
            DevelopmentDiagnostics.log("windowClose.commandW.postFailed", [
                "windowID": window.id,
                "pid": processIdentifier,
                "reason": reason
            ])
            return .failed("commandWPostFailed")
        }
        DevelopmentDiagnostics.log("windowClose.commandW.requested", [
            "windowID": window.id,
            "pid": processIdentifier,
            "reason": reason
        ])
        return .requested
    }
    private func postCommandW(to processIdentifier: Int32) -> Bool {
        let source = CGEventSource(stateID: .combinedSessionState)
        guard let keyDown = CGEvent(
            keyboardEventSource: source,
            virtualKey: Self.commandWVirtualKeyCode,
            keyDown: true
        ),
              let keyUp = CGEvent(
                keyboardEventSource: source,
                virtualKey: Self.commandWVirtualKeyCode,
                keyDown: false
              )
        else {
            return false
        }
        keyDown.flags = .maskCommand
        keyUp.flags = .maskCommand
        keyDown.postToPid(processIdentifier)
        keyUp.postToPid(processIdentifier)
        return true
    }
    private func axWindow(
        for window: AlignerWindow,
        in axMetadata: [AXWindowMetadata],
        operation: String = "windowClose",
        matchAttempt: String = "initial"
    ) -> AXWindowMetadata? {
        if let directMatch = axMetadata.first(where: { $0.windowID == window.id }) {
            DevelopmentDiagnostics.log("windowClose.axMatch.directWindowID", [
            DevelopmentDiagnostics.log("\(operation).axMatch.directWindowID", [
                "windowID": window.id,
                "pid": window.app.processIdentifier,
                "attempt": matchAttempt
@@ -382,12 +464,35 @@
            return nil
        }
        if let geometryFingerprint = axWindowGeometryFingerprint(for: window) {
            let geometryMatches = axMetadata.filter { metadata in
                AXWindowGeometryFingerprint(metadata) == geometryFingerprint
            }
            if geometryMatches.count == 1 {
                DevelopmentDiagnostics.log("\(operation).axMatch.geometryFingerprint", [
                    "windowID": window.id,
                    "pid": processIdentifier,
                    "attempt": matchAttempt
                ])
                return geometryMatches[0]
            }
            if geometryMatches.count > 1 {
                DevelopmentDiagnostics.log("\(operation).axMatch.geometryFingerprintAmbiguous", [
                    "windowID": window.id,
                    "pid": processIdentifier,
                    "candidateCount": geometryMatches.count,
                    "attempt": matchAttempt
                ])
            }
        }
        if let cgFingerprint = cgWindowFingerprint(for: window) {
            let fingerprintMatches = axMetadata.filter { metadata in
                AXWindowFingerprint(metadata) == cgFingerprint
            }
            if fingerprintMatches.count == 1 {
                DevelopmentDiagnostics.log("windowClose.axMatch.fingerprint", [
                DevelopmentDiagnostics.log("\(operation).axMatch.fingerprint", [
                    "windowID": window.id,
                    "pid": processIdentifier,
                    "attempt": matchAttempt
@@ -396,10 +501,11 @@
            }
            if fingerprintMatches.count > 1 {
                DevelopmentDiagnostics.log("windowClose.axMatch.fingerprintAmbiguous", [
                DevelopmentDiagnostics.log("\(operation).axMatch.fingerprintAmbiguous", [
                    "windowID": window.id,
                    "pid": processIdentifier,
                    "candidateCount": fingerprintMatches.count
                    "candidateCount": fingerprintMatches.count,
                    "attempt": matchAttempt
                ])
            }
        }
@@ -409,8 +515,8 @@
            metadata.processIdentifier == processIdentifier
                && normalizedWindowTitle(metadata.title ?? "") == normalizedTitle
        }
        if titleMatches.count == 1 {
            DevelopmentDiagnostics.log("windowClose.axMatch.normalizedTitle", [
        if !normalizedTitle.isEmpty, titleMatches.count == 1 {
            DevelopmentDiagnostics.log("\(operation).axMatch.normalizedTitle", [
                "windowID": window.id,
                "pid": processIdentifier,
                "attempt": matchAttempt
@@ -424,7 +530,7 @@
            processIdentifier: processIdentifier,
            in: axMetadata
           ) {
            DevelopmentDiagnostics.log("windowClose.axMatch.finderFallback", [
            DevelopmentDiagnostics.log("\(operation).axMatch.finderFallback", [
                "windowID": window.id,
                "pid": processIdentifier,
                "attempt": matchAttempt
@@ -433,10 +539,11 @@
        }
        if titleMatches.count > 1 {
            DevelopmentDiagnostics.log("windowClose.axMatch.titleAmbiguous", [
            DevelopmentDiagnostics.log("\(operation).axMatch.titleAmbiguous", [
                "windowID": window.id,
                "pid": processIdentifier,
                "candidateCount": titleMatches.count
                "candidateCount": titleMatches.count,
                "attempt": matchAttempt
            ])
        }
@@ -475,6 +582,29 @@
            title: normalizedWindowTitle(title(rawWindow: rawWindow, axWindow: nil)),
            size: bounds(rawWindow[kCGWindowBounds as String]).size
        )
    }
    private func axWindowGeometryFingerprint(for window: AlignerWindow) -> AXWindowGeometryFingerprint? {
        guard let processIdentifier = window.app.processIdentifier else { return nil }
        let frame = cgWindowFrame(for: window) ?? window.frame
        return AXWindowGeometryFingerprint(
            processIdentifier: processIdentifier,
            title: normalizedWindowTitle(window.title),
            frame: frame
        )
    }
    private func cgWindowFrame(for window: AlignerWindow) -> CGRect? {
        guard let rawWindows = CGWindowListCopyWindowInfo(
            [.optionIncludingWindow],
            CGWindowID(window.id)
        ) as? [[String: Any]],
              let rawWindow = rawWindows.first
        else {
            return nil
        }
        return bounds(rawWindow[kCGWindowBounds as String])
    }
    private func matchingAXTitleCandidateCount(
@@ -642,13 +772,14 @@
                return nil
            }
            let rawBounds = bounds(rawWindow[kCGWindowBounds as String])
            let ownerPID = int32Value(rawWindow[kCGWindowOwnerPID as String])
            let runningApplication = ownerPID.flatMap(NSRunningApplication.init(processIdentifier:))
            let rawFingerprint = ownerPID.map {
                AXWindowFingerprint(
                    processIdentifier: $0,
                    title: title(rawWindow: rawWindow, axWindow: nil),
                    size: bounds(rawWindow[kCGWindowBounds as String]).size
                    size: rawBounds.size
                )
            }
            let directAXWindow = axMetadata[windowID]
@@ -668,7 +799,8 @@
                app: app,
                activationPolicy: activationPolicy(from: runningApplication),
                title: title(rawWindow: rawWindow, axWindow: axWindow),
                size: bounds(rawWindow[kCGWindowBounds as String]).size,
                size: rawBounds.size,
                frame: rawBounds,
                subrole: axWindow?.subrole ?? .standard,
                isMinimized: axWindow?.isMinimized ?? false,
                isFullscreen: !Set(spaceIDs).isDisjoint(with: fullscreenSpaceIDs),
@@ -748,6 +880,7 @@
                title: axWindow.title ?? "",
                identifierSource: identifierSource,
                size: axWindow.size,
                frame: axWindow.frame,
                subrole: axWindow.subrole,
                isMinimized: axWindow.isMinimized,
                isFullscreen: !Set(spaceIDs).isDisjoint(with: fullscreenSpaceIDs),
@@ -981,6 +1114,7 @@
        }
    }
    private static let commandWVirtualKeyCode: CGKeyCode = 13
}
private struct AXWindowMetadata {
@@ -990,6 +1124,7 @@
    let title: String?
    let isMinimized: Bool
    let size: CGSize
    let frame: CGRect?
    let subrole: AlignerWindowSubrole
    let hasExplicitSubrole: Bool
    let isInteractable: Bool
@@ -1049,6 +1184,34 @@
    }
}
private struct AXWindowGeometryFingerprint: Hashable {
    let processIdentifier: Int32
    let title: String
    let x: Int
    let y: Int
    let width: Int
    let height: Int
    init?(processIdentifier: Int32, title: String, frame: CGRect?) {
        guard let frame else { return nil }
        self.processIdentifier = processIdentifier
        self.title = title
        self.x = Int(frame.origin.x.rounded())
        self.y = Int(frame.origin.y.rounded())
        self.width = Int(frame.size.width.rounded())
        self.height = Int(frame.size.height.rounded())
    }
    init?(_ metadata: AXWindowMetadata) {
        self.init(
            processIdentifier: metadata.processIdentifier,
            title: normalizedWindowTitle(metadata.title ?? ""),
            frame: metadata.frame
        )
    }
}
private struct RestorableAXWindow {
    let processIdentifier: Int32
    let element: AXUIElement
@@ -1081,13 +1244,16 @@
            for (windowIndex, window) in windows.enumerated() {
                let subrole = subrole(for: window)
                let size = size(for: window)
                let position = position(for: window)
                result.append(AXWindowMetadata(
                    windowID: windowID(for: window),
                    app: app,
                    activationPolicy: activationPolicy(from: application),
                    title: title(for: window),
                    isMinimized: boolAttribute(kAXMinimizedAttribute as String, for: window) ?? false,
                    size: size(for: window),
                    size: size,
                    frame: position.map { CGRect(origin: $0, size: size) },
                    subrole: subrole.value,
                    hasExplicitSubrole: subrole.isExplicit,
                    isInteractable: boolAttribute(kAXEnabledAttribute as String, for: window) ?? true,
@@ -1214,6 +1380,23 @@
        return size
    }
    private static func position(for window: AXUIElement) -> CGPoint? {
        var value: CFTypeRef?
        guard AXUIElementCopyAttributeValue(window, kAXPositionAttribute as CFString, &value) == .success,
              let axValue = value,
              CFGetTypeID(axValue) == AXValueGetTypeID()
        else {
            return nil
        }
        var position = CGPoint.zero
        guard AXValueGetValue(axValue as! AXValue, .cgPoint, &position) else {
            return nil
        }
        return position
    }
    private static func subrole(for window: AXUIElement) -> (value: AlignerWindowSubrole, isExplicit: Bool) {
        var value: CFTypeRef?
        guard AXUIElementCopyAttributeValue(window, kAXSubroleAttribute as CFString, &value) == .success,