fix: don't hang waiting for faulty apps to reply (closes #182)

This commit is contained in:
Louis Pontoise authored and lwouis committed 2020-03-25 14:41:18 +09:00
1 parent 9110f6947f
commit 246cf69310
5 files changed
+42 -30

No files matched your search

+20 -14
View File
@@ -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) {
+18 -15
View File
@@ -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
View File
@@ -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)
+1
View File
@@ -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
View File
@@ -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