From 645c7f76ea9d52c0a1321d897b0818811d79795d Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 21 Feb 2026 05:06:29 -0800 Subject: [PATCH] Start workspace command process before workspace is opened --- Sources/GhosttyTerminalView.swift | 30 +++++++++++++++++++ Sources/TerminalController.swift | 3 ++ .../test_cli_new_workspace_command_queue.py | 8 ++--- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Sources/GhosttyTerminalView.swift b/Sources/GhosttyTerminalView.swift index b1f2faa5..78121fa0 100644 --- a/Sources/GhosttyTerminalView.swift +++ b/Sources/GhosttyTerminalView.swift @@ -1135,6 +1135,7 @@ final class TerminalSurface: Identifiable, ObservableObject { private var pendingTextQueue: [Data] = [] private var pendingTextBytes: Int = 0 private let maxPendingTextBytes = 1_048_576 + private var backgroundSurfaceStartQueued = false @Published var searchState: SearchState? = nil { didSet { if let searchState { @@ -1616,6 +1617,35 @@ final class TerminalSurface: Identifiable, ObservableObject { writeTextData(data, to: surface) } + func requestBackgroundSurfaceStartIfNeeded() { + if !Thread.isMainThread { + DispatchQueue.main.async { [weak self] in + self?.requestBackgroundSurfaceStartIfNeeded() + } + return + } + + guard surface == nil, attachedView != nil else { return } + guard !backgroundSurfaceStartQueued else { return } + backgroundSurfaceStartQueued = true + + DispatchQueue.main.async { [weak self] in + guard let self else { return } + self.backgroundSurfaceStartQueued = false + guard self.surface == nil, let view = self.attachedView else { return } + #if DEBUG + let startedAt = ProcessInfo.processInfo.systemUptime + #endif + self.createSurface(for: view) + #if DEBUG + let elapsedMs = (ProcessInfo.processInfo.systemUptime - startedAt) * 1000.0 + dlog( + "surface.background_start surface=\(self.id.uuidString.prefix(8)) inWindow=\(view.window != nil ? 1 : 0) ready=\(self.surface != nil ? 1 : 0) ms=\(String(format: "%.2f", elapsedMs))" + ) + #endif + } + } + private func writeTextData(_ data: Data, to surface: ghostty_surface_t) { data.withUnsafeBytes { rawBuffer in guard let baseAddress = rawBuffer.baseAddress?.assumingMemoryBound(to: CChar.self) else { return } diff --git a/Sources/TerminalController.swift b/Sources/TerminalController.swift index 7e073b93..5f1a3833 100644 --- a/Sources/TerminalController.swift +++ b/Sources/TerminalController.swift @@ -3165,6 +3165,7 @@ class TerminalController { } else { // Avoid blocking the main actor waiting for view/surface attachment. terminalPanel.sendText(text) + terminalPanel.surface.requestBackgroundSurfaceStartIfNeeded() queued = true } #if DEBUG @@ -9981,6 +9982,7 @@ class TerminalController { sendSocketText(unescaped, surface: surface) } else { terminalPanel.sendText(unescaped) + terminalPanel.surface.requestBackgroundSurfaceStartIfNeeded() } success = true } @@ -10009,6 +10011,7 @@ class TerminalController { sendSocketText(unescaped, surface: surface) } else { terminalPanel.sendText(unescaped) + terminalPanel.surface.requestBackgroundSurfaceStartIfNeeded() } success = true } diff --git a/tests_v2/test_cli_new_workspace_command_queue.py b/tests_v2/test_cli_new_workspace_command_queue.py index 672b1dce..da7523c2 100644 --- a/tests_v2/test_cli_new_workspace_command_queue.py +++ b/tests_v2/test_cli_new_workspace_command_queue.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Regression: `new-workspace --command` must not block/fail before surface attach.""" +"""Regression: `new-workspace --command` should execute without selecting the workspace.""" from __future__ import annotations @@ -89,9 +89,6 @@ def main() -> int: # Creation with --command should not steal focus. _must(c.current_workspace() == baseline_ws_id, "new-workspace --command should preserve selected workspace") - # Selecting the created workspace should attach/create the surface and flush queued input. - c.select_workspace(created_ws_id) - observed = "" deadline = time.time() + 12.0 while time.time() < deadline: @@ -106,6 +103,7 @@ def main() -> int: _must(marker.exists(), f"Command marker file was not created: {marker}") _must(observed == token, f"Queued command did not execute as expected: expected={token!r} observed={observed!r}") + _must(c.current_workspace() == baseline_ws_id, "Command execution should not switch selected workspace") finally: if created_ws_id: try: @@ -118,7 +116,7 @@ def main() -> int: except OSError: pass - print("PASS: new-workspace --command queues input until surface attach and returns without blocking") + print("PASS: new-workspace --command executes without opening the created workspace") return 0