Reset terminal modes before snapshot repaint
ci / test (push) Waiting to run
ci / fuzz-smoke (push) Waiting to run
ci / windows-client (push) Waiting to run
ci / package-release (linux-x86_64, ubuntu-latest) (push) Waiting to run
ci / package-release (macos-aarch64, macos-14) (push) Waiting to run
ci / package-release (macos-x86_64, macos-13) (push) Waiting to run
ci / package-release (windows-x86_64, windows-latest) (push) Waiting to run
ci / publish-gitea-release (push) Blocked by required conditions
ci / remote-bench (push) Waiting to run
ci / test (push) Waiting to run
ci / fuzz-smoke (push) Waiting to run
ci / windows-client (push) Waiting to run
ci / package-release (linux-x86_64, ubuntu-latest) (push) Waiting to run
ci / package-release (macos-aarch64, macos-14) (push) Waiting to run
ci / package-release (macos-x86_64, macos-13) (push) Waiting to run
ci / package-release (windows-x86_64, windows-latest) (push) Waiting to run
ci / publish-gitea-release (push) Blocked by required conditions
ci / remote-bench (push) Waiting to run
This commit is contained in:
+53
-9
@@ -9418,11 +9418,39 @@ async fn detach_once(socket: &UdpSocket, cred: &CachedCredential, seq: u64) -> R
|
||||
|
||||
fn render_frame(frame: &Frame) -> Result<()> {
|
||||
let mut stdout = std::io::stdout();
|
||||
stdout.write_all(&frame.bytes)?;
|
||||
stdout.write_all(&render_frame_bytes(frame))?;
|
||||
stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_frame_bytes(frame: &Frame) -> Vec<u8> {
|
||||
if !frame.snapshot {
|
||||
return frame.bytes.clone();
|
||||
}
|
||||
let mut bytes = Vec::with_capacity(TERMINAL_SNAPSHOT_RESET.len() + frame.bytes.len());
|
||||
bytes.extend_from_slice(TERMINAL_SNAPSHOT_RESET);
|
||||
bytes.extend_from_slice(&frame.bytes);
|
||||
bytes
|
||||
}
|
||||
|
||||
const TERMINAL_SNAPSHOT_RESET: &[u8] = concat!(
|
||||
"\x1b[0m",
|
||||
"\x1b[?25h",
|
||||
"\x1b[?1003l",
|
||||
"\x1b[?1002l",
|
||||
"\x1b[?1001l",
|
||||
"\x1b[?1000l",
|
||||
"\x1b[?1004l",
|
||||
"\x1b[?1015l",
|
||||
"\x1b[?1006l",
|
||||
"\x1b[?1005l",
|
||||
"\x1b[?2004l",
|
||||
"\x1b[?47l",
|
||||
"\x1b[?1047l",
|
||||
"\x1b[?1049l"
|
||||
)
|
||||
.as_bytes();
|
||||
|
||||
/// Seconds of silence from the server before the disconnect status line appears.
|
||||
/// Short enough to give quick feedback on a lost link, long enough that a normal
|
||||
/// idle period (the run loop only pings after 2s of quiet) never flashes it.
|
||||
@@ -9754,11 +9782,11 @@ mod tests {
|
||||
NativeIdentityContext, POST_RECONNECT_STALE_INPUT_GRACE, POST_SUBMIT_ALL_INPUT_HOLD,
|
||||
PendingStreamOpen, PredictMode, Predictor, RESTART_STATUS_SCRIPT, RemoteForward,
|
||||
STALE_TERMINAL_INPUT_AFTER, STARTUP_INPUT_HOLD, STREAM_INITIAL_WINDOW, SshConfig,
|
||||
SshPathTokenContext, StartupGateMode, StatusAction, UpdateOptions, UpdateRole, auth_allows,
|
||||
cache_key, cache_server_prefix, cleanup_stream_state, clear_cached_credentials,
|
||||
effective_update_artifact_tag, ensure_tui_safe_status_overlay, expand_ssh_path_tokens,
|
||||
input_contains_focus_in, input_matches_escape, is_local_status_target,
|
||||
is_resume_response_for_client, latest_release_download_url,
|
||||
SshPathTokenContext, StartupGateMode, StatusAction, TERMINAL_SNAPSHOT_RESET, UpdateOptions,
|
||||
UpdateRole, auth_allows, cache_key, cache_server_prefix, cleanup_stream_state,
|
||||
clear_cached_credentials, effective_update_artifact_tag, ensure_tui_safe_status_overlay,
|
||||
expand_ssh_path_tokens, input_contains_focus_in, input_matches_escape,
|
||||
is_local_status_target, is_resume_response_for_client, latest_release_download_url,
|
||||
load_first_native_identity_with_prompt, native_proxy_udp_warning,
|
||||
newest_client_trace_path_from, parse_dynamic_forward, parse_escape_key,
|
||||
parse_local_forward, parse_remote_forward, parse_single_remote_path, parse_ssh_config,
|
||||
@@ -9766,9 +9794,9 @@ mod tests {
|
||||
parse_update_options, post_submit_hold_duration, queue_or_send_stream_data,
|
||||
queue_pending_user_input, queue_stale_pending_user_input, raw_contains_host_table,
|
||||
recv_response_until, refresh_live_addr, release_tag_download_url,
|
||||
release_tag_from_effective_url, release_version_from_tag, render_status_clear,
|
||||
render_status_overlay, requested_env, resolved_startup_command, retire_stream_state,
|
||||
retransmit_stream_opens, rewrite_forward_command, sanitize_trace_name,
|
||||
release_tag_from_effective_url, release_version_from_tag, render_frame_bytes,
|
||||
render_status_clear, render_status_overlay, requested_env, resolved_startup_command,
|
||||
retire_stream_state, retransmit_stream_opens, rewrite_forward_command, sanitize_trace_name,
|
||||
selected_predict_mode, selected_udp_host, send_stream_eof, server_version_mismatch,
|
||||
should_flush_terminal_input_after_contact, should_health_log_client_start,
|
||||
should_hold_during_startup_gate, should_hold_post_submit_input,
|
||||
@@ -10424,6 +10452,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_render_resets_local_terminal_modes_before_authoritative_bytes() {
|
||||
let frame = test_frame(7, true);
|
||||
let bytes = render_frame_bytes(&frame);
|
||||
|
||||
assert!(bytes.starts_with(TERMINAL_SNAPSHOT_RESET));
|
||||
assert!(bytes.ends_with(b"frame-7"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn live_frame_render_preserves_terminal_output_exactly() {
|
||||
let frame = test_frame(7, false);
|
||||
|
||||
assert_eq!(render_frame_bytes(&frame), b"frame-7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frame_buffer_renders_only_contiguous_frames() {
|
||||
let mut buffer = FrameBuffer::default();
|
||||
|
||||
Reference in New Issue
Block a user