diff --git a/alt-tab-macos/api-wrappers/AXUIElement.swift b/alt-tab-macos/api-wrappers/AXUIElement.swift index 814fade5..704359a4 100644 --- a/alt-tab-macos/api-wrappers/AXUIElement.swift +++ b/alt-tab-macos/api-wrappers/AXUIElement.swift @@ -49,13 +49,15 @@ extension AXUIElement { return attribute(kAXSubroleAttribute, String.self) } - func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?) { + func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?, _ callback: (() -> Void)? = nil, _ previousResult: AXError? = nil) { let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer) - if result != .success && result != .notificationUnsupported && result != .notificationAlreadyRegistered { - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: { - self.subscribeWithRetry(axObserver, notification, pointer) - }) + if result == .success || result == .notificationUnsupported || result == .notificationAlreadyRegistered { + callback?() + return } + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: { + self.subscribeWithRetry(axObserver, notification, pointer, callback, result) + }) } private func attribute(_ key: String, _ type: T.Type) -> T? { diff --git a/alt-tab-macos/logic/Application.swift b/alt-tab-macos/logic/Application.swift index 95e163c3..011802fc 100644 --- a/alt-tab-macos/logic/Application.swift +++ b/alt-tab-macos/logic/Application.swift @@ -4,6 +4,7 @@ class Application: NSObject { var runningApplication: NSRunningApplication var axUiElement: AXUIElement? var axObserver: AXObserver? + var isReallyFinishedLaunching = false init(_ runningApplication: NSRunningApplication) { self.runningApplication = runningApplication @@ -22,13 +23,7 @@ class Application: NSObject { private func addAndObserveWindows() { axUiElement = AXUIElementCreateApplication(runningApplication.processIdentifier) AXObserverCreate(runningApplication.processIdentifier, axObserverCallback, &axObserver) - observeAllWindows() - } - - private func observeAllWindows() { - let windows = getActualWindows() - debugPrint("Adding app: " + (runningApplication.bundleIdentifier ?? "nil"), windows.map { $0.title() }) - addWindows(windows) + debugPrint("Adding app: " + (runningApplication.bundleIdentifier ?? "nil")) observeEvents() } @@ -65,7 +60,14 @@ class Application: NSObject { kAXApplicationHiddenNotification, kAXApplicationShownNotification, ] { - axUiElement!.subscribeWithRetry(axObserver, notification, selfPointer) + axUiElement!.subscribeWithRetry(axObserver, notification, selfPointer, { + // some apps have `isFinishedLaunching == true` but are actually not finished, and will return .cannotComplete + // we consider them ready when the first subscription succeeds, and list their windows again at that point + if !self.isReallyFinishedLaunching { + self.isReallyFinishedLaunching = true + self.observeNewWindows() + } + }) } CFRunLoopAddSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(axObserver), .defaultMode) }