fix: some windows would not be shown in fullscreen app (closes #688)

This commit is contained in:
Louis Pontoise authored and lwouis committed 2020-11-12 00:30:27 +09:00
1 parent 8b22d4128e
commit 5f9caed280
4 files changed
+29 -18

No files matched your search

+5 -5
View File
@@ -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)
}
}
}
+16 -11
View File
@@ -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+
+7 -2
View File
@@ -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
}
}
}
}
+1
View File
@@ -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)
}