fix: don't hang waiting for faulty apps to reply (closes #182)
This commit is contained in:
1 parent
9110f6947f
commit
246cf69310
5 files changed
+42
-30
No files matched your search
@@ -53,21 +53,27 @@ extension AXUIElement {
|
||||
}
|
||||
|
||||
func subscribeWithRetry(_ axObserver: AXObserver, _ notification: String, _ pointer: UnsafeMutableRawPointer?, _ callback: (() -> Void)? = nil, _ runningApplication: NSRunningApplication? = nil, _ wid: CGWindowID? = nil, _ attemptsCount: Int = 0) {
|
||||
if let runningApplication = runningApplication, Applications.appsInSubscriptionRetryLoop.first(where: { $0 == String(runningApplication.processIdentifier) + String(notification) }) == nil { return }
|
||||
if let wid = wid, Windows.windowsInSubscriptionRetryLoop.first(where: { $0 == String(wid) + String(notification) }) == nil { return }
|
||||
let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer)
|
||||
if result == .success || result == .notificationAlreadyRegistered {
|
||||
callback?()
|
||||
stopRetries(runningApplication, wid, notification)
|
||||
return
|
||||
} else if result == .notificationUnsupported || result == .notImplemented {
|
||||
stopRetries(runningApplication, wid, notification)
|
||||
return
|
||||
DispatchQueue.global(qos: .userInteractive).async {
|
||||
DispatchQueue.main.async {
|
||||
if let runningApplication = runningApplication, Applications.appsInSubscriptionRetryLoop.first(where: { $0 == String(runningApplication.processIdentifier) + String(notification) }) == nil { return }
|
||||
if let wid = wid, Windows.windowsInSubscriptionRetryLoop.first(where: { $0 == String(wid) + String(notification) }) == nil { return }
|
||||
}
|
||||
let result = AXObserverAddNotification(axObserver, self, notification as CFString, pointer)
|
||||
if result == .success || result == .notificationAlreadyRegistered {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
callback?()
|
||||
self?.stopRetries(runningApplication, wid, notification)
|
||||
}
|
||||
} else if result == .notificationUnsupported || result == .notImplemented {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.stopRetries(runningApplication, wid, notification)
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.global(qos: .userInteractive).asyncAfter(deadline: .now() + .milliseconds(10), execute: { [weak self] in
|
||||
self?.subscribeWithRetry(axObserver, notification, pointer, callback, runningApplication, wid, attemptsCount + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.subscribeWithRetry(axObserver, notification, pointer, callback, runningApplication, wid, attemptsCount + 1)
|
||||
})
|
||||
}
|
||||
|
||||
func stopRetries(_ runningApplication: NSRunningApplication?, _ wid: CGWindowID?, _ notification: String) {
|
||||
|
||||
@@ -15,8 +15,8 @@ class Applications {
|
||||
|
||||
static func initialDiscovery() {
|
||||
addInitialRunningApplications()
|
||||
observeRunningApplications()
|
||||
addInitialRunningApplicationsWindows()
|
||||
observeRunningApplications()
|
||||
}
|
||||
|
||||
static func addInitialRunningApplications() {
|
||||
@@ -28,23 +28,18 @@ class Applications {
|
||||
}
|
||||
|
||||
static func addInitialRunningApplicationsWindows() {
|
||||
// on initial launch, we use private APIs to bring windows from other spaces into the current space, observe them, then remove them from the current space
|
||||
let spaces = Spaces.otherSpaces()
|
||||
if spaces.count == 0 {
|
||||
Windows.sortByLevel()
|
||||
return
|
||||
}
|
||||
let windows = Spaces.windowsInSpaces(spaces).filter { window in
|
||||
return Windows.list.first(where: { $0.cgWindowId == window }) == nil
|
||||
}
|
||||
if windows.count > 0 {
|
||||
CGSAddWindowsToSpaces(cgsMainConnectionId, windows as NSArray, [Spaces.currentSpaceId])
|
||||
Applications.observeNewWindows()
|
||||
Windows.sortByLevel()
|
||||
CGSRemoveWindowsFromSpaces(cgsMainConnectionId, windows as NSArray, [Spaces.currentSpaceId])
|
||||
return
|
||||
} else {
|
||||
let windows = Spaces.windowsInSpaces(spaces)
|
||||
if windows.count > 0 {
|
||||
// on initial launch, we use private APIs to bring windows from other spaces into the current space, observe them, then remove them from the current space
|
||||
CGSAddWindowsToSpaces(cgsMainConnectionId, windows as NSArray, [Spaces.currentSpaceId])
|
||||
Applications.observeNewWindows()
|
||||
CGSRemoveWindowsFromSpaces(cgsMainConnectionId, windows as NSArray, [Spaces.currentSpaceId])
|
||||
}
|
||||
}
|
||||
Windows.sortByLevel()
|
||||
}
|
||||
|
||||
static func addRunningApplications(_ runningApps: [NSRunningApplication]) {
|
||||
@@ -71,7 +66,15 @@ class Applications {
|
||||
}
|
||||
|
||||
private static func filterApplications(_ apps: [NSRunningApplication]) -> [NSRunningApplication] {
|
||||
return apps.filter { $0.activationPolicy != .prohibited || $0.bundleIdentifier == "io.github.hluk.CopyQ" }
|
||||
return apps.filter {
|
||||
($0.activationPolicy != .prohibited ||
|
||||
// Bug in CopyQ; see https://github.com/hluk/CopyQ/issues/1330
|
||||
$0.bundleIdentifier == "io.github.hluk.CopyQ" ||
|
||||
// Bug in Octave.app; see https://github.com/octave-app/octave-app/issues/193#issuecomment-603648857
|
||||
$0.localizedName == "octave-gui") &&
|
||||
// bug in Octave.app; see https://github.com/octave-app/octave-app/issues/193
|
||||
$0.bundleIdentifier != "org.octave-app.Octave"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
|
||||
// OS events should be observed on background threads
|
||||
class DispatchQueues {
|
||||
static let focusActions = DispatchQueue(label: "focusActions", qos: .userInteractive)
|
||||
static let keyboardEvents = DispatchQueue(label: "keyboardEvents", qos: .userInteractive)
|
||||
|
||||
@@ -8,6 +8,7 @@ class Spaces {
|
||||
|
||||
static func observeSpaceChanges() {
|
||||
NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.activeSpaceDidChangeNotification, object: nil, queue: nil, using: { _ in
|
||||
debugPrint("OS event", "activeSpaceDidChangeNotification")
|
||||
updateCurrentSpace()
|
||||
Applications.observeNewWindows()
|
||||
let app = App.shared as! App
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ class App: NSApplication, NSApplicationDelegate {
|
||||
Keyboard.listenToGlobalEvents(self)
|
||||
preferencesWindow = PreferencesWindow()
|
||||
UpdatesTab.observeUserDefaults()
|
||||
// Windows.refreshAllThumbnails()
|
||||
// TODO: undeterministic; events in the queue may still be processing; good enough for now
|
||||
DispatchQueue.main.async { Windows.sortByLevel() }
|
||||
}
|
||||
|
||||
// keyboard shortcuts are broken without a menu. We generated the default menu from XCode and load it
|
||||
|
||||
Reference in new issue
Block a user