From 2d24950ad9a02096921bc764e2e3dcd8900c3366 Mon Sep 17 00:00:00 2001 From: akbash Date: Tue, 18 Aug 2026 04:37:11 +0300 Subject: fix: isolate hidden pane render cadence (#2892) refs #2890 Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> --- src/app/mod.rs | 6 +++++- src/app/runtime.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'src/app') diff --git a/src/app/mod.rs b/src/app/mod.rs index 2095b99e..501f39f6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -149,7 +149,10 @@ pub struct App { /// Parsed `ui.window_title` plus the hostname resolved when it was applied. window_title_template: Option<(crate::config::WindowTitleTemplate, String)>, pub(crate) persist_pane_history: bool, + /// Last render-loop attempt, including a throttled hidden-only PTY skip. pub(crate) last_render_at: Option, + /// Last attempt that could update a connected presentation surface. + pub(crate) last_presentation_at: Option, pub(crate) input_leases: input::InputLeaseTable, pub render_notify: Arc, pub(crate) render_dirty: Arc, @@ -783,6 +786,7 @@ impl App { selection_highlight_clear_deadline: None, persist_pane_history: config.experimental.pane_history, last_render_at: None, + last_presentation_at: None, input_leases: input::InputLeaseTable::default(), api_rx, event_hub, @@ -1133,7 +1137,7 @@ impl App { self.render_dirty.request_generic(); self.render_notify.notify_one(); } - self.last_render_at = Some(now); + self.record_render_attempt(now, true); needs_render = false; continue; } diff --git a/src/app/runtime.rs b/src/app/runtime.rs index d7023727..8367a223 100644 --- a/src/app/runtime.rs +++ b/src/app/runtime.rs @@ -534,6 +534,22 @@ impl App { } } + pub(crate) fn can_present_now(&self, now: Instant) -> bool { + match self.last_presentation_at { + Some(last_presentation_at) => { + now.duration_since(last_presentation_at) >= MIN_RENDER_INTERVAL + } + None => true, + } + } + + pub(crate) fn record_render_attempt(&mut self, now: Instant, presentation: bool) { + self.last_render_at = Some(now); + if presentation { + self.last_presentation_at = Some(now); + } + } + pub(crate) fn run_auto_update_check(&mut self) { if !background_update_check_enabled(self.no_session, self.update_version_check_enabled) { self.next_auto_update_check = None; @@ -657,6 +673,20 @@ mod tests { use crate::app::state; use crate::workspace::Workspace; + #[test] + fn hidden_render_attempt_keeps_presentation_cadence_available() { + let (mut app, _) = test_app_with_pane(); + let initial_presentation = Instant::now(); + app.record_render_attempt(initial_presentation, true); + + let hidden_attempt = initial_presentation + MIN_RENDER_INTERVAL; + app.record_render_attempt(hidden_attempt, false); + let foreground_echo = hidden_attempt + Duration::from_millis(1); + + assert!(!app.can_render_now(foreground_echo)); + assert!(app.can_present_now(foreground_echo)); + } + #[test] fn interrupted_detached_process_wait_keeps_child_for_retry() { let interrupted = std::io::Error::new(std::io::ErrorKind::Interrupted, "test interrupt"); -- cgit v1.2.3