From cb06df63312eee11006eead7153db061fd02820d Mon Sep 17 00:00:00 2001 From: JJ Liebig Date: Sun, 13 Sep 2026 19:43:13 +0400 Subject: [PATCH] fix(windows): disable host mouse reporting on terminal restore (#4055) Windows skipped the xterm mouse-reporting reset because clear_host_mouse_reporting was a no-op there. Standalone Git Bash (mintty) keeps SGR mouse reporting enabled after detach, so mouse motion leaks escape sequences into the shell. Emit the reset sequence on Windows too and include the X10 mode. refs #3748 --- src/client/mod.rs | 4 ++++ src/terminal_modes.rs | 15 +++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index b815a057..af2e92c8 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -142,6 +142,10 @@ fn run_client_with_mode( init_logging(); let loaded_config = crate::config::Config::load(); + // Windows may not have virtual terminal processing enabled until the rendered + // client initializes the terminal, so defer the host mouse reset to + // `setup_terminal_with_capabilities` instead of emitting raw escapes early. + #[cfg(not(windows))] crate::terminal_modes::clear_host_mouse_reporting(&mut io::stdout())?; let client_rendered_shell = attach_request.is_none(); let socket_path = client_socket_path(); diff --git a/src/terminal_modes.rs b/src/terminal_modes.rs index b38d251b..e6b15d36 100644 --- a/src/terminal_modes.rs +++ b/src/terminal_modes.rs @@ -1,8 +1,7 @@ use std::io::{self, Write}; -#[cfg(any(not(windows), test))] const DISABLE_HOST_MOUSE_REPORTING_SEQUENCE: &[u8] = - b"\x1b[?1006l\x1b[?1016l\x1b[?1015l\x1b[?1005l\x1b[?1003l\x1b[?1002l\x1b[?1000l"; + b"\x1b[?1006l\x1b[?1016l\x1b[?1015l\x1b[?1005l\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?9l"; #[cfg(any(windows, test))] const WINDOWS_SSH_MOUSE_REPORTING_ENABLE_SEQUENCE: &[u8] = @@ -11,17 +10,11 @@ const WINDOWS_SSH_MOUSE_REPORTING_ENABLE_SEQUENCE: &[u8] = const WINDOWS_SSH_MOUSE_REPORTING_DISABLE_SEQUENCE: &[u8] = b"\x1b[?1016l\x1b[?1006l\x1b[?1003l\x1b[?1002l\x1b[?1000l"; -#[cfg(not(windows))] pub(crate) fn clear_host_mouse_reporting(writer: &mut W) -> io::Result<()> { writer.write_all(DISABLE_HOST_MOUSE_REPORTING_SEQUENCE)?; writer.flush() } -#[cfg(windows)] -pub(crate) fn clear_host_mouse_reporting(_writer: &mut W) -> io::Result<()> { - Ok(()) -} - #[cfg(any(windows, test))] pub(crate) fn set_windows_ssh_mouse_reporting( writer: &mut W, @@ -182,9 +175,11 @@ mod tests { #[test] fn clears_all_known_host_mouse_modes() { - let sequence = std::str::from_utf8(DISABLE_HOST_MOUSE_REPORTING_SEQUENCE).unwrap(); + let mut output = Vec::new(); + clear_host_mouse_reporting(&mut output).unwrap(); + let sequence = std::str::from_utf8(&output).unwrap(); - for mode in ["1000", "1002", "1003", "1005", "1006", "1015", "1016"] { + for mode in ["9", "1000", "1002", "1003", "1005", "1006", "1015", "1016"] { assert!( sequence.contains(&format!("\x1b[?{mode}l")), "missing mouse mode {mode}"