From 3ee8ef4f650f550698ee1e8e81e591511e195bf4 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Wed, 4 Mar 2026 12:26:13 -0600 Subject: [PATCH] macos: suppress split-focus click mouse reports Amp-Thread-ID: https://ampcode.com/threads/T-019cb9fe-b11b-753f-99e7-8ecc52b73ec4 Co-authored-by: Amp --- .../Surface View/SurfaceView_AppKit.swift | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift index 581691ca9..060b7990b 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift @@ -221,6 +221,10 @@ extension Ghostty { // This is set to non-null during keyDown to accumulate insertText contents private var keyTextAccumulator: [String]? + // True when we've consumed a left mouse-down only to move focus and + // should suppress the matching mouse-up from being reported. + private var suppressNextLeftMouseUp: Bool = false + // A small delay that is introduced before a title change to avoid flickers private var titleChangeTimer: Timer? @@ -644,12 +648,18 @@ extension Ghostty { let location = convert(event.locationInWindow, from: nil) guard hitTest(location) == self else { return event } - // We only want to grab focus if either our app or window was - // not focused. - guard !NSApp.isActive || !window.isKeyWindow else { return event } + // If we're already the first responder then no focus transfer is + // happening, so the click should continue as normal. + guard window.firstResponder !== self else { return event } - // If we're already focused we do nothing - guard !focused else { return event } + // If our window/app is already focused, then this click is only + // being used to transfer split focus. Consume it so it does not + // get forwarded to the terminal as a mouse click. + if NSApp.isActive && window.isKeyWindow { + window.makeFirstResponder(self) + suppressNextLeftMouseUp = true + return nil + } // Make ourselves the first responder window.makeFirstResponder(self) @@ -854,6 +864,13 @@ extension Ghostty { } override func mouseUp(with event: NSEvent) { + // If this mouse-up corresponds to a focus-only click transfer, + // suppress it so we don't emit a release without a press. + if suppressNextLeftMouseUp { + suppressNextLeftMouseUp = false + return + } + // Always reset our pressure when the mouse goes up prevPressureStage = 0