diff --git a/Sources/ContentView.swift b/Sources/ContentView.swift index beed69a2..c82cba22 100644 --- a/Sources/ContentView.swift +++ b/Sources/ContentView.swift @@ -196,19 +196,31 @@ final class FileDropOverlayView: NSView { // MARK: Mouse forwarding — safety net for the rare case where stale drag pasteboard // data causes hitTest to return self when no drag is actually active. - // The isForwarding guard prevents infinite recursion: sendEvent → mouseDown → - // forwardEvent → sendEvent, which can stack overflow when gesture recognizer - // routing re-delivers the event despite isHidden. - - private var isForwarding = false + // We hit-test contentView directly and dispatch to the target rather than using + // window.sendEvent(), which caches the mouse target and causes infinite recursion. private func forwardEvent(_ event: NSEvent) { - guard !isForwarding else { return } - isForwarding = true + guard let window, let contentView = window.contentView, + let themeFrame = contentView.superview else { return } isHidden = true - window?.sendEvent(event) + let point = themeFrame.convert(event.locationInWindow, from: nil) + let target = contentView.hitTest(point) isHidden = false - isForwarding = false + guard let target else { return } + + switch event.type { + case .leftMouseDown: target.mouseDown(with: event) + case .leftMouseUp: target.mouseUp(with: event) + case .leftMouseDragged: target.mouseDragged(with: event) + case .rightMouseDown: target.rightMouseDown(with: event) + case .rightMouseUp: target.rightMouseUp(with: event) + case .rightMouseDragged: target.rightMouseDragged(with: event) + case .otherMouseDown: target.otherMouseDown(with: event) + case .otherMouseUp: target.otherMouseUp(with: event) + case .otherMouseDragged: target.otherMouseDragged(with: event) + case .scrollWheel: target.scrollWheel(with: event) + default: break + } } override func mouseDown(with event: NSEvent) { forwardEvent(event) } diff --git a/Sources/Panels/BrowserPanelView.swift b/Sources/Panels/BrowserPanelView.swift index 34ef9c94..9cf46a7f 100644 --- a/Sources/Panels/BrowserPanelView.swift +++ b/Sources/Panels/BrowserPanelView.swift @@ -2132,9 +2132,21 @@ private struct OmnibarTextFieldRepresentable: NSViewRepresentable { firstResponder === nsView || ((firstResponder as? NSTextView)?.delegate as? NSTextField) === nsView if isFocused, !isFirstResponder { - window.makeFirstResponder(nsView) + // Defer to avoid triggering input method XPC during layout pass, + // which can crash via re-entrant view hierarchy modification. + DispatchQueue.main.async { [weak nsView] in + guard let nsView, let window = nsView.window else { return } + window.makeFirstResponder(nsView) + } } else if !isFocused, isFirstResponder { - window.makeFirstResponder(nil) + DispatchQueue.main.async { [weak nsView] in + guard let nsView, let window = nsView.window else { return } + let fr = window.firstResponder + let stillFirst = fr === nsView || + ((fr as? NSTextView)?.delegate as? NSTextField) === nsView + guard stillFirst else { return } + window.makeFirstResponder(nil) + } } } diff --git a/homebrew-cmux b/homebrew-cmux index a06b9553..1da02f8a 160000 --- a/homebrew-cmux +++ b/homebrew-cmux @@ -1 +1 @@ -Subproject commit a06b95531ead59479d4655b314f2b86cb4cc4c84 +Subproject commit 1da02f8acbe6ba7ee90b720d8328941b62f73f8b