From 61f6400675b065bf8f801816ed78c1f3206bcc06 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Sun, 26 Jul 2026 15:05:28 +0300 Subject: fix: repaint after transient terminal resizes --- src/platform/mod.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'src/platform') diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 9b763223..ba92ca63 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -78,6 +78,45 @@ pub fn current_process_is_detached_server_daemon() -> bool { unsafe { libc::getsid(0) == libc::getpid() } } +/// Raised by the SIGWINCH handler, consumed by the host resize watcher. +#[cfg(unix)] +static TERMINAL_RESIZE_SIGNALLED: std::sync::atomic::AtomicBool = + std::sync::atomic::AtomicBool::new(false); + +#[cfg(unix)] +extern "C" fn record_terminal_resize_signal(_signal: libc::c_int) { + TERMINAL_RESIZE_SIGNALLED.store(true, std::sync::atomic::Ordering::Release); +} + +/// Records SIGWINCH events that size polling can miss. +#[cfg(unix)] +pub(crate) fn watch_terminal_resize_signal() { + let mut action: libc::sigaction = unsafe { std::mem::zeroed() }; + action.sa_sigaction = + record_terminal_resize_signal as extern "C" fn(libc::c_int) as libc::sighandler_t; + // Keep blocking stdin and socket reads from failing with EINTR. + action.sa_flags = libc::SA_RESTART; + unsafe { + libc::sigemptyset(&mut action.sa_mask); + libc::sigaction(libc::SIGWINCH, &action, std::ptr::null_mut()); + } +} + +#[cfg(not(unix))] +pub(crate) fn watch_terminal_resize_signal() {} + +/// Returns whether a terminal size change was signalled since the last call. +#[cfg(unix)] +pub(crate) fn take_terminal_resize_signal() -> bool { + TERMINAL_RESIZE_SIGNALLED.swap(false, std::sync::atomic::Ordering::AcqRel) +} + +/// Windows relies on size polling. +#[cfg(not(unix))] +pub(crate) fn take_terminal_resize_signal() -> bool { + false +} + #[cfg(unix)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct ClipboardCommand { @@ -312,6 +351,19 @@ impl PrefixInputSource for RealPrefixInputSource { mod tests { use super::*; + #[test] + fn terminal_resize_signal_is_recorded_once_per_delivery() { + watch_terminal_resize_signal(); + assert!(!take_terminal_resize_signal()); + + unsafe { + libc::raise(libc::SIGWINCH); + } + + assert!(take_terminal_resize_signal()); + assert!(!take_terminal_resize_signal()); + } + #[test] fn pane_shell_process_names_reject_exec_replacement_programs() { for shell in ["bash", "-zsh", "/bin/fish", "pwsh", "powershell.exe"] { -- cgit v1.2.3