From 71acd11e8d5349672da8b1b6bb254e9a25ff4e51 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Wed, 10 Jun 2026 11:59:13 +0800
Subject: [PATCH] Fix Space Lane tile visuals
---
C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift | 410 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 401 insertions(+), 9 deletions(-)
diff --git a/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift b/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
index 773abed..bba0912 100644
--- a/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
+++ b/C1.source/Sources/Aligner/Infrastructure/Windows/CGWindowAXWindowService.swift
@@ -1,10 +1,10 @@
import AppKit
-import ApplicationServices
+@preconcurrency import ApplicationServices
import CoreGraphics
import Foundation
import AlignerCore
-final class CGWindowAXWindowService: WindowServiceProtocol, WindowRestorationServiceProtocol, WindowActivationServiceProtocol {
+final class CGWindowAXWindowService: WindowServiceProtocol, WindowRestorationServiceProtocol, WindowActivationServiceProtocol, WindowCloseServiceProtocol {
weak var delegate: WindowServiceDelegate?
private let appCategorizer: any AppCategorizer
@@ -257,6 +257,286 @@
: .appActivatedOnly
}
+ func close(window: AlignerWindow) throws -> WindowCloseResult {
+ DevelopmentDiagnostics.log("windowClose.window.start", [
+ "windowID": window.id,
+ "identifierSource": String(describing: window.identifierSource),
+ "titleHash": DevelopmentDiagnostics.stableFingerprint(window.title),
+ "titleLength": window.title.count,
+ "titleIsEmpty": window.title.isEmpty,
+ "appName": window.app.name,
+ "bundleIdentifier": window.app.bundleIdentifier,
+ "pid": window.app.processIdentifier
+ ])
+
+ let axMetadata = AXWindowMetadataReader.metadata(appCategorizer: appCategorizer)
+ var matchedAXWindow = axWindow(for: window, in: axMetadata)
+ var finalAXMetadata = axMetadata
+
+ if matchedAXWindow == nil {
+ Thread.sleep(forTimeInterval: 0.12)
+ finalAXMetadata = AXWindowMetadataReader.metadata(appCategorizer: appCategorizer)
+ matchedAXWindow = axWindow(for: window, in: finalAXMetadata, matchAttempt: "retry")
+ }
+
+ guard let matchedAXWindow else {
+ DevelopmentDiagnostics.log("windowClose.window.axMissing", [
+ "windowID": window.id,
+ "appName": window.app.name,
+ "pid": window.app.processIdentifier,
+ "candidateCount": finalAXMetadata.filter { $0.processIdentifier == window.app.processIdentifier }.count,
+ "matchingTitleCandidateCount": matchingAXTitleCandidateCount(for: window, in: finalAXMetadata),
+ "matchingSizeCandidateCount": matchingAXSizeCandidateCount(for: window, in: finalAXMetadata)
+ ])
+ return .windowNotFound
+ }
+
+ guard let closeButton = closeButton(for: matchedAXWindow) else {
+ Thread.sleep(forTimeInterval: 0.12)
+ let retryMetadata = AXWindowMetadataReader.metadata(appCategorizer: appCategorizer)
+ if let retryAXWindow = axWindow(for: window, in: retryMetadata, matchAttempt: "closeButtonRetry"),
+ let retryCloseButton = closeButton(for: retryAXWindow) {
+ return performCloseButtonPress(
+ retryCloseButton,
+ window: window,
+ processIdentifier: retryAXWindow.processIdentifier,
+ attempt: "closeButtonRetry"
+ )
+ }
+ DevelopmentDiagnostics.log("windowClose.window.closeButtonMissing", [
+ "windowID": window.id,
+ "pid": matchedAXWindow.processIdentifier,
+ "appName": window.app.name
+ ])
+ return .windowNotFound
+ }
+
+ return performCloseButtonPress(
+ closeButton,
+ window: window,
+ processIdentifier: matchedAXWindow.processIdentifier,
+ attempt: "initial"
+ )
+ }
+
+ private func closeButton(for axWindow: AXWindowMetadata) -> AXUIElement? {
+ var closeButtonValue: CFTypeRef?
+ let closeButtonLookupResult = AXUIElementCopyAttributeValue(
+ axWindow.element,
+ kAXCloseButtonAttribute as CFString,
+ &closeButtonValue
+ )
+ guard closeButtonLookupResult == .success,
+ let closeButton = closeButtonValue,
+ CFGetTypeID(closeButton) == AXUIElementGetTypeID()
+ else {
+ DevelopmentDiagnostics.log("windowClose.window.closeButtonLookupFailed", [
+ "pid": axWindow.processIdentifier,
+ "axError": String(describing: closeButtonLookupResult)
+ ])
+ return nil
+ }
+
+ return (closeButton as! AXUIElement)
+ }
+
+ private func performCloseButtonPress(
+ _ closeButton: AXUIElement,
+ window: AlignerWindow,
+ processIdentifier: Int32,
+ attempt: String
+ ) -> WindowCloseResult {
+ let closeResult = AXUIElementPerformAction(closeButton, kAXPressAction as CFString)
+ DevelopmentDiagnostics.log("windowClose.window.result", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "attempt": attempt,
+ "axError": String(describing: closeResult)
+ ])
+
+ switch closeResult {
+ case .success:
+ return .requested
+ case .actionUnsupported, .notImplemented:
+ return .unsupported
+ default:
+ return .failed(String(describing: closeResult))
+ }
+ }
+
+ private func axWindow(
+ for window: AlignerWindow,
+ in axMetadata: [AXWindowMetadata],
+ matchAttempt: String = "initial"
+ ) -> AXWindowMetadata? {
+ if let directMatch = axMetadata.first(where: { $0.windowID == window.id }) {
+ DevelopmentDiagnostics.log("windowClose.axMatch.directWindowID", [
+ "windowID": window.id,
+ "pid": window.app.processIdentifier,
+ "attempt": matchAttempt
+ ])
+ return directMatch
+ }
+
+ guard let processIdentifier = window.app.processIdentifier else {
+ return nil
+ }
+
+ if let cgFingerprint = cgWindowFingerprint(for: window) {
+ let fingerprintMatches = axMetadata.filter { metadata in
+ AXWindowFingerprint(metadata) == cgFingerprint
+ }
+ if fingerprintMatches.count == 1 {
+ DevelopmentDiagnostics.log("windowClose.axMatch.fingerprint", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "attempt": matchAttempt
+ ])
+ return fingerprintMatches[0]
+ }
+
+ if fingerprintMatches.count > 1 {
+ DevelopmentDiagnostics.log("windowClose.axMatch.fingerprintAmbiguous", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "candidateCount": fingerprintMatches.count
+ ])
+ }
+ }
+
+ let normalizedTitle = normalizedWindowTitle(window.title)
+ let titleMatches = axMetadata.filter { metadata in
+ metadata.processIdentifier == processIdentifier
+ && normalizedWindowTitle(metadata.title ?? "") == normalizedTitle
+ }
+ if titleMatches.count == 1 {
+ DevelopmentDiagnostics.log("windowClose.axMatch.normalizedTitle", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "attempt": matchAttempt
+ ])
+ return titleMatches[0]
+ }
+
+ if window.app.bundleIdentifier == "com.apple.finder",
+ let finderMatch = finderFallbackAXWindow(
+ for: window,
+ processIdentifier: processIdentifier,
+ in: axMetadata
+ ) {
+ DevelopmentDiagnostics.log("windowClose.axMatch.finderFallback", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "attempt": matchAttempt
+ ])
+ return finderMatch
+ }
+
+ if titleMatches.count > 1 {
+ DevelopmentDiagnostics.log("windowClose.axMatch.titleAmbiguous", [
+ "windowID": window.id,
+ "pid": processIdentifier,
+ "candidateCount": titleMatches.count
+ ])
+ }
+
+ return nil
+ }
+
+ private func finderFallbackAXWindow(
+ for window: AlignerWindow,
+ processIdentifier: Int32,
+ in axMetadata: [AXWindowMetadata]
+ ) -> AXWindowMetadata? {
+ let normalizedTitle = normalizedWindowTitle(window.title)
+ guard !normalizedTitle.isEmpty else { return nil }
+
+ let finderCandidates = axMetadata.filter { metadata in
+ metadata.processIdentifier == processIdentifier
+ && normalizedWindowTitle(metadata.title ?? "") == normalizedTitle
+ && metadata.subrole == .standard
+ }
+ return finderCandidates.count == 1 ? finderCandidates[0] : nil
+ }
+
+ private func cgWindowFingerprint(for window: AlignerWindow) -> AXWindowFingerprint? {
+ guard let rawWindows = CGWindowListCopyWindowInfo(
+ [.optionIncludingWindow],
+ CGWindowID(window.id)
+ ) as? [[String: Any]],
+ let rawWindow = rawWindows.first,
+ let ownerPID = int32Value(rawWindow[kCGWindowOwnerPID as String])
+ else {
+ return nil
+ }
+
+ return AXWindowFingerprint(
+ processIdentifier: ownerPID,
+ title: normalizedWindowTitle(title(rawWindow: rawWindow, axWindow: nil)),
+ size: bounds(rawWindow[kCGWindowBounds as String]).size
+ )
+ }
+
+ private func matchingAXTitleCandidateCount(
+ for window: AlignerWindow,
+ in axMetadata: [AXWindowMetadata]
+ ) -> Int {
+ guard let processIdentifier = window.app.processIdentifier else { return 0 }
+ let normalizedTitle = normalizedWindowTitle(window.title)
+ return axMetadata.filter { metadata in
+ metadata.processIdentifier == processIdentifier
+ && normalizedWindowTitle(metadata.title ?? "") == normalizedTitle
+ }.count
+ }
+
+ private func matchingAXSizeCandidateCount(
+ for window: AlignerWindow,
+ in axMetadata: [AXWindowMetadata]
+ ) -> Int {
+ guard let processIdentifier = window.app.processIdentifier,
+ let cgFingerprint = cgWindowFingerprint(for: window)
+ else {
+ return 0
+ }
+
+ return axMetadata.filter { metadata in
+ metadata.processIdentifier == processIdentifier
+ && Int(metadata.size.width.rounded()) == cgFingerprint.width
+ && Int(metadata.size.height.rounded()) == cgFingerprint.height
+ }.count
+ }
+
+ func close(app: AlignerApp) throws -> WindowCloseResult {
+ DevelopmentDiagnostics.log("windowClose.app.start", [
+ "appName": app.name,
+ "bundleIdentifier": app.bundleIdentifier,
+ "pid": app.processIdentifier
+ ])
+
+ guard let processIdentifier = app.processIdentifier else {
+ DevelopmentDiagnostics.log("windowClose.app.missingPID", [
+ "appName": app.name,
+ "bundleIdentifier": app.bundleIdentifier
+ ])
+ return .appNotFound
+ }
+ guard let runningApplication = NSRunningApplication(processIdentifier: processIdentifier) else {
+ DevelopmentDiagnostics.log("windowClose.app.missingApplication", [
+ "appName": app.name,
+ "pid": processIdentifier
+ ])
+ return .appNotFound
+ }
+
+ let requested = runningApplication.terminate()
+ DevelopmentDiagnostics.log("windowClose.app.result", [
+ "appName": app.name,
+ "pid": processIdentifier,
+ "requested": requested
+ ])
+ return requested ? .requested : .failed("terminateFailed")
+ }
+
private func activateViaPrivateWindowServerAPI(_ window: AlignerWindow) -> PrivateWindowActivationOutcome? {
guard window.identifierSource == .cgWindow,
let processIdentifier = window.app.processIdentifier,
@@ -395,6 +675,7 @@
isGhost: isGhost(
rawWindow: rawWindow,
axWindow: axWindow,
+ app: app,
spaceIDs: spaceIDs,
hasAXBackedTitleDuplicate: hasAXBackedTitleDuplicate
),
@@ -583,6 +864,7 @@
private func isGhost(
rawWindow: [String: Any],
axWindow: AXWindowMetadata?,
+ app: AlignerApp,
spaceIDs: [UInt64],
hasAXBackedTitleDuplicate: Bool = false
) -> Bool {
@@ -596,10 +878,20 @@
return false
}
if axWindow != nil {
- return layer != 0 || alpha <= 0
+ if alpha <= 0 {
+ return true
+ }
+ if layer == 0 {
+ return false
+ }
+
+ return !isOnscreen
}
if hasAXBackedTitleDuplicate {
return true
+ }
+ if isKnownCGOnlyMainWindow(rawWindow: rawWindow, app: app) {
+ return false
}
if !spaceIDs.isEmpty, isLikelyFullscreenAuxiliaryCGWindow(bounds: rawBounds) {
return true
@@ -616,6 +908,40 @@
private func isLikelyFullscreenAuxiliaryCGWindow(bounds: CGRect) -> Bool {
bounds.width < 500 || bounds.height < 160
+ }
+
+ private func isKnownCGOnlyMainWindow(rawWindow: [String: Any], app: AlignerApp) -> Bool {
+ let layer = intValue(rawWindow[kCGWindowLayer as String]) ?? 0
+ let alpha = doubleValue(rawWindow[kCGWindowAlpha as String]) ?? 1
+ let isOnscreen = boolValue(rawWindow[kCGWindowIsOnscreen as String])
+ let title = stringValue(rawWindow[kCGWindowName as String]) ?? ""
+ let rawBounds = bounds(rawWindow[kCGWindowBounds as String])
+ let trimmedTitle = title.trimmingCharacters(in: .whitespacesAndNewlines)
+
+ if app.bundleIdentifier == "cc.ffitch.shottr" {
+ return isOnscreen
+ && alpha > 0
+ && trimmedTitle == "Shottr"
+ && rawBounds.width > WindowEnumerationPolicy.minimumVisibleWidth
+ && rawBounds.height > WindowEnumerationPolicy.minimumVisibleHeight
+ }
+
+ if app.bundleIdentifier == "com.apple.finder" {
+ return layer == 0
+ && alpha > 0
+ && !trimmedTitle.isEmpty
+ && rawBounds.width > WindowEnumerationPolicy.minimumVisibleWidth
+ && rawBounds.height > WindowEnumerationPolicy.minimumVisibleHeight
+ }
+
+ guard app.bundleIdentifier == "io.dcloud.HBuilderX" else { return false }
+
+ return layer == 0
+ && alpha > 0
+ && !isOnscreen
+ && !trimmedTitle.isEmpty
+ && rawBounds.width > WindowEnumerationPolicy.minimumVisibleWidth
+ && rawBounds.height > WindowEnumerationPolicy.minimumVisibleHeight
}
private func isDesktopElement(rawWindow: [String: Any], app: AlignerApp) -> Bool {
@@ -654,6 +980,7 @@
return "activationFailed"
}
}
+
}
private struct AXWindowMetadata {
@@ -749,12 +1076,8 @@
let app = app(from: application, appCategorizer: appCategorizer)
let appElement = AXUIElementCreateApplication(application.processIdentifier)
- var windowsValue: CFTypeRef?
- guard AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &windowsValue) == .success,
- let windows = windowsValue as? [AXUIElement]
- else {
- continue
- }
+ let windows = windows(for: appElement)
+ guard !windows.isEmpty else { continue }
for (windowIndex, window) in windows.enumerated() {
let subrole = subrole(for: window)
@@ -773,6 +1096,38 @@
element: window
))
}
+ }
+
+ return result
+ }
+
+ private static func windows(for appElement: AXUIElement) -> [AXUIElement] {
+ var result: [AXUIElement] = []
+ var seen = Set<AXWindowElementKey>()
+
+ func append(_ window: AXUIElement) {
+ let key = AXWindowElementKey(window)
+ guard !seen.contains(key) else { return }
+ seen.insert(key)
+ result.append(window)
+ }
+
+ var windowsValue: CFTypeRef?
+ if AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute as CFString, &windowsValue) == .success,
+ let windows = windowsValue as? [AXUIElement] {
+ windows.forEach(append)
+ }
+
+ for attribute in [kAXFocusedWindowAttribute, kAXMainWindowAttribute] {
+ var windowValue: CFTypeRef?
+ guard AXUIElementCopyAttributeValue(appElement, attribute as CFString, &windowValue) == .success,
+ let window = windowValue,
+ CFGetTypeID(window) == AXUIElementGetTypeID()
+ else {
+ continue
+ }
+
+ append(window as! AXUIElement)
}
return result
@@ -880,6 +1235,39 @@
}
}
+private struct AXWindowElementKey: Hashable {
+ private let typeID: CFTypeID
+ private let pointer: UInt
+ private let windowID: UInt32?
+
+ init(_ element: AXUIElement) {
+ self.typeID = CFGetTypeID(element)
+ self.pointer = UInt(bitPattern: Unmanaged.passUnretained(element).toOpaque())
+ self.windowID = PrivateAXWindowIDReader.windowID(for: element)
+ }
+
+ static func == (lhs: AXWindowElementKey, rhs: AXWindowElementKey) -> Bool {
+ if let lhsWindowID = lhs.windowID,
+ let rhsWindowID = rhs.windowID {
+ return lhsWindowID == rhsWindowID
+ }
+
+ return lhs.typeID == rhs.typeID && lhs.pointer == rhs.pointer
+ }
+
+ func hash(into hasher: inout Hasher) {
+ if let windowID {
+ hasher.combine("windowID")
+ hasher.combine(windowID)
+ return
+ }
+
+ hasher.combine("pointer")
+ hasher.combine(typeID)
+ hasher.combine(pointer)
+ }
+}
+
private extension AlignerWindowSubrole {
var isExplicitlySupported: Bool {
switch self {
@@ -918,6 +1306,10 @@
value as? String
}
+private func normalizedWindowTitle(_ title: String) -> String {
+ title.trimmingCharacters(in: .whitespacesAndNewlines)
+}
+
private func boolValue(_ value: Any?) -> Bool {
switch value {
case let bool as Bool:
--
Gitblit v1.9.3