Fix Shottr and HBuilderX window candidates
| | |
| | | <key>CFBundlePackageType</key> |
| | | <string>APPL</string> |
| | | <key>CFBundleShortVersionString</key> |
| | | <string>0.0.20</string> |
| | | <string>0.0.21</string> |
| | | <key>CFBundleVersion</key> |
| | | <string>20260603.2338</string> |
| | | <string>20260604.0030</string> |
| | | <key>LSMinimumSystemVersion</key> |
| | | <string>26.0</string> |
| | | <key>NSHighResolutionCapable</key> |
| | |
| | | isGhost: isGhost( |
| | | rawWindow: rawWindow, |
| | | axWindow: axWindow, |
| | | app: app, |
| | | spaceIDs: spaceIDs, |
| | | hasAXBackedTitleDuplicate: hasAXBackedTitleDuplicate |
| | | ), |
| | |
| | | private func isGhost( |
| | | rawWindow: [String: Any], |
| | | axWindow: AXWindowMetadata?, |
| | | app: AlignerApp, |
| | | spaceIDs: [UInt64], |
| | | hasAXBackedTitleDuplicate: Bool = false |
| | | ) -> Bool { |
| | |
| | | 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 |
| | |
| | | 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 alpha > 0 |
| | | && trimmedTitle == "Shottr" |
| | | && 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 { |
| | | guard app.bundleIdentifier == "com.apple.finder" else { return false } |
| | | let title = stringValue(rawWindow[kCGWindowName as String]) ?? "" |
| | |
| | | |
| | | public static func shouldInclude(_ record: WindowEnumerationRecord) -> Bool { |
| | | guard record.id != 0 else { return false } |
| | | guard record.activationPolicy == .regular else { return false } |
| | | guard hasSupportedActivationPolicy(record) 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 !isKnownClosedDocumentPlaceholder(record) else { return false } |
| | | |
| | | return record.isMinimized || record.isFullscreen || isLargeEnough(record.size) |
| | | } |
| | | |
| | | private static func hasSupportedActivationPolicy(_ record: WindowEnumerationRecord) -> Bool { |
| | | record.activationPolicy == .regular |
| | | || (record.activationPolicy == .accessory && isKnownCGOnlyOffscreenMainWindow(record)) |
| | | } |
| | | |
| | | public static func window(from record: WindowEnumerationRecord) -> AlignerWindow? { |
| | |
| | | } |
| | | |
| | | private static func isCGOnlyOffscreenGhost(_ record: WindowEnumerationRecord) -> Bool { |
| | | record.identifierSource == .cgWindow |
| | | && !record.hasAXBacking |
| | | && !record.isOnscreen |
| | | && !record.isMinimized |
| | | && !record.isFullscreen |
| | | guard record.identifierSource == .cgWindow, |
| | | !record.hasAXBacking, |
| | | !record.isOnscreen, |
| | | !record.isMinimized, |
| | | !record.isFullscreen |
| | | else { |
| | | return false |
| | | } |
| | | |
| | | return !isKnownCGOnlyOffscreenMainWindow(record) |
| | | } |
| | | |
| | | private static func isKnownCGOnlyOffscreenMainWindow(_ record: WindowEnumerationRecord) -> Bool { |
| | | let trimmedTitle = record.title.trimmingCharacters(in: .whitespacesAndNewlines) |
| | | |
| | | switch record.app.bundleIdentifier { |
| | | case "cc.ffitch.shottr": |
| | | return trimmedTitle == "Shottr" && isLargeEnough(record.size) |
| | | case "io.dcloud.HBuilderX": |
| | | return !trimmedTitle.isEmpty && isLargeEnough(record.size) |
| | | default: |
| | | return false |
| | | } |
| | | } |
| | | |
| | | private static func isKnownClosedDocumentPlaceholder(_ record: WindowEnumerationRecord) -> Bool { |
| | |
| | | XCTAssertEqual(window?.title, "Visible Document.md") |
| | | } |
| | | |
| | | func testWindowEnumerationPolicyKeepsKnownCGOnlyOffscreenMainWindows() { |
| | | let hbuilderApp = AlignerApp( |
| | | bundleIdentifier: "io.dcloud.HBuilderX", |
| | | name: "HBuilderX", |
| | | category: .editorIDE |
| | | ) |
| | | let record = WindowEnumerationRecord( |
| | | id: 55_388, |
| | | app: hbuilderApp, |
| | | title: "lm-app - HBuilder X 5.07", |
| | | size: CGSize(width: 1048, height: 796), |
| | | subrole: .standard, |
| | | hasAXBacking: false, |
| | | isOnscreen: false |
| | | ) |
| | | let emptyTitleRecord = WindowEnumerationRecord( |
| | | id: 55_389, |
| | | app: hbuilderApp, |
| | | title: "", |
| | | size: CGSize(width: 1048, height: 796), |
| | | subrole: .standard, |
| | | hasAXBacking: false, |
| | | isOnscreen: false |
| | | ) |
| | | let tinyRecord = WindowEnumerationRecord( |
| | | id: 55_390, |
| | | app: hbuilderApp, |
| | | title: "HBuilderX Utility", |
| | | size: CGSize(width: 80, height: 40), |
| | | subrole: .standard, |
| | | hasAXBacking: false, |
| | | isOnscreen: false |
| | | ) |
| | | |
| | | XCTAssertEqual(WindowEnumerationPolicy.window(from: record)?.title, "lm-app - HBuilder X 5.07") |
| | | XCTAssertNil(WindowEnumerationPolicy.window(from: emptyTitleRecord)) |
| | | XCTAssertNil(WindowEnumerationPolicy.window(from: tinyRecord)) |
| | | } |
| | | |
| | | func testWindowEnumerationPolicyKeepsShottrCGOnlyAnnotationWindow() { |
| | | let shottrApp = AlignerApp( |
| | | bundleIdentifier: "cc.ffitch.shottr", |
| | | name: "Shottr", |
| | | category: .generic |
| | | ) |
| | | let annotationRecord = WindowEnumerationRecord( |
| | | id: 78, |
| | | app: shottrApp, |
| | | activationPolicy: .accessory, |
| | | title: "Shottr", |
| | | size: CGSize(width: 1710, height: 997), |
| | | subrole: .standard, |
| | | hasAXBacking: false, |
| | | isOnscreen: false |
| | | ) |
| | | let utilityRecord = WindowEnumerationRecord( |
| | | id: 102, |
| | | app: shottrApp, |
| | | activationPolicy: .accessory, |
| | | title: "Colors", |
| | | size: CGSize(width: 250, height: 397), |
| | | subrole: .standard, |
| | | hasAXBacking: false, |
| | | isOnscreen: false |
| | | ) |
| | | |
| | | XCTAssertEqual(WindowEnumerationPolicy.window(from: annotationRecord)?.title, "Shottr") |
| | | XCTAssertNil(WindowEnumerationPolicy.window(from: utilityRecord)) |
| | | } |
| | | |
| | | func testWindowEnumerationPolicyKeepsMinimizedAndFullscreenCGOnlyOffscreenWindows() { |
| | | let minimized = makeWindowRecord( |
| | | title: "Minimized Document.md", |