Stop repainting healthy idle TUIs
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s
ci / test (push) Canceled after 0s
ci / fuzz-smoke (push) Canceled after 0s
ci / macos-client (macos-aarch64, macos-14) (push) Canceled after 0s
ci / macos-client (macos-x86_64, macos-13) (push) Canceled after 0s
ci / windows-client (push) Canceled after 0s
ci / package-release (linux-x86_64, ubuntu-latest, , , ) (push) Canceled after 0s
ci / package-release (macos-aarch64, macos-14, , , ) (push) Canceled after 0s
ci / package-release (macos-x86_64, macos-13, , , ) (push) Canceled after 0s
ci / package-release (windows-aarch64, windows-latest, aarch64, windows, aarch64-pc-windows-msvc) (push) Canceled after 0s
ci / package-release (windows-x86_64, windows-latest, , , ) (push) Canceled after 0s
ci / remote-bench (push) Canceled after 0s
ci / publish-gitea-release (push) Canceled after 0s
This commit is contained in:
+18
-90
@@ -77,7 +77,6 @@ const POST_SUBMIT_ALL_INPUT_HOLD: Duration = Duration::from_millis(120);
|
||||
const STALE_TERMINAL_INPUT_AFTER: Duration = Duration::from_secs(2);
|
||||
const POST_RECONNECT_STALE_INPUT_GRACE: Duration = Duration::from_secs(5);
|
||||
const FOCUS_REPAINT_COOLDOWN: Duration = Duration::from_secs(1);
|
||||
const ALT_SCREEN_IDLE_REPAINT_AFTER: Duration = Duration::from_secs(15);
|
||||
const LOCAL_SLEEP_REPAINT_AFTER: Duration = Duration::from_secs(5);
|
||||
const LOCAL_SLEEP_REPAINT_RETRY_AFTER: Duration = Duration::from_secs(1);
|
||||
const LOCAL_SLEEP_REPAINT_RETRY_WINDOW: Duration = Duration::from_secs(10);
|
||||
@@ -6614,9 +6613,8 @@ async fn run_terminal(
|
||||
let mut startup_input_hold_until: Option<Instant> = None;
|
||||
let mut startup_gate_mode = StartupGateMode::HoldControl;
|
||||
let mut stale_terminal_input_suppress_until: Option<Instant> = None;
|
||||
let mut last_terminal_frame_at = Instant::now();
|
||||
let mut last_focus_repaint_at = Instant::now() - FOCUS_REPAINT_COOLDOWN;
|
||||
let mut last_idle_repaint_attempt_at = Instant::now() - ALT_SCREEN_IDLE_REPAINT_AFTER;
|
||||
let mut last_idle_repaint_attempt_at = Instant::now() - LOCAL_SLEEP_REPAINT_RETRY_AFTER;
|
||||
let mut last_status_tick_at = Instant::now();
|
||||
let mut wake_repaint_retry_until: Option<Instant> = None;
|
||||
if let Some(frame) = first_frame {
|
||||
@@ -6624,7 +6622,6 @@ async fn run_terminal(
|
||||
render_frame(&frame)?;
|
||||
note_snapshot_rendered(&frame, &mut disconnect_status, &mut status_restore_pending);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
}
|
||||
if frame.closed {
|
||||
return Ok(());
|
||||
@@ -6694,7 +6691,6 @@ async fn run_terminal(
|
||||
&mut disconnect_status,
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
send_ack(&socket, addr, &cred, &mut send_seq).await?;
|
||||
if frame.closed {
|
||||
@@ -6779,7 +6775,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
last_packet_at = Instant::now();
|
||||
last_focus_repaint_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
@@ -6839,7 +6834,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
last_packet_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
flush_pending_user_input(
|
||||
@@ -7063,7 +7057,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
}
|
||||
last_packet_at = Instant::now();
|
||||
flush_pending_user_input(
|
||||
@@ -7137,7 +7130,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
}
|
||||
last_packet_at = Instant::now();
|
||||
@@ -7202,7 +7194,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
}
|
||||
last_packet_at = Instant::now();
|
||||
@@ -7232,7 +7223,6 @@ async fn run_terminal(
|
||||
predictor.clear_pending()?;
|
||||
if !forward_only {
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
if render_resync_needed {
|
||||
if frame.closed {
|
||||
@@ -7388,7 +7378,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
}
|
||||
last_packet_at = Instant::now();
|
||||
@@ -8008,7 +7997,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
last_idle_repaint_attempt_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
repainted_this_tick = true;
|
||||
@@ -8058,8 +8046,6 @@ async fn run_terminal(
|
||||
.await?;
|
||||
let now = Instant::now();
|
||||
if !repainted_this_tick && !render_resync_needed && !frame_renderer.has_pending() && should_repaint_idle_terminal(
|
||||
predictor.alternate_screen,
|
||||
last_terminal_frame_at,
|
||||
last_idle_repaint_attempt_at,
|
||||
status_tick_gap,
|
||||
wake_repaint_retry_until,
|
||||
@@ -8095,7 +8081,6 @@ async fn run_terminal(
|
||||
&mut status_restore_pending,
|
||||
);
|
||||
predictor.observe_output(&frame.bytes);
|
||||
last_terminal_frame_at = Instant::now();
|
||||
last_packet_at = Instant::now();
|
||||
wake_repaint_retry_until = None;
|
||||
flush_pending_user_input(
|
||||
@@ -8670,8 +8655,6 @@ fn strip_terminal_focus_reports(bytes: &[u8]) -> Vec<u8> {
|
||||
}
|
||||
|
||||
fn should_repaint_idle_terminal(
|
||||
alternate_screen: bool,
|
||||
last_terminal_frame_at: Instant,
|
||||
last_attempt_at: Instant,
|
||||
status_tick_gap: Duration,
|
||||
wake_repaint_retry_until: Option<Instant>,
|
||||
@@ -8679,12 +8662,8 @@ fn should_repaint_idle_terminal(
|
||||
) -> bool {
|
||||
let sleep_wake_gap = status_tick_gap >= LOCAL_SLEEP_REPAINT_AFTER;
|
||||
let wake_retry_active = wake_repaint_retry_until.is_some_and(|deadline| now < deadline);
|
||||
let stale_alternate_screen = alternate_screen
|
||||
&& now.duration_since(last_terminal_frame_at) >= ALT_SCREEN_IDLE_REPAINT_AFTER;
|
||||
if sleep_wake_gap || wake_retry_active {
|
||||
return now.duration_since(last_attempt_at) >= LOCAL_SLEEP_REPAINT_RETRY_AFTER;
|
||||
}
|
||||
stale_alternate_screen && now.duration_since(last_attempt_at) >= ALT_SCREEN_IDLE_REPAINT_AFTER
|
||||
(sleep_wake_gap || wake_retry_active)
|
||||
&& now.duration_since(last_attempt_at) >= LOCAL_SLEEP_REPAINT_RETRY_AFTER
|
||||
}
|
||||
|
||||
fn arm_stale_terminal_input_suppression(suppress_until: &mut Option<Instant>) {
|
||||
@@ -11206,16 +11185,16 @@ const TERMINAL_CLEANUP: &[u8] = concat!(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
ALT_SCREEN_IDLE_REPAINT_AFTER, CachedCredential, DisconnectStatus, DynamicForward,
|
||||
FRAME_GAP_RESYNC_AFTER_MS, FrameBuffer, LOCAL_SLEEP_REPAINT_AFTER,
|
||||
LOCAL_SLEEP_REPAINT_RETRY_WINDOW, LocalForward, MAX_PENDING_USER_INPUT_BYTES,
|
||||
NativeIdentityContext, POST_RECONNECT_STALE_INPUT_GRACE, POST_SUBMIT_ALL_INPUT_HOLD,
|
||||
PendingStreamControl, PendingStreamOpen, PendingWindowAdjust, PredictMode, Predictor,
|
||||
RESTART_STATUS_SCRIPT, RemoteForward, STALE_TERMINAL_INPUT_AFTER, STARTUP_INPUT_HOLD,
|
||||
STDIN_QUEUE_CAPACITY, STREAM_CONTROL_RETRANSMIT_MAX_ATTEMPTS, STREAM_INITIAL_WINDOW,
|
||||
SshConfig, SshPathTokenContext, StartupGateMode, StatusAction, TERMINAL_CLEANUP,
|
||||
TERMINAL_SNAPSHOT_RESET, UpdateOptions, UpdateRole, auth_allows, cache_key,
|
||||
cache_server_prefix, cleanup_stream_state, clear_cached_credentials,
|
||||
CachedCredential, DisconnectStatus, DynamicForward, FRAME_GAP_RESYNC_AFTER_MS, FrameBuffer,
|
||||
LOCAL_SLEEP_REPAINT_AFTER, LOCAL_SLEEP_REPAINT_RETRY_AFTER,
|
||||
LOCAL_SLEEP_REPAINT_RETRY_WINDOW, LocalForward,
|
||||
MAX_PENDING_USER_INPUT_BYTES, NativeIdentityContext, POST_RECONNECT_STALE_INPUT_GRACE,
|
||||
POST_SUBMIT_ALL_INPUT_HOLD, PendingStreamControl, PendingStreamOpen, PendingWindowAdjust,
|
||||
PredictMode, Predictor, RESTART_STATUS_SCRIPT, RemoteForward, STALE_TERMINAL_INPUT_AFTER,
|
||||
STARTUP_INPUT_HOLD, STDIN_QUEUE_CAPACITY, STREAM_CONTROL_RETRANSMIT_MAX_ATTEMPTS,
|
||||
STREAM_INITIAL_WINDOW, SshConfig, SshPathTokenContext, StartupGateMode, StatusAction,
|
||||
TERMINAL_CLEANUP, TERMINAL_SNAPSHOT_RESET, UpdateOptions, UpdateRole, auth_allows,
|
||||
cache_key, cache_server_prefix, cleanup_stream_state, clear_cached_credentials,
|
||||
ensure_tui_safe_status_overlay, expand_ssh_path_tokens, first_resolved_addr,
|
||||
imported_host_block, input_contains_focus_in, input_prefix_before_escape,
|
||||
is_local_status_target, is_resume_response_for_client,
|
||||
@@ -14478,86 +14457,35 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_repaint_runs_for_stale_alternate_screen_or_sleep_gap() {
|
||||
fn idle_repaint_runs_only_after_sleep_or_an_armed_wake_retry() {
|
||||
let now = Instant::now();
|
||||
let stale = now - ALT_SCREEN_IDLE_REPAINT_AFTER - Duration::from_secs(1);
|
||||
let recent = now - Duration::from_secs(1);
|
||||
let stale = now - LOCAL_SLEEP_REPAINT_RETRY_AFTER - Duration::from_secs(1);
|
||||
let just_attempted = now - Duration::from_millis(500);
|
||||
assert!(should_repaint_idle_terminal(
|
||||
true,
|
||||
stale,
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
None,
|
||||
now
|
||||
));
|
||||
assert!(should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
stale,
|
||||
LOCAL_SLEEP_REPAINT_AFTER + Duration::from_secs(1),
|
||||
None,
|
||||
now
|
||||
));
|
||||
assert!(should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
recent,
|
||||
LOCAL_SLEEP_REPAINT_AFTER + Duration::from_secs(1),
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
Some(now + LOCAL_SLEEP_REPAINT_RETRY_WINDOW),
|
||||
now
|
||||
));
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
just_attempted,
|
||||
LOCAL_SLEEP_REPAINT_AFTER + Duration::from_secs(1),
|
||||
Some(now + LOCAL_SLEEP_REPAINT_RETRY_WINDOW),
|
||||
now
|
||||
));
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
false,
|
||||
stale,
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
None,
|
||||
now
|
||||
));
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
true,
|
||||
recent,
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
None,
|
||||
now
|
||||
));
|
||||
assert!(should_repaint_idle_terminal(
|
||||
true,
|
||||
stale,
|
||||
recent,
|
||||
LOCAL_SLEEP_REPAINT_AFTER + Duration::from_secs(1),
|
||||
None,
|
||||
now
|
||||
));
|
||||
assert!(should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
Some(now + LOCAL_SLEEP_REPAINT_RETRY_WINDOW),
|
||||
now
|
||||
));
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
just_attempted,
|
||||
Duration::from_secs(1),
|
||||
Some(now + LOCAL_SLEEP_REPAINT_RETRY_WINDOW),
|
||||
now
|
||||
));
|
||||
assert!(!should_repaint_idle_terminal(
|
||||
false,
|
||||
recent,
|
||||
stale,
|
||||
Duration::from_secs(1),
|
||||
Some(now - Duration::from_millis(1)),
|
||||
|
||||
Reference in New Issue
Block a user