mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
fix(tui): avoid flaky keyboard enhancement on Windows Terminal (fixes #514)
This commit is contained in:
@@ -101,7 +101,8 @@ impl App<'_> {
|
||||
let post_frame_redraw = Arc::new(AtomicBool::new(false));
|
||||
let frame_timer = Arc::new(FrameTimer::new());
|
||||
|
||||
let enhanced_keys_supported = supports_keyboard_enhancement().unwrap_or(false);
|
||||
let enhanced_keys_supported = supports_keyboard_enhancement().unwrap_or(false)
|
||||
&& crate::tui::should_enable_keyboard_enhancement();
|
||||
|
||||
// Spawn a dedicated thread for reading the crossterm event loop and
|
||||
// re-publishing the events as AppEvents, as appropriate.
|
||||
|
||||
+78
-10
@@ -106,12 +106,11 @@ pub fn init(config: &Config) -> Result<(Tui, TerminalInfo)> {
|
||||
let terminal_info = query_terminal_info();
|
||||
|
||||
enable_raw_mode()?;
|
||||
// Enable keyboard enhancement flags only when supported. On some Windows 10
|
||||
// consoles/environments, attempting to push these flags can interfere with
|
||||
// input delivery (reported as a freeze where keypresses don’t register).
|
||||
// We already normalize key kinds when enhancement is unsupported elsewhere,
|
||||
// so it’s safe to skip enabling here.
|
||||
if supports_keyboard_enhancement().unwrap_or(false) {
|
||||
// Enable keyboard enhancement flags only when supported *and* the current
|
||||
// terminal environment is known to handle them reliably. Some Windows
|
||||
// terminal stacks (including WSL/ConPTY-derived PTYs) can report support
|
||||
// but still deliver broken input when these flags are enabled.
|
||||
if supports_keyboard_enhancement().unwrap_or(false) && should_enable_keyboard_enhancement() {
|
||||
let _ = execute!(
|
||||
stdout(),
|
||||
PushKeyboardEnhancementFlags(
|
||||
@@ -121,7 +120,7 @@ pub fn init(config: &Config) -> Result<(Tui, TerminalInfo)> {
|
||||
)
|
||||
);
|
||||
} else {
|
||||
tracing::info!("Keyboard enhancement flags not supported; skipping enable.");
|
||||
tracing::info!("Keyboard enhancement flags unsupported or disabled; skipping enable.");
|
||||
}
|
||||
set_panic_hook();
|
||||
|
||||
@@ -229,7 +228,7 @@ pub fn restore() -> Result<()> {
|
||||
// Belt-and-suspenders: on terminals that do not maintain a clean stack,
|
||||
// explicitly set enhancement flags to empty, then pop again. This avoids
|
||||
// leaving kitty/xterm enhanced keyboard protocols active after exit.
|
||||
if supports_keyboard_enhancement().unwrap_or(false) {
|
||||
if supports_keyboard_enhancement().unwrap_or(false) && should_enable_keyboard_enhancement() {
|
||||
let _ = execute!(stdout(), PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::empty()));
|
||||
let _ = execute!(stdout(), PopKeyboardEnhancementFlags);
|
||||
}
|
||||
@@ -261,7 +260,7 @@ pub fn leave_alt_screen_only() -> Result<()> {
|
||||
// Pop keyboard enhancement flags so keys like Enter/Arrows don't emit
|
||||
// enhanced escape sequences (e.g., kitty/xterm modifyOtherKeys) into the buffer.
|
||||
let _ = execute!(stdout(), PopKeyboardEnhancementFlags);
|
||||
if supports_keyboard_enhancement().unwrap_or(false) {
|
||||
if supports_keyboard_enhancement().unwrap_or(false) && should_enable_keyboard_enhancement() {
|
||||
let _ = execute!(stdout(), PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::empty()));
|
||||
let _ = execute!(stdout(), PopKeyboardEnhancementFlags);
|
||||
}
|
||||
@@ -273,7 +272,7 @@ pub fn leave_alt_screen_only() -> Result<()> {
|
||||
/// Restores title and colors and performs a full clear to ensure a clean frame.
|
||||
pub fn enter_alt_screen_only(theme_fg: ratatui::style::Color, theme_bg: ratatui::style::Color) -> Result<()> {
|
||||
// Re-enable enhanced keyboard and focus/paste signaling for full TUI fidelity.
|
||||
if supports_keyboard_enhancement().unwrap_or(false) {
|
||||
if supports_keyboard_enhancement().unwrap_or(false) && should_enable_keyboard_enhancement() {
|
||||
let _ = execute!(
|
||||
stdout(),
|
||||
PushKeyboardEnhancementFlags(
|
||||
@@ -397,3 +396,72 @@ fn should_enable_focus_change() -> bool {
|
||||
// Default: enabled for modern terminals (xterm-256color, iTerm2, Alacritty, kitty, tmux, etc.)
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn should_enable_keyboard_enhancement() -> bool {
|
||||
// Hard overrides first
|
||||
if env::var("CODE_DISABLE_KBD_ENHANCEMENT")
|
||||
.map(|v| v == "1")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if env::var("CODE_ENABLE_KBD_ENHANCEMENT")
|
||||
.map(|v| v == "1")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Windows Terminal + ConPTY/WSL stacks are a frequent source of flaky input
|
||||
// when keyboard enhancement flags are enabled.
|
||||
if env::var("WT_SESSION").is_ok() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Git Bash / MSYS2 frequently struggles with these sequences.
|
||||
if env::var("MSYSTEM").is_ok() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::should_enable_keyboard_enhancement;
|
||||
|
||||
#[test]
|
||||
fn keyboard_enhancement_respects_env_overrides() {
|
||||
let prev_wt_session = std::env::var("WT_SESSION").ok();
|
||||
let prev_enable = std::env::var("CODE_ENABLE_KBD_ENHANCEMENT").ok();
|
||||
let prev_disable = std::env::var("CODE_DISABLE_KBD_ENHANCEMENT").ok();
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("WT_SESSION", "1");
|
||||
std::env::remove_var("CODE_ENABLE_KBD_ENHANCEMENT");
|
||||
std::env::remove_var("CODE_DISABLE_KBD_ENHANCEMENT");
|
||||
}
|
||||
assert!(!should_enable_keyboard_enhancement());
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("CODE_ENABLE_KBD_ENHANCEMENT", "1");
|
||||
}
|
||||
assert!(should_enable_keyboard_enhancement());
|
||||
|
||||
unsafe {
|
||||
std::env::set_var("CODE_DISABLE_KBD_ENHANCEMENT", "1");
|
||||
}
|
||||
assert!(!should_enable_keyboard_enhancement());
|
||||
|
||||
restore_env("WT_SESSION", prev_wt_session);
|
||||
restore_env("CODE_ENABLE_KBD_ENHANCEMENT", prev_enable);
|
||||
restore_env("CODE_DISABLE_KBD_ENHANCEMENT", prev_disable);
|
||||
}
|
||||
|
||||
fn restore_env(key: &str, value: Option<String>) {
|
||||
match value {
|
||||
Some(value) => unsafe { std::env::set_var(key, value) },
|
||||
None => unsafe { std::env::remove_var(key) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user