diff --git a/codex-rs/cloud-tasks/src/lib.rs b/codex-rs/cloud-tasks/src/lib.rs index f96ac62ed9..b59a990968 100644 --- a/codex-rs/cloud-tasks/src/lib.rs +++ b/codex-rs/cloud-tasks/src/lib.rs @@ -763,6 +763,7 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option) -> an let backend = backend; // Terminal setup + use codex_tui::should_enable_keyboard_enhancement; use crossterm::ExecutableCommand; use crossterm::event::DisableBracketedPaste; use crossterm::event::EnableBracketedPaste; @@ -781,14 +782,16 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option) -> an stdout.execute(EnableBracketedPaste)?; // Enable enhanced key reporting so Shift+Enter is distinguishable from Enter. // Some terminals may not support these flags; ignore errors if enabling fails. - let _ = crossterm::execute!( - std::io::stdout(), - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_EVENT_TYPES - | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS - ) - ); + if should_enable_keyboard_enhancement() { + let _ = crossterm::execute!( + std::io::stdout(), + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) + ); + } let backend_ui = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend_ui)?; terminal.clear()?; diff --git a/codex-rs/tui/src/keyboard_enhancement.rs b/codex-rs/tui/src/keyboard_enhancement.rs new file mode 100644 index 0000000000..79e8460443 --- /dev/null +++ b/codex-rs/tui/src/keyboard_enhancement.rs @@ -0,0 +1,94 @@ +use codex_terminal_detection::TerminalInfo; +use codex_terminal_detection::TerminalName; +use codex_terminal_detection::terminal_info; + +/// Returns whether Codex should enable progressive keyboard enhancement flags. +/// +/// We currently disable them for the VS Code integrated terminal under WSL +/// because that terminal stack partially supports the protocol and breaks +/// dead-key composition for some keyboard layouts. +pub fn should_enable_keyboard_enhancement() -> bool { + should_enable_keyboard_enhancement_for(terminal_info(), process_is_wsl()) +} + +/// Pure-logic helper for [`should_enable_keyboard_enhancement`], separated so +/// tests can inject terminal identity and WSL status without touching the +/// process environment or filesystem. +/// +/// The rule is a denylist: disable enhancement only for known-broken +/// combinations. Today, the only entry is VS Code under WSL, where the +/// terminal's partial protocol support corrupts dead-key composition sequences +/// for layouts that rely on them (e.g. Portuguese, French, Spanish). +fn should_enable_keyboard_enhancement_for(terminal: TerminalInfo, is_wsl: bool) -> bool { + !(is_wsl && matches!(terminal.name, TerminalName::VsCode)) +} + +/// Detects whether the current process is running inside Windows Subsystem for Linux. +/// +/// Uses a three-tier heuristic (checked only on Linux; returns `false` on all other targets): +/// 1. `WSL_DISTRO_NAME` environment variable (set by WSL 2). +/// 2. `WSL_INTEROP` environment variable (set by WSL 1 and some WSL 2 configurations). +/// 3. `/proc/version` containing "microsoft" (fallback for minimal WSL images that +/// strip environment variables). +fn process_is_wsl() -> bool { + #[cfg(target_os = "linux")] + { + if std::env::var_os("WSL_DISTRO_NAME").is_some() + || std::env::var_os("WSL_INTEROP").is_some() + { + return true; + } + match std::fs::read_to_string("/proc/version") { + Ok(version) => version.to_lowercase().contains("microsoft"), + Err(_) => false, + } + } + #[cfg(not(target_os = "linux"))] + { + false + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn disables_keyboard_enhancement_for_vscode_under_wsl() { + assert!(!should_enable_keyboard_enhancement_for( + TerminalInfo { + name: TerminalName::VsCode, + term_program: None, + version: None, + term: None, + multiplexer: None, + }, + /*is_wsl*/ true, + )); + } + + #[test] + fn keeps_keyboard_enhancement_enabled_outside_wsl_vscode() { + assert!(should_enable_keyboard_enhancement_for( + TerminalInfo { + name: TerminalName::VsCode, + term_program: None, + version: None, + term: None, + multiplexer: None, + }, + /*is_wsl*/ false, + )); + + assert!(should_enable_keyboard_enhancement_for( + TerminalInfo { + name: TerminalName::AppleTerminal, + term_program: None, + version: None, + term: None, + multiplexer: None, + }, + /*is_wsl*/ true, + )); + } +} diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 4c31516b5b..2ffe4a7293 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -115,6 +115,7 @@ mod get_git_diff; mod history_cell; pub mod insert_history; mod key_hint; +mod keyboard_enhancement; mod line_truncation; pub mod live_wrap; mod local_chatgpt_auth; @@ -216,6 +217,8 @@ pub mod test_backend; #[cfg(test)] pub(crate) mod test_support; +pub use keyboard_enhancement::should_enable_keyboard_enhancement; + use crate::onboarding::onboarding_screen::OnboardingScreenArgs; use crate::onboarding::onboarding_screen::run_onboarding_app; use crate::tui::Tui; diff --git a/codex-rs/tui/src/tui.rs b/codex-rs/tui/src/tui.rs index fc5d350527..00f1f02b87 100644 --- a/codex-rs/tui/src/tui.rs +++ b/codex-rs/tui/src/tui.rs @@ -42,6 +42,7 @@ use crate::custom_terminal; use crate::custom_terminal::Terminal as CustomTerminal; use crate::notifications::DesktopNotificationBackend; use crate::notifications::detect_backend; +use crate::should_enable_keyboard_enhancement; use crate::tui::event_stream::EventBroker; use crate::tui::event_stream::TuiEventStream; #[cfg(unix)] @@ -70,14 +71,16 @@ pub fn set_modes() -> Result<()> { // Some terminals (notably legacy Windows consoles) do not support // keyboard enhancement flags. Attempt to enable them, but continue // gracefully if unsupported. - let _ = execute!( - stdout(), - PushKeyboardEnhancementFlags( - KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES - | KeyboardEnhancementFlags::REPORT_EVENT_TYPES - | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS - ) - ); + if should_enable_keyboard_enhancement() { + let _ = execute!( + stdout(), + PushKeyboardEnhancementFlags( + KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + | KeyboardEnhancementFlags::REPORT_EVENT_TYPES + | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS + ) + ); + } let _ = execute!(stdout(), EnableFocusChange); Ok(()) @@ -266,7 +269,8 @@ impl Tui { // Detect keyboard enhancement support before any EventStream is created so the // crossterm poller can acquire its lock without contention. - let enhanced_keys_supported = supports_keyboard_enhancement().unwrap_or(false); + let enhanced_keys_supported = should_enable_keyboard_enhancement() + && supports_keyboard_enhancement().unwrap_or(false); // Cache this to avoid contention with the event reader. supports_color::on_cached(supports_color::Stream::Stdout); let _ = crate::terminal_palette::default_colors();