import CoreGraphics
|
import Foundation
|
|
public enum AlignerActivationPolicy: Equatable, Sendable {
|
case regular
|
case accessory
|
case prohibited
|
case unknown
|
}
|
|
public enum AlignerWindowSubrole: Equatable, Sendable {
|
case standard
|
case dialog
|
case sheet
|
case unknown(String)
|
}
|
|
public struct WindowEnumerationRecord: Equatable, Sendable {
|
public let id: UInt32
|
public let identifierSource: AlignerWindow.IdentifierSource
|
public let app: AlignerApp
|
public let activationPolicy: AlignerActivationPolicy
|
public let title: String
|
public let size: CGSize
|
public let subrole: AlignerWindowSubrole
|
public let isMinimized: Bool
|
public let isFullscreen: Bool
|
public let isGhost: Bool
|
public let isDesktopElement: Bool
|
public let isSystemCritical: Bool
|
public let isInteractable: Bool
|
public let hasAXBacking: Bool
|
public let isOnscreen: Bool
|
public let spaceIDs: [UInt64]
|
|
public init(
|
id: UInt32,
|
app: AlignerApp,
|
activationPolicy: AlignerActivationPolicy = .regular,
|
title: String,
|
identifierSource: AlignerWindow.IdentifierSource = .cgWindow,
|
size: CGSize,
|
subrole: AlignerWindowSubrole,
|
isMinimized: Bool = false,
|
isFullscreen: Bool = false,
|
isGhost: Bool = false,
|
isDesktopElement: Bool = false,
|
isSystemCritical: Bool = false,
|
isInteractable: Bool = true,
|
hasAXBacking: Bool = false,
|
isOnscreen: Bool = false,
|
spaceIDs: [UInt64] = []
|
) {
|
self.id = id
|
self.identifierSource = identifierSource
|
self.app = app
|
self.activationPolicy = activationPolicy
|
self.title = title
|
self.size = size
|
self.subrole = subrole
|
self.isMinimized = isMinimized
|
self.isFullscreen = isFullscreen
|
self.isGhost = isGhost
|
self.isDesktopElement = isDesktopElement
|
self.isSystemCritical = isSystemCritical
|
self.isInteractable = isInteractable
|
self.hasAXBacking = hasAXBacking
|
self.isOnscreen = isOnscreen
|
self.spaceIDs = spaceIDs
|
}
|
}
|
|
public enum WindowEnumerationPolicy {
|
public static let minimumVisibleWidth: CGFloat = 100
|
public static let minimumVisibleHeight: CGFloat = 50
|
|
public static func shouldInclude(_ record: WindowEnumerationRecord) -> Bool {
|
guard record.id != 0 else { return false }
|
guard record.activationPolicy == .regular else { return false }
|
guard isSupportedSubrole(record.subrole) else { return false }
|
guard !record.app.isHidden || record.isFullscreen else { return false }
|
guard !record.isGhost else { return false }
|
guard !record.isDesktopElement else { return false }
|
guard !record.isSystemCritical else { return false }
|
guard record.isInteractable else { return false }
|
guard !isCGOnlyOffscreenGhost(record) else { return false }
|
guard !isKnownClosedDocumentPlaceholder(record) else { return false }
|
|
return record.isMinimized || record.isFullscreen || isLargeEnough(record.size)
|
}
|
|
public static func window(from record: WindowEnumerationRecord) -> AlignerWindow? {
|
guard shouldInclude(record) else { return nil }
|
|
return AlignerWindow(
|
id: record.id,
|
app: record.app,
|
title: displayTitle(for: record),
|
identifierSource: record.identifierSource,
|
isMinimized: record.isMinimized,
|
isFullscreen: record.isFullscreen,
|
isGhost: record.isGhost,
|
spaceIDs: record.spaceIDs
|
)
|
}
|
|
private static func isSupportedSubrole(_ subrole: AlignerWindowSubrole) -> Bool {
|
switch subrole {
|
case .standard, .dialog, .sheet:
|
return true
|
case .unknown:
|
return false
|
}
|
}
|
|
private static func isLargeEnough(_ size: CGSize) -> Bool {
|
size.width > minimumVisibleWidth && size.height > minimumVisibleHeight
|
}
|
|
private static func isCGOnlyOffscreenGhost(_ record: WindowEnumerationRecord) -> Bool {
|
record.identifierSource == .cgWindow
|
&& !record.hasAXBacking
|
&& !record.isOnscreen
|
&& !record.isMinimized
|
&& !record.isFullscreen
|
}
|
|
private static func isKnownClosedDocumentPlaceholder(_ record: WindowEnumerationRecord) -> Bool {
|
let trimmedTitle = record.title.trimmingCharacters(in: .whitespacesAndNewlines)
|
return record.identifierSource == .cgWindow
|
&& record.app.bundleIdentifier == "app.vmark"
|
&& trimmedTitle.isEmpty
|
&& !record.hasAXBacking
|
&& !record.isOnscreen
|
&& !record.isMinimized
|
&& !record.isFullscreen
|
}
|
|
private static func displayTitle(for record: WindowEnumerationRecord) -> String {
|
let trimmedTitle = record.title.trimmingCharacters(in: .whitespacesAndNewlines)
|
guard trimmedTitle.isEmpty else { return record.title }
|
|
return "\(record.app.name) Untitled"
|
}
|
}
|