From 6be72f36d76314d06afb8f099d44ae6403295ec0 Mon Sep 17 00:00:00 2001 From: Louis Pontoise Date: Sat, 17 Apr 2021 19:30:01 +0900 Subject: [PATCH] fix: apps could steal key focus from alt-tab main window (#719 #916) (closes #719, closes #916) --- src/logic/SystemPermissions.swift | 2 +- src/logic/Window.swift | 7 ++++++ src/logic/Windows.swift | 23 +------------------ src/logic/events/AccessibilityEvents.swift | 20 ++++++++-------- src/ui/App.swift | 15 ++++++++++++ src/ui/main-window/ThumbnailsPanel.swift | 13 ++++++++++- .../permission-window/PermissionsWindow.swift | 6 ----- 7 files changed, 45 insertions(+), 41 deletions(-) diff --git a/src/logic/SystemPermissions.swift b/src/logic/SystemPermissions.swift index c96e9d21..eca8142e 100644 --- a/src/logic/SystemPermissions.swift +++ b/src/logic/SystemPermissions.swift @@ -93,7 +93,7 @@ class SystemPermissions { startupBlock() } else { permissionsWindow = PermissionsWindow() - permissionsWindow.show() + App.app.showSecondaryWindow(permissionsWindow) observePermissionsPreStartup(startupBlock) } } diff --git a/src/logic/Window.swift b/src/logic/Window.swift index 355fd38a..eec378e2 100644 --- a/src/logic/Window.swift +++ b/src/logic/Window.swift @@ -216,5 +216,12 @@ class Window { isOnAllSpaces = true } } + + func isOnScreen(_ screen: NSScreen) -> Bool { + if let screenUuid = screen.uuid(), let screenSpaces = Spaces.screenSpacesMap[screenUuid] { + return screenSpaces.contains { $0 == spaceId } + } + return true + } } diff --git a/src/logic/Windows.swift b/src/logic/Windows.swift index ff41ebdd..2e0fec53 100644 --- a/src/logic/Windows.swift +++ b/src/logic/Windows.swift @@ -181,30 +181,9 @@ class Windows { !(!(Preferences.showMinimizedWindows[App.app.shortcutIndex] != .hide) && window.isMinimized) && !(Preferences.spacesToShow[App.app.shortcutIndex] == .active && window.spaceId != Spaces.currentSpaceId) && !(Preferences.spacesToShow[App.app.shortcutIndex] == .visible && !Spaces.visibleSpaces.contains(window.spaceId)) && - !(Preferences.screensToShow[App.app.shortcutIndex] == .showingAltTab && !isOnScreen(window, screen)) && + !(Preferences.screensToShow[App.app.shortcutIndex] == .showingAltTab && !window.isOnScreen(screen)) && (Preferences.showTabsAsWindows || !window.isTabbed)) } - - static func isOnScreen(_ window: Window, _ screen: NSScreen) -> Bool { - if let screenUuid = screen.uuid(), let screenSpaces = Spaces.screenSpacesMap[screenUuid] { - return screenSpaces.contains { $0 == window.spaceId } - } - return true - } - - static func checkIfShortcutsShouldBeDisabled(_ activeWindow: Window) { - let shortcutsShouldBeDisabled = (!Preferences.disableShortcutsBlacklistOnlyFullscreen || activeWindow.isFullscreen) && - (Preferences.disableShortcutsBlacklist.first { blacklistedId in - if let id = activeWindow.application.runningApplication.bundleIdentifier { - return id.hasPrefix(blacklistedId) - } - return false - } != nil) - KeyboardEvents.toggleGlobalShortcuts(shortcutsShouldBeDisabled) - if shortcutsShouldBeDisabled && App.app.appIsBeingUsed { - App.app.hideUi() - } - } } func sortByBooleanAttribute(_ b1: Bool, _ b2: Bool) -> Bool? { diff --git a/src/logic/events/AccessibilityEvents.swift b/src/logic/events/AccessibilityEvents.swift index b64cd260..c0eba030 100644 --- a/src/logic/events/AccessibilityEvents.swift +++ b/src/logic/events/AccessibilityEvents.swift @@ -36,18 +36,16 @@ fileprivate func focusedUiElementChanged(_ pid: pid_t) throws { } fileprivate func applicationActivated(_ element: AXUIElement, _ pid: pid_t) throws { - if let appFocusedWindow = try element.focusedWindow(), - let wid = try appFocusedWindow.cgWindowId() { - DispatchQueue.main.async { - if let app = (Applications.list.first { $0.pid == pid }), !app.hasBeenActiveOnce { + let appFocusedWindow = try element.focusedWindow() + let wid = try appFocusedWindow?.cgWindowId() + DispatchQueue.main.async { + if let app = (Applications.list.first { $0.pid == pid }) { + if !app.hasBeenActiveOnce { app.hasBeenActiveOnce = true } - // ensure alt-tab window remains key, so local shortcuts work - if App.app.appIsBeingUsed { App.app.thumbnailsPanel.makeKeyAndOrderFront(nil) } - if let window = Windows.updateLastFocus(appFocusedWindow, wid) { - Windows.checkIfShortcutsShouldBeDisabled(window.first!) - App.app.refreshOpenUi(window) - } + let window = (appFocusedWindow != nil && wid != nil) ? Windows.updateLastFocus(appFocusedWindow!, wid!)?.first : nil + App.app.checkIfShortcutsShouldBeDisabled(window, app.runningApplication) + App.app.refreshOpenUi(window != nil ? [window!] : nil) } } } @@ -187,7 +185,7 @@ fileprivate func windowResized(_ element: AXUIElement) throws { if let window = (Windows.list.first { $0.isEqualRobust(element, wid) }) { if window.isFullscreen != isFullscreen { window.isFullscreen = isFullscreen - Windows.checkIfShortcutsShouldBeDisabled(window) + App.app.checkIfShortcutsShouldBeDisabled(window, nil) } App.app.refreshOpenUi([window]) } diff --git a/src/ui/App.swift b/src/ui/App.swift index 9d4a422e..b47de7d3 100644 --- a/src/ui/App.swift +++ b/src/ui/App.swift @@ -282,4 +282,19 @@ class App: AppCenterApplication, NSApplicationDelegate { Applications.refreshBadges() KeyRepeatTimer.toggleRepeatingKeyNextWindow() } + + func checkIfShortcutsShouldBeDisabled(_ activeWindow: Window?, _ activeApp: NSRunningApplication?) { + let app = activeWindow?.application.runningApplication ?? activeApp + let shortcutsShouldBeDisabled = (!Preferences.disableShortcutsBlacklistOnlyFullscreen || (activeWindow?.isFullscreen ?? false)) && + (Preferences.disableShortcutsBlacklist.first { blacklistedId in + if let id = app?.bundleIdentifier { + return id.hasPrefix(blacklistedId) + } + return false + } != nil) + KeyboardEvents.toggleGlobalShortcuts(shortcutsShouldBeDisabled) + if shortcutsShouldBeDisabled && App.app.appIsBeingUsed { + App.app.hideUi() + } + } } diff --git a/src/ui/main-window/ThumbnailsPanel.swift b/src/ui/main-window/ThumbnailsPanel.swift index 97a151b0..8e5a3664 100644 --- a/src/ui/main-window/ThumbnailsPanel.swift +++ b/src/ui/main-window/ThumbnailsPanel.swift @@ -1,11 +1,12 @@ import Cocoa -class ThumbnailsPanel: NSPanel { +class ThumbnailsPanel: NSPanel, NSWindowDelegate { var thumbnailsView = ThumbnailsView() override var canBecomeKey: Bool { true } convenience init() { self.init(contentRect: .zero, styleMask: .nonactivatingPanel, backing: .buffered, defer: false) + delegate = self isFloatingPanel = true updateFadeOutAnimation() hidesOnDeactivate = false @@ -24,6 +25,16 @@ class ThumbnailsPanel: NSPanel { setAccessibilitySubrole(.unknown) } + func windowDidResignKey(_ notification: Notification) { + // other windows can steal key focus from alt-tab; we make sure that if it's active, if keeps key focus + // dispatching to the main queue is necessary to introduce a delay in scheduling the makeKey; otherwise it is ignored + DispatchQueue.main.async { + if App.app.appIsBeingUsed { + App.app.thumbnailsPanel.makeKeyAndOrderFront(nil) + } + } + } + func updateFadeOutAnimation() { animationBehavior = Preferences.fadeOutAnimation ? .utilityWindow : .none } diff --git a/src/ui/permission-window/PermissionsWindow.swift b/src/ui/permission-window/PermissionsWindow.swift index bcbf6def..7638c04d 100644 --- a/src/ui/permission-window/PermissionsWindow.swift +++ b/src/ui/permission-window/PermissionsWindow.swift @@ -11,12 +11,6 @@ class PermissionsWindow: NSWindow, NSWindowDelegate { setupView() } - func show() { - center() - App.shared.activate(ignoringOtherApps: true) - makeKeyAndOrderFront(nil) - } - func windowShouldClose(_ sender: NSWindow) -> Bool { debugPrint("Before using this app, you need to give permission in System Preferences > Security & Privacy > Privacy > Accessibility.", "Please authorize and re-launch.",