Skip to content

Commit 3e3705b

Browse files
authored
macOS: fix surface focus/render state after dragging in to to another window/tab (ghostty-org#12338)
Fixes 2 bugs 1. After dragging a non-focused surface from window A to window B **quickly without making B the key window**, the focused surface in window A is not receiving `keyDown` events. https://github.com/user-attachments/assets/a8861c0a-9300-470d-bf7e-0f32a9ab2cd1 2. ghostty-org#12343 After dragging a surface from tab A to tab B within the same window, the dragged surface is not rendering input correctly. > The reason the thread is stuck is because the surface's occlusion state is set to invisible after target tab's activate while dragging, since the dragged surface is still in previous tree before dropping, and after dropping the occlusion state of this surface is not updated to visible, which causing the surface is accepting input but not rendering. https://github.com/user-attachments/assets/d67f5dba-8609-4f67-a956-921982faf796
2 parents f5aa271 + 2c6dd59 commit 3e3705b

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

macos/Sources/Features/Terminal/BaseTerminalController.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class BaseTerminalController: NSWindowController,
292292
if to.isEmpty {
293293
focusedSurface = nil
294294
}
295+
syncSurfaceTreeOcclusionState()
295296
}
296297

297298
/// Update all surfaces with the focus state. This ensures that libghostty has an accurate view about
@@ -470,8 +471,12 @@ class BaseTerminalController: NSWindowController,
470471

471472
replaceSurfaceTree(
472473
surfaceTree.removing(node),
473-
moveFocusTo: nextFocus,
474-
moveFocusFrom: focusedSurface,
474+
// When a non-focused surface is removed and this window stays as the key window,
475+
// we should refocus the `focusedSurface` to make sure the window's firstResponder remains as it is.
476+
//
477+
// This is a weird workaround, since `resignFirstResponder` wasn't called on `focusedSurface` after drag,
478+
// but the first responder became the window itself.
479+
moveFocusTo: nextFocus ?? focusedSurface,
475480
undoAction: "Close Terminal"
476481
)
477482
}
@@ -1277,10 +1282,15 @@ class BaseTerminalController: NSWindowController,
12771282
}
12781283

12791284
func windowDidChangeOcclusionState(_ notification: Notification) {
1285+
syncSurfaceTreeOcclusionState()
1286+
}
1287+
1288+
private func syncSurfaceTreeOcclusionState() {
12801289
let visible = self.window?.occlusionState.contains(.visible) ?? false
12811290
for view in surfaceTree {
1282-
if let surface = view.surface {
1291+
if let surface = view.surface, view.isWindowVisible != visible {
12831292
ghostty_surface_set_occlusion(surface, visible)
1293+
view.isWindowVisible = visible
12841294
}
12851295
}
12861296
}

macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ extension Ghostty {
8989
// Whether the cursor is currently visible (not hidden by typing, etc.)
9090
@Published private(set) var cursorVisible: Bool = true
9191

92+
/// Whether the belonging window is visible
93+
///
94+
/// We track this to restore surface occlusion state
95+
/// after this surface is dragged to another window
96+
var isWindowVisible = false
97+
9298
/// The configuration derived from the Ghostty config so we don't need to rely on references.
9399
@Published private(set) var derivedConfig: DerivedConfig
94100

0 commit comments

Comments
 (0)