// suspend TUI, stop stdin-reader to avoid SIGTTIN, run cmd on real TTY, resume async fn run_external(app: &mut App, cmdline: &str, _ui_tx: mpsc::Sender) -> bool { // Stop stdin reader so we don't read while backgrounded if let Some(h) = app.stdin_task.take() { h.abort(); let _ = h.await; } // Leave alt-screen and show cursor let _ = std::io::Write::write_all(app.terminal.backend_mut(), b"\x1b[?1049l\x1b[?25h\x1b[0m"); let _ = app.terminal.backend_mut().flush(); // Restore cooked while external program runs. set_tty_cooked(&app.saved_termios); // Run synchronously. let status = std::process::Command::new("sh") .arg("-lc") .arg(cmdline) .status(); // Re-enter alt-screen, hide cursor and restore RAW. let _ = std::io::Write::write_all(app.terminal.backend_mut(), b"\x1b[?1049h\x1b[?25l"); let _ = app.terminal.backend_mut().flush(); // Switch terminal back to RAW (so keystrokes are delivered immediately) set_tty_raw_from_saved(&app.saved_termios); // Re-apply winsize (external may have resized) if let Ok(sz) = app.terminal.size() { let area = ratatui::layout::Rect::new(0, 0, sz.width, sz.height); app.area = area; set_pty_winsize(&app.pty_master_cmd, area); if let Some(ref fd) = app.pty_master_ipc { set_pty_winsize(fd, area); } } // Redraw, refresh title and respawn stdin reader. app.force_redraw(); app.update_osc_title_for_tab(); spawn_stdin_reader(app); match status { Ok(st) => { let msg = if let Some(c) = st.code() { format!(":! {} exited with code {}", cmdline, c) } else { #[cfg(unix)] { use std::os::unix::process::ExitStatusExt; if let Some(sig) = st.signal() { format!(":! {} killed by signal {}", cmdline, sig) } else { format!(":! {} exited", cmdline) } } #[cfg(not(unix))] { format!(":! {} exited", cmdline) } }; app.push_msg_plain(msg); } Err(e) => { app.push_msg_plain(format!(":! {} failed: {}", cmdline, e)); } } false }