Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Adapt keyboard event reporting to the terminal (#35021)
## Why

Reporting key event types can leak an exit-shortcut release into the parent
shell in iTerm2 and can cause tmux's `xterm` extended-key format to lose
Shift+Enter.

## What changed

- Select keyboard enhancement flags using the detected terminal and tmux
  extended-key format.
- Disable event-type reporting for iTerm2 and tmux's `xterm` format while
  retaining alternate-key reporting.
- Preserve event-type reporting for other terminals and tmux's `csi-u`
  format so repeat events remain distinguishable.

## Testing

Added unit coverage for iTerm2, Kitty, unknown terminals, and both tmux
extended-key formats.

GitOrigin-RevId: 03a6b9d5443f626da5279413a14933af2a0ec3e5
  • Loading branch information
fcoury-oai authored and copyberry committed Jul 23, 2026
commit fe0d472c4c3a5e1c5163808446e904cb4c20fc54
105 changes: 92 additions & 13 deletions codex-rs/tui/src/tui/keyboard_modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use std::fmt;
use std::io::stdout;

use codex_terminal_detection::TerminalName;
use codex_terminal_detection::terminal_info;
use crossterm::Command;
use crossterm::event::KeyboardEnhancementFlags;
use crossterm::event::PopKeyboardEnhancementFlags;
Expand Down Expand Up @@ -123,21 +125,47 @@ pub(super) fn enable_keyboard_enhancement() {
return;
}

let running_in_tmux_session = running_in_tmux_session();
let tmux_extended_keys_format = if running_in_tmux_session {
read_tmux_extended_keys_format()
} else {
None
};

let _ = execute!(
stdout(),
DisableModifyOtherKeys,
PushKeyboardEnhancementFlags(
KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_EVENT_TYPES
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
)
PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
terminal_info().name,
tmux_extended_keys_format.as_deref()
))
);

if tmux_should_enable_modify_other_keys() {
if tmux_should_enable_modify_other_keys_for(
running_in_tmux_session,
tmux_extended_keys_format.as_deref(),
) {
let _ = execute!(stdout(), EnableModifyOtherKeys);
}
}

fn keyboard_enhancement_flags(
terminal_name: TerminalName,
tmux_extended_keys_format: Option<&str>,
) -> KeyboardEnhancementFlags {
let flags = KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
| KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS;

// iTerm can leak the release of an exit shortcut into the parent shell.
// tmux's xterm key format also loses Shift-Enter when event types are
// reported. Preserve repeat classification on transports that support it.
if terminal_name == TerminalName::Iterm2 || matches!(tmux_extended_keys_format, Some("xterm")) {
flags
} else {
flags | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
}
}

fn running_in_tmux_session() -> bool {
tmux_session_detected(
std::env::var("TMUX").ok().as_deref(),
Expand All @@ -149,13 +177,6 @@ fn tmux_session_detected(tmux: Option<&str>, tmux_pane: Option<&str>) -> bool {
tmux.is_some() || tmux_pane.is_some()
}

fn tmux_should_enable_modify_other_keys() -> bool {
tmux_should_enable_modify_other_keys_for(
running_in_tmux_session(),
read_tmux_extended_keys_format().as_deref(),
)
}

fn tmux_should_enable_modify_other_keys_for(
running_in_tmux_session: bool,
extended_keys_format: Option<&str>,
Expand Down Expand Up @@ -283,11 +304,14 @@ mod tests {
use super::EnableModifyOtherKeys;
use super::ResetKeyboardEnhancementFlags;
use super::keyboard_enhancement_disabled_for;
use super::keyboard_enhancement_flags;
use super::parse_bool_env;
use super::tmux_session_detected;
use super::tmux_should_enable_modify_other_keys_for;
use super::vscode_terminal_detected;
use codex_terminal_detection::TerminalName;
use crossterm::Command;
use crossterm::event::PushKeyboardEnhancementFlags;
use pretty_assertions::assert_eq;

fn ansi_for(command: impl Command) -> String {
Expand All @@ -296,6 +320,61 @@ mod tests {
out
}

#[test]
fn keyboard_enhancement_suppresses_release_reporting_for_iterm() {
assert_eq!(
ansi_for(PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
TerminalName::Iterm2,
/*tmux_extended_keys_format*/ None
))),
"\x1b[>5u"
);
}

#[test]
fn keyboard_enhancement_preserves_repeat_reporting_for_kitty() {
assert_eq!(
ansi_for(PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
TerminalName::Kitty,
/*tmux_extended_keys_format*/ None
))),
"\x1b[>7u"
);
}

#[test]
fn keyboard_enhancement_preserves_repeat_reporting_for_csi_u_tmux() {
assert_eq!(
ansi_for(PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
TerminalName::Kitty,
Some("csi-u")
))),
"\x1b[>7u"
);
}

#[test]
fn keyboard_enhancement_preserves_shift_enter_for_xterm_tmux() {
assert_eq!(
ansi_for(PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
TerminalName::Kitty,
Some("xterm")
))),
"\x1b[>5u"
);
}

#[test]
fn keyboard_enhancement_preserves_repeat_reporting_for_unknown_terminals() {
assert_eq!(
ansi_for(PushKeyboardEnhancementFlags(keyboard_enhancement_flags(
TerminalName::Unknown,
/*tmux_extended_keys_format*/ None
))),
"\x1b[>7u"
);
}

#[test]
fn keyboard_enhancement_env_flag_parses_common_values() {
assert_eq!(parse_bool_env(Some("1")), Some(true));
Expand Down
Loading