fix: some windows would not be shown in fullscreen app (closes #688)
This commit is contained in:
1 parent
8b22d4128e
commit
5f9caed280
4 files changed
+29
-18
No files matched your search
@@ -7,22 +7,22 @@ import ApplicationServices.HIServices.AXAttributeConstants
|
||||
import ApplicationServices.HIServices.AXActionConstants
|
||||
|
||||
// if the window server is busy, it may not reply to AX calls. We retry right before the call times-out and returns a bogus value
|
||||
func retryAxCallUntilTimeout(_ group: DispatchGroup? = nil, _ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) {
|
||||
func retryAxCallUntilTimeout(_ group: DispatchGroup? = nil, _ timeoutInSeconds: Double = Double(AXUIElement.globalTimeoutInSeconds), _ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) {
|
||||
group?.enter()
|
||||
BackgroundWork.axCallsQueue.async {
|
||||
retryAxCallUntilTimeout_(group, fn, startTime)
|
||||
retryAxCallUntilTimeout_(group, timeoutInSeconds, fn, startTime)
|
||||
}
|
||||
}
|
||||
|
||||
func retryAxCallUntilTimeout_(_ group: DispatchGroup?, _ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) {
|
||||
func retryAxCallUntilTimeout_(_ group: DispatchGroup?, _ timeoutInSeconds: Double, _ fn: @escaping () throws -> Void, _ startTime: DispatchTime = DispatchTime.now()) {
|
||||
do {
|
||||
try fn()
|
||||
group?.leave()
|
||||
} catch {
|
||||
let timePassedInSeconds = Double(DispatchTime.now().uptimeNanoseconds - startTime.uptimeNanoseconds) / 1_000_000_000
|
||||
if timePassedInSeconds < Double(AXUIElement.globalTimeoutInSeconds) {
|
||||
if timePassedInSeconds < timeoutInSeconds {
|
||||
BackgroundWork.axCallsQueue.asyncAfter(deadline: .now() + .milliseconds(10)) {
|
||||
retryAxCallUntilTimeout_(group, fn, startTime)
|
||||
retryAxCallUntilTimeout_(group, timeoutInSeconds, fn, startTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,11 +146,27 @@ func GetProcessInformation(_ psn: inout ProcessSerialNumber, _ info: inout Proce
|
||||
@_silgen_name("GetProcessForPID") @discardableResult
|
||||
func GetProcessForPID(_ pid: pid_t, _ psn: inout ProcessSerialNumber) -> OSStatus
|
||||
|
||||
enum CGSSpaceType: Int {
|
||||
case user = 0
|
||||
case system = 2
|
||||
case fullscreen = 4
|
||||
}
|
||||
|
||||
// get the CGSSpaceType for a given space. Maybe useful for fullscreen windows
|
||||
// * macOS 10.10+
|
||||
@_silgen_name("CGSSpaceGetType")
|
||||
func CGSSpaceGetType(_ cid: CGSConnectionID, _ sid: CGSSpaceID) -> CGSSpaceType
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// below are some notes on some private APIs I experimented with
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// returns true if the display is animating
|
||||
//@_silgen_name("CGSManagedDisplayIsAnimating") @discardableResult
|
||||
//func CGSManagedDisplayIsAnimating(_ cid: CGSConnectionID, _ displayUuid: CFString) -> Bool
|
||||
|
||||
//@_silgen_name("CGSGetSymbolicHotKeyValue")
|
||||
//func CGSGetSymbolicHotKeyValue(_ hotKey: Int, _ options: inout UInt32, _ keyCode: inout UInt32, _ modifiers: inout UInt32) -> CGError
|
||||
//
|
||||
@@ -256,17 +272,6 @@ func GetProcessForPID(_ pid: pid_t, _ psn: inout ProcessSerialNumber) -> OSStatu
|
||||
//@_silgen_name("CGSGetWorkspaceWindowList") @discardableResult
|
||||
//func CGSGetWorkspaceWindowList(_ cid: CGSConnectionID, _ workspaceNumber: CGSSpaceID, _ count: Int, _ list: [Int], _ outCount: [Int]) -> OSStatus
|
||||
//
|
||||
//enum CGSSpaceType {
|
||||
// case user
|
||||
// case fullscreen
|
||||
// case system
|
||||
// case unknown
|
||||
//}
|
||||
//
|
||||
//// get the CGSSpaceType for a given space. Maybe useful for fullscreen windows
|
||||
//// * macOS 10.10+
|
||||
//@_silgen_name("CGSSpaceGetType")
|
||||
//func CGSSpaceGetType(_ connection: CGSConnectionID, _ space: CGSSpaceID) -> CGSSpaceType
|
||||
//
|
||||
//// assigns a process to all spaces. This creates weird behaviours where its windows are available from all spaces
|
||||
//// * macOS 10.10+
|
||||
|
||||
@@ -12,9 +12,10 @@ class Application: NSObject {
|
||||
var icon: NSImage?
|
||||
var dockLabel: String?
|
||||
var pid: pid_t { runningApplication.processIdentifier }
|
||||
var wasLaunchedBeforeAltTab = false
|
||||
|
||||
static func notifications(_ app: NSRunningApplication) -> [String] {
|
||||
var n = [
|
||||
let n = [
|
||||
kAXApplicationActivatedNotification,
|
||||
kAXMainWindowChangedNotification,
|
||||
kAXFocusedWindowChangedNotification,
|
||||
@@ -74,7 +75,7 @@ class Application: NSObject {
|
||||
|
||||
func observeNewWindows(_ group: DispatchGroup? = nil) {
|
||||
if runningApplication.isFinishedLaunching && runningApplication.activationPolicy != .prohibited {
|
||||
retryAxCallUntilTimeout(group) { [weak self] in
|
||||
retryAxCallUntilTimeout(group, 5) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
if let axWindows_ = try self.axUiElement!.windows(), axWindows_.count > 0 {
|
||||
// bug in macOS: sometimes the OS returns multiple duplicate windows (e.g. Mail.app starting at login)
|
||||
@@ -105,6 +106,10 @@ class Application: NSObject {
|
||||
let window = self.addWindowslessAppsIfNeeded()
|
||||
App.app.refreshOpenUi(window)
|
||||
}
|
||||
// workaround: opening an app while the active app is fullscreen; we wait out the space transition animation
|
||||
if group == nil && !self.wasLaunchedBeforeAltTab && CGSSpaceGetType(cgsMainConnectionId, Spaces.currentSpaceId) == .fullscreen {
|
||||
throw AxError.runtimeError
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ class Applications {
|
||||
static func observeNewWindowsBlocking() {
|
||||
let group = DispatchGroup()
|
||||
for app in list {
|
||||
app.wasLaunchedBeforeAltTab = true
|
||||
guard app.runningApplication.isFinishedLaunching else { continue }
|
||||
app.observeNewWindows(group)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user