diff --git a/macos/Sources/Features/Terminal/BaseTerminalController.swift b/macos/Sources/Features/Terminal/BaseTerminalController.swift index 5f067c128..b70fd2c56 100644 --- a/macos/Sources/Features/Terminal/BaseTerminalController.swift +++ b/macos/Sources/Features/Terminal/BaseTerminalController.swift @@ -195,6 +195,11 @@ class BaseTerminalController: NSWindowController, selector: #selector(ghosttyDidResizeSplit(_:)), name: Ghostty.Notification.didResizeSplit, object: nil) + center.addObserver( + self, + selector: #selector(ghosttyDidPresentTerminal(_:)), + name: Ghostty.Notification.ghosttyPresentTerminal, + object: nil) // Listen for local events that we need to know of outside of // single surface handlers. @@ -700,6 +705,15 @@ class BaseTerminalController: NSWindowController, } } + @objc private func ghosttyDidPresentTerminal(_ notification: Notification) { + guard let target = notification.object as? Ghostty.SurfaceView else { return } + guard surfaceTree.contains(target) else { return } + + // Bring the window to front and focus the surface without activating the app + window?.orderFrontRegardless() + Ghostty.moveFocus(to: target) + } + // MARK: Local Events private func localEventHandler(_ event: NSEvent) -> NSEvent? { diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 4e9d039d4..3348ab714 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -627,12 +627,13 @@ extension Ghostty { case GHOSTTY_ACTION_SEARCH_SELECTED: searchSelected(app, target: target, v: action.action.search_selected) + case GHOSTTY_ACTION_PRESENT_TERMINAL: + return presentTerminal(app, target: target) + case GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW: fallthrough case GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS: fallthrough - case GHOSTTY_ACTION_PRESENT_TERMINAL: - fallthrough case GHOSTTY_ACTION_SIZE_LIMIT: fallthrough case GHOSTTY_ACTION_QUIT_TIMER: @@ -845,6 +846,30 @@ extension Ghostty { } } + private static func presentTerminal( + _ app: ghostty_app_t, + target: ghostty_target_s + ) -> Bool { + switch (target.tag) { + case GHOSTTY_TARGET_APP: + return false + + case GHOSTTY_TARGET_SURFACE: + guard let surface = target.target.surface else { return false } + guard let surfaceView = self.surfaceView(from: surface) else { return false } + + NotificationCenter.default.post( + name: Notification.ghosttyPresentTerminal, + object: surfaceView + ) + return true + + default: + assertionFailure() + return false + } + } + private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s, mode: ghostty_action_close_tab_mode_e) { switch (target.tag) { case GHOSTTY_TARGET_APP: diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index b834ea31f..375e5c37b 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -435,6 +435,9 @@ extension Ghostty.Notification { /// New window. Has base surface config requested in userinfo. static let ghosttyNewWindow = Notification.Name("com.mitchellh.ghostty.newWindow") + /// Present terminal. Bring the surface's window to focus without activating the app. + static let ghosttyPresentTerminal = Notification.Name("com.mitchellh.ghostty.presentTerminal") + /// Toggle fullscreen of current window static let ghosttyToggleFullscreen = Notification.Name("com.mitchellh.ghostty.toggleFullscreen") static let FullscreenModeKey = ghosttyToggleFullscreen.rawValue