diff --git a/Cargo.toml b/Cargo.toml index 1d68593..7e902c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ vt100 = "0.15" tempfile = "3.14" [profile.release] -codegen-units = 1 -lto = "thin" -strip = true +opt-level = 2 +codegen-units = 16 +incremental = true +lto = false +strip = false diff --git a/src/bin/dosh-server.rs b/src/bin/dosh-server.rs index 2b7b8e5..09f265d 100644 --- a/src/bin/dosh-server.rs +++ b/src/bin/dosh-server.rs @@ -277,7 +277,7 @@ async fn handle_bootstrap_attach( session.parser.set_size(req.rows, req.cols); } let client_id = crypto::random_16(); - let snapshot = session.parser.screen().state_formatted(); + let snapshot = screen_snapshot(session.parser.screen()); let screen = session.parser.screen().clone(); let output_seq = session.output_seq; session.clients.insert( @@ -385,7 +385,7 @@ async fn handle_ticket_attach( session.parser.set_size(req.rows, req.cols); } let client_id = crypto::random_16(); - let snapshot = session.parser.screen().state_formatted(); + let snapshot = screen_snapshot(session.parser.screen()); let screen = session.parser.screen().clone(); let output_seq = session.output_seq; session.clients.insert( @@ -491,7 +491,7 @@ async fn handle_resume( session.pty.resize(req.cols, req.rows)?; session.parser.set_size(req.rows, req.cols); } - let snapshot = session.parser.screen().state_formatted(); + let snapshot = screen_snapshot(session.parser.screen()); client.last_screen = Some(session.parser.screen().clone()); (client.send_seq, session.output_seq, snapshot) }; @@ -675,7 +675,7 @@ async fn broadcast_output( let current_screen = session.parser.screen().clone(); let (bytes, snapshot) = if client.pending.len() >= retransmit_window { client.pending.clear(); - (current_screen.state_formatted(), true) + (screen_snapshot(¤t_screen), true) } else { (output.bytes.clone(), false) }; @@ -758,6 +758,17 @@ async fn broadcast_session_exit( Ok(()) } +fn screen_snapshot(screen: &vt100::Screen) -> Vec { + let mut bytes = Vec::new(); + if screen.alternate_screen() { + bytes.extend_from_slice(b"\x1b[?1049h"); + } else { + bytes.extend_from_slice(b"\x1b[?1049l"); + } + bytes.extend(screen.state_formatted()); + bytes +} + async fn retransmit_pending( state: &Arc>, socket: &Arc, diff --git a/tests/integration_smoke.rs b/tests/integration_smoke.rs index ebe4207..35630d9 100644 --- a/tests/integration_smoke.rs +++ b/tests/integration_smoke.rs @@ -572,6 +572,63 @@ fn live_output_forwards_terminal_control_sequences() { ); } +#[test] +fn resume_snapshot_preserves_alternate_screen_mode() { + let dir = tempfile::tempdir().unwrap(); + let port = free_udp_port(); + let config = write_server_config(&dir, port); + let mut server = start_server(&dir, &config); + let (socket, bootstrap, ok) = direct_attach(&config, port, "read-write"); + + let input = Input { + bytes: b"printf '\\033[?1049hALT_SNAPSHOT'\n".to_vec(), + }; + send_encrypted( + &socket, + port, + PacketKind::Input, + ok.client_id, + 2, + 0, + &bootstrap.session_key, + &protocol::to_body(&input).unwrap(), + ); + let _ = collect_frame_text(&socket, &bootstrap.session_key, 500); + let new_socket = UdpSocket::bind("127.0.0.1:0").unwrap(); + new_socket + .set_read_timeout(Some(Duration::from_secs(2))) + .unwrap(); + + let resume = ResumeRequest { + session: "default".to_string(), + last_rendered_seq: ok.initial_seq, + cols: 80, + rows: 24, + }; + send_encrypted( + &new_socket, + port, + PacketKind::ResumeRequest, + ok.client_id, + 3, + 0, + &bootstrap.session_key, + &protocol::to_body(&resume).unwrap(), + ); + let (_header, resume_frame) = + recv_frame(&new_socket, &bootstrap.session_key).expect("resume response"); + + let _ = server.kill(); + let _ = server.wait(); + + assert!(resume_frame.snapshot); + assert!( + resume_frame.bytes.starts_with(b"\x1b[?1049h"), + "snapshot did not enter alternate screen first: {:?}", + String::from_utf8_lossy(&resume_frame.bytes) + ); +} + #[test] fn resume_updates_udp_endpoint_for_roaming() { let dir = tempfile::tempdir().unwrap();