import AppKit
|
import CoreGraphics
|
import AlignerCore
|
|
struct SystemCriticalWindowRecord: Equatable {
|
let ownerName: String
|
let title: String
|
let processIdentifier: Int32
|
let layer: Int
|
let bounds: CGRect
|
}
|
|
protocol SystemCriticalWindowDetecting: AnyObject {
|
func visibleSystemCriticalWindows() -> [SystemCriticalWindowRecord]
|
}
|
|
final class CGWindowSystemCriticalWindowDetector: SystemCriticalWindowDetecting {
|
func visibleSystemCriticalWindows() -> [SystemCriticalWindowRecord] {
|
guard let windowInfo = CGWindowListCopyWindowInfo(
|
[.optionOnScreenOnly, .excludeDesktopElements],
|
kCGNullWindowID
|
) as? [[String: Any]] else {
|
return []
|
}
|
|
return windowInfo.compactMap { rawWindow in
|
let ownerName = stringValue(rawWindow[kCGWindowOwnerName as String]) ?? ""
|
guard ownerName != "Aligner" else { return nil }
|
|
let title = stringValue(rawWindow[kCGWindowName as String]) ?? ""
|
let descriptor = SystemCriticalWindowDescriptor(ownerName: ownerName, title: title)
|
guard SystemCriticalWindowClassifier.isSystemCritical(descriptor) else {
|
return nil
|
}
|
|
let bounds = rectValue(rawWindow[kCGWindowBounds as String])
|
guard bounds.width > 0, bounds.height > 0 else { return nil }
|
|
return SystemCriticalWindowRecord(
|
ownerName: ownerName,
|
title: title,
|
processIdentifier: Int32(intValue(rawWindow[kCGWindowOwnerPID as String])),
|
layer: intValue(rawWindow[kCGWindowLayer as String]),
|
bounds: bounds
|
)
|
}
|
}
|
|
private func stringValue(_ value: Any?) -> String? {
|
value as? String
|
}
|
|
private func intValue(_ value: Any?) -> Int {
|
switch value {
|
case let number as NSNumber:
|
return number.intValue
|
case let int as Int:
|
return int
|
default:
|
return 0
|
}
|
}
|
|
private func doubleValue(_ value: Any?) -> Double {
|
switch value {
|
case let number as NSNumber:
|
return number.doubleValue
|
case let double as Double:
|
return double
|
case let int as Int:
|
return Double(int)
|
default:
|
return 0
|
}
|
}
|
|
private func rectValue(_ value: Any?) -> CGRect {
|
guard let dictionary = value as? [String: Any] else {
|
return .zero
|
}
|
|
return CGRect(
|
x: doubleValue(dictionary["X"]),
|
y: doubleValue(dictionary["Y"]),
|
width: doubleValue(dictionary["Width"]),
|
height: doubleValue(dictionary["Height"])
|
)
|
}
|
}
|