Skip to content

fix: defer clipboard init to avoid blocking startup on X11-less hosts#1772

Closed
zlh124 wants to merge 1 commit into
Hmbown:mainfrom
zlh124:fix/clipboard-x11-blocking-startup
Closed

fix: defer clipboard init to avoid blocking startup on X11-less hosts#1772
zlh124 wants to merge 1 commit into
Hmbown:mainfrom
zlh124:fix/clipboard-x11-blocking-startup

Conversation

@zlh124
Copy link
Copy Markdown
Contributor

@zlh124 zlh124 commented May 18, 2026

Summary

  • arboard::Clipboard::new() blocks on X11 connect() during startup
  • On WSL2 / headless Linux, this makes the TUI hang with a blank alt-screen
    and Ctrl+C stops working (raw mode suppresses SIGINT, event loop not started yet)
  • Fix: lazy-init clipboard on first use with 500 ms timeout

Test plan

  • cargo test -p deepseek-tui — all tests pass
  • strace confirms no X11 connect() during startup (--version, doctor)
  • Clipboard read/write still works when X11 is available (OSC 52 fallback
    covers the no-X11 case)
  • Manual test on WSL2 without X server — TUI shows onboarding without hanging

Fixes #1773

…osts

On Linux, `arboard::Clipboard::new()` opens a blocking connect() to the
X11 Unix socket. When no X server is running (headless, WSL2 without
WSLg), the call hangs indefinitely. Because raw mode and the alternate
screen are already active at that point, Ctrl+C no longer generates
SIGINT and the event loop hasn't started yet — leaving the user with a
blank screen and no way to exit.

Move clipboard initialization from `ClipboardHandler::new()` (called
synchronously during App construction) to a lazy `ensure_clipboard()`
that runs on first read/write with a 500 ms timeout. If the X11
connection doesn't respond in time, the handler stays in fallback mode
and `write_text` falls through to the existing OSC 52 / pbcopy /
PowerShell paths.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces lazy initialization for the system clipboard in ClipboardHandler, using a background thread with a 500ms timeout to prevent startup hangs on headless environments or WSL2. Feedback indicates that the current implementation still blocks the main TUI thread during the first clipboard interaction, which could cause UI freezes; a background initialization during startup was suggested as an alternative. Additionally, it was recommended to gate the initialization call in the read method with #[cfg(not(test))] to ensure test suite performance is maintained in CI environments.

Comment on lines +86 to +102
fn ensure_clipboard(&mut self) {
if self.clipboard_init_attempted {
return;
}
self.clipboard_init_attempted = true;

let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let _ = tx.send(Clipboard::new().ok());
});
// 500 ms is generous for a local Unix socket connect — the
// kernel either answers or doesn't.
self.clipboard = rx
.recv_timeout(std::time::Duration::from_millis(500))
.ok()
.flatten();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This method blocks the main TUI thread for up to 500ms on the first clipboard interaction. In an async application, blocking the event loop for this long causes a noticeable freeze in the user interface.

Consider initiating the connection attempt in a background thread during ClipboardHandler::new and storing the Receiver. This would allow the connection to be established while the user is performing other startup tasks, making the first clipboard use near-instant without blocking the event loop.

/// `workspace` is used as a fallback location when `~/.deepseek/` cannot
/// be resolved (e.g. running with a stripped HOME in CI sandboxes).
pub fn read(&mut self, workspace: &Path) -> Option<ClipboardContent> {
self.ensure_clipboard();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This call to ensure_clipboard is not gated by #[cfg(not(test))], unlike the corresponding call in write_text. This will introduce a 500ms delay in any test that invokes read in a headless environment (like many CI runners) where the clipboard connection hangs. Gating this call ensures test suite performance is maintained.

Suggested change
self.ensure_clipboard();
#[cfg(not(test))]
self.ensure_clipboard();

@Hmbown Hmbown mentioned this pull request May 20, 2026
11 tasks
@Hmbown
Copy link
Copy Markdown
Owner

Hmbown commented May 21, 2026

Thanks for the X11-less startup fix. This was harvested into v0.8.40 release PR #1823 as commit 5c45232, and #1823 is now CI-green. Closing as superseded by the release branch; thank you for tightening host compatibility.

@Hmbown Hmbown closed this May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI hangs on startup with blank screen on WSL2 (no X server)

2 participants