diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 76dfdb5ec..1aeea6626 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -63,6 +63,10 @@ class AppDelegate: NSObject, /// This is only true before application has become active. private var applicationHasBecomeActive: Bool = false + /// This is set in applicationDidFinishLaunching with the system uptime so we can determine the + /// seconds since the process was launched. + private var applicationLaunchTime: TimeInterval = 0 + /// The ghostty global state. Only one per process. let ghostty: Ghostty.App = Ghostty.App() @@ -73,6 +77,11 @@ class AppDelegate: NSObject, let updaterController: SPUStandardUpdaterController let updaterDelegate: UpdaterDelegate = UpdaterDelegate() + /// The elapsed time since the process was started + var timeSinceLaunch: TimeInterval { + return ProcessInfo.processInfo.systemUptime - applicationLaunchTime + } + override init() { terminalManager = TerminalManager(ghostty) updaterController = SPUStandardUpdaterController( @@ -106,6 +115,9 @@ class AppDelegate: NSObject, "ApplePressAndHoldEnabled": false, ]) + // Store our start time + applicationLaunchTime = ProcessInfo.processInfo.systemUptime + // Check if secure input was enabled when we last quit. if (UserDefaults.standard.bool(forKey: "SecureInput") != SecureInput.shared.enabled) { toggleSecureInput(self) @@ -421,10 +433,21 @@ class AppDelegate: NSObject, // If our reload adds global keybinds and we don't have ax permissions then // we need to request them. - global: if (ghostty_app_has_global_keybinds(ghostty.app!)) { - DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) { + if (ghostty_app_has_global_keybinds(ghostty.app!)) { + if (timeSinceLaunch > 5) { + // If the process has been running for awhile we enable right away + // because no windows are likely to pop up. GlobalEventTap.shared.enable() + } else { + // If the process just started, we wait a couple seconds to allow + // the initial windows and so on to load so our permissions dialog + // doesn't get buried. + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) { + GlobalEventTap.shared.enable() + } } + } else { + GlobalEventTap.shared.disable() } } diff --git a/macos/Sources/Features/Global Keybinds/GlobalEventTap.swift b/macos/Sources/Features/Global Keybinds/GlobalEventTap.swift index 3a768df79..73c081df3 100644 --- a/macos/Sources/Features/Global Keybinds/GlobalEventTap.swift +++ b/macos/Sources/Features/Global Keybinds/GlobalEventTap.swift @@ -22,6 +22,7 @@ class GlobalEventTap { // don't have permissions. private var enableTimer: Timer? = nil + // Private init so it can't be constructed outside of our singleton private init() {} deinit {