diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 049cc917..5ec773ad 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -881,6 +881,7 @@ dependencies = [ "image", "insta", "lazy_static", + "libc", "mcp-types", "path-clean", "pretty_assertions", diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index 719c6311..31f198f5 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -72,6 +72,9 @@ unicode-segmentation = "1.12.0" unicode-width = "0.1" uuid = "1" +[target.'cfg(unix)'.dependencies] +libc = "0.2" + [dev-dependencies] chrono = { version = "0.4", features = ["serde"] } diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index e3290e10..ebce2699 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -255,9 +255,11 @@ impl App<'_> { kind: KeyEventKind::Press, .. } => { - if let AppState::Chat { widget } = &mut self.app_state { - widget.on_ctrl_z(); + #[cfg(unix)] + { + self.suspend(terminal)?; } + // No-op on non-Unix platforms. } KeyEvent { code: KeyCode::Char('d'), @@ -459,6 +461,23 @@ impl App<'_> { Ok(()) } + #[cfg(unix)] + fn suspend(&mut self, terminal: &mut tui::Tui) -> Result<()> { + tui::restore()?; + // SAFETY: Unix-only code path. We intentionally send SIGTSTP to the + // current process group (pid 0) to trigger standard job-control + // suspension semantics. This FFI does not involve any raw pointers, + // is not called from a signal handler, and uses a constant signal. + // Errors from kill are acceptable (e.g., if already stopped) — the + // subsequent re-init path will still leave the terminal in a good state. + // We considered `nix`, but didn't think it was worth pulling in for this one call. + unsafe { libc::kill(0, libc::SIGTSTP) }; + *terminal = tui::init(&self.config)?; + terminal.clear()?; + self.app_event_tx.send(AppEvent::RequestRedraw); + Ok(()) + } + pub(crate) fn token_usage(&self) -> codex_core::protocol::TokenUsage { match &self.app_state { AppState::Chat { widget } => widget.token_usage().clone(), diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 6719cfb7..3d312ffc 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -652,10 +652,6 @@ impl ChatWidget<'_> { } } - pub(crate) fn on_ctrl_z(&mut self) { - self.interrupt_running_task(); - } - pub(crate) fn composer_is_empty(&self) -> bool { self.bottom_pane.composer_is_empty() }