From b660cad80450147610091e0e5758faa6276e5983 Mon Sep 17 00:00:00 2001 From: Can Celik Date: Sat, 15 Aug 2026 15:03:59 +0300 Subject: fix(client): handle remote terminal hangup (#2827) refs #2424 --- src/client/mod.rs | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) (limited to 'src/client') diff --git a/src/client/mod.rs b/src/client/mod.rs index cba735fa..b70b6c9c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -421,6 +421,7 @@ fn setup_terminal_with_capabilities( Ok(TerminalGuard { reset_modify_other_keys: modify_other_keys_mode.is_some(), reset_host_color_scheme_reports: host_color_scheme_reports, + restored: false, #[cfg(windows)] restore_windows_input_mode: windows_virtual_terminal_input.restore_mode, }) @@ -434,6 +435,7 @@ fn should_enable_host_color_scheme_reports(enable_client_protocols: bool) -> boo struct TerminalGuard { reset_modify_other_keys: bool, reset_host_color_scheme_reports: bool, + restored: bool, #[cfg(windows)] restore_windows_input_mode: Option, } @@ -582,7 +584,7 @@ fn restore_terminal_state( reset_modify_other_keys: bool, reset_host_color_scheme_reports: bool, #[cfg(windows)] restore_windows_input_mode: Option, -) { +) -> io::Result<()> { let _ = clear_received_kitty_graphics(&mut io::stdout()); // Reset modifyOtherKeys if we enabled it. @@ -606,13 +608,16 @@ fn restore_terminal_state( restore_windows_input_mode_value(mode); } - let _ = ratatui::try_restore(); - let _ = write_terminal_restore_postlude(&mut io::stdout(), reset_host_color_scheme_reports); + let restore_result = ratatui::try_restore(); + let postlude_result = + write_terminal_restore_postlude(&mut io::stdout(), reset_host_color_scheme_reports); #[cfg(windows)] if windows_vti_input_backend_enabled() && windows_win32_input_mode_enabled() { let _ = disable_windows_win32_input_mode(&mut io::stdout()); } + + restore_result.and(postlude_result) } #[cfg(not(windows))] @@ -657,14 +662,28 @@ fn disable_windows_win32_input_mode(writer: &mut impl std::io::Write) -> io::Res writer.flush() } -impl Drop for TerminalGuard { - fn drop(&mut self) { +impl TerminalGuard { + fn restore(mut self) -> io::Result<()> { + self.restored = true; restore_terminal_state( self.reset_modify_other_keys, self.reset_host_color_scheme_reports, #[cfg(windows)] self.restore_windows_input_mode, - ); + ) + } +} + +impl Drop for TerminalGuard { + fn drop(&mut self) { + if !self.restored { + let _ = restore_terminal_state( + self.reset_modify_other_keys, + self.reset_host_color_scheme_reports, + #[cfg(windows)] + self.restore_windows_input_mode, + ); + } } } @@ -1281,7 +1300,7 @@ fn run_client_with_mode( let panic_restore_windows_input_mode = terminal_guard.restore_windows_input_mode; let original_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(move |info| { - restore_terminal_state( + let _ = restore_terminal_state( panic_resets_modify_other_keys, panic_resets_host_color_scheme_reports, #[cfg(windows)] @@ -1323,19 +1342,22 @@ fn run_client_with_mode( }); // Restore the terminal before printing any final status message. - drop(terminal_guard); + let terminal_restore_failed = terminal_guard.restore().is_err(); if let Err(err) = result { - eprintln!("herdr: {err}"); + let _ = writeln!(io::stderr(), "herdr: {err}"); rt.shutdown_timeout(Duration::from_millis(100)); crate::logging::shutdown("client"); - if matches!( - err, + let detached = matches!( + &err, ClientError::ServerShutdown { reason: Some(reason) } if reason == "detached" - ) { + ); + let connection_lost_during_terminal_hangup = + terminal_restore_failed && matches!(&err, ClientError::ConnectionLost(_)); + if detached || connection_lost_during_terminal_hangup { return Ok(()); } -- cgit v1.2.3