aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-25 16:32:00 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-25 16:32:00 +0300
commitc792f2fd53794a02b12c369903fd463c4896b7ef (patch)
treea80080d9f42c00aab8d4814e49f5d8c196875699
parent58b9fecc21a71d253b71cf104b1df0da8e81aeba (diff)
fix: preserve alacritty input on windows
refs #792
-rw-r--r--docs/next/CHANGELOG.md3
-rw-r--r--docs/next/website/src/content/docs/windows-beta.mdx4
-rw-r--r--src/client/input.rs43
-rw-r--r--src/client/input/windows_vti.rs316
-rw-r--r--src/client/mod.rs8
-rw-r--r--src/input/model.rs43
-rw-r--r--src/main.rs6
7 files changed, 396 insertions, 27 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index e84b7823..b2b26f37 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -2,6 +2,9 @@
## Unreleased
+### Fixed
+- Native Windows clients running inside Alacritty now preserve mouse reports and `ctrl+j` input instead of leaking mouse escape sequences into panes. `shift+enter` remains dependent on whether the outer terminal reports it as a distinct modified Enter key. (#792)
+
## [0.7.1] - 2026-06-24
### Added
diff --git a/docs/next/website/src/content/docs/windows-beta.mdx b/docs/next/website/src/content/docs/windows-beta.mdx
index 539abf8d..52d25b35 100644
--- a/docs/next/website/src/content/docs/windows-beta.mdx
+++ b/docs/next/website/src/content/docs/windows-beta.mdx
@@ -63,6 +63,10 @@ Herdr's pane text copy works on Windows beta. Drag-select text inside a pane to
For text paste, use `ctrl+shift+v` in Windows Terminal. Multiline text paste is bracketed so shells and agent prompts receive it as one paste instead of submitting each line separately. Hold `shift` and right-click to use the outer terminal paste action instead of sending the click through Herdr.
+## Keyboard and mouse limitations
+
+Windows terminals do not all report modified keys in the same shape. Herdr preserves mouse reporting and `ctrl+j` in Windows Terminal and Alacritty on Windows, but `shift+enter` only works when the outer terminal reports it as a distinct modified Enter key. If Windows or the terminal reports it as plain Enter, Herdr forwards plain Enter.
+
## Not supported on Windows beta
| Capability | Status |
diff --git a/src/client/input.rs b/src/client/input.rs
index 7cf69533..7099311b 100644
--- a/src/client/input.rs
+++ b/src/client/input.rs
@@ -212,7 +212,7 @@ fn windows_event_is_control_key(event: &crossterm::event::Event) -> bool {
)
}
-#[cfg(windows)]
+#[cfg(any(windows, test))]
fn windows_key_raw_bytes(
event: &crossterm::event::Event,
raw_sequence_pending: bool,
@@ -228,6 +228,9 @@ fn windows_key_raw_bytes(
match key.code {
KeyCode::Esc if key.modifiers.is_empty() => Some(vec![0x1b]),
+ KeyCode::Char('[') if !raw_sequence_pending && key.modifiers == KeyModifiers::CONTROL => {
+ Some(vec![0x1b])
+ }
KeyCode::Char(ch)
if !raw_sequence_pending
&& matches!(ch, 'i' | 'I')
@@ -365,7 +368,7 @@ mod tests {
}
}
-#[cfg(all(test, windows))]
+#[cfg(test)]
mod windows_tests {
use super::*;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
@@ -396,6 +399,42 @@ mod windows_tests {
}
#[test]
+ fn windows_ctrl_bracket_starts_raw_escape_sequence() {
+ let ctrl_bracket = Event::Key(KeyEvent::new(KeyCode::Char('['), KeyModifiers::CONTROL));
+ assert_eq!(
+ windows_key_raw_bytes(&ctrl_bracket, false).as_deref(),
+ Some(b"\x1b".as_slice())
+ );
+
+ let mut framer = crate::raw_input::RawInputFramer::default();
+ assert!(framer.push(b"\x1b").is_empty());
+ let events = framer.push(b"[<35;48;26M");
+ assert_eq!(events.len(), 1);
+
+ let event = windows_client_input_event_from_raw(events.into_iter().next().unwrap())
+ .expect("raw mouse converts");
+ assert!(matches!(
+ event,
+ crate::protocol::ClientInputEvent::Mouse {
+ kind: crate::protocol::ClientMouseKind::Moved,
+ column: 47,
+ row: 25,
+ modifiers: _,
+ }
+ ));
+ }
+
+ #[test]
+ fn windows_ctrl_shift_bracket_stays_semantic() {
+ let ctrl_shift_bracket = Event::Key(KeyEvent::new(
+ KeyCode::Char('['),
+ KeyModifiers::CONTROL | KeyModifiers::SHIFT,
+ ));
+ assert_eq!(windows_key_raw_bytes(&ctrl_shift_bracket, false), None);
+ }
+
+ #[cfg(windows)]
+ #[test]
fn windows_ctrl_d_semantic_event_encodes_to_eot() {
let event = Event::Key(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL));
assert_eq!(windows_key_raw_bytes(&event, false), None);
diff --git a/src/client/input/windows_vti.rs b/src/client/input/windows_vti.rs
index 376efdb5..9f6d3788 100644
--- a/src/client/input/windows_vti.rs
+++ b/src/client/input/windows_vti.rs
@@ -134,6 +134,7 @@ fn windows_console_input_record_from_os(
key_down: key.bKeyDown != 0,
repeat_count: key.wRepeatCount,
virtual_key_code: key.wVirtualKeyCode,
+ virtual_scan_code: key.wVirtualScanCode,
unicode,
control_key_state: key.dwControlKeyState,
}))
@@ -168,6 +169,7 @@ struct WindowsKeyRecord {
key_down: bool,
repeat_count: u16,
virtual_key_code: u16,
+ virtual_scan_code: u16,
unicode: u16,
control_key_state: u32,
}
@@ -198,6 +200,10 @@ struct WindowsInputPump {
enum PlatformInputItem {
Bytes(Vec<u8>),
Semantic(crate::protocol::ClientInputEvent),
+ PasteAwareBytes {
+ paste_bytes: Vec<u8>,
+ raw_bytes: Vec<u8>,
+ },
PasteAwareKey {
bytes: Vec<u8>,
events: Vec<crate::protocol::ClientInputEvent>,
@@ -228,6 +234,16 @@ impl WindowsInputPump {
events.push(event);
events
}
+ PlatformInputItem::PasteAwareBytes {
+ paste_bytes,
+ raw_bytes,
+ } => {
+ if self.framer.has_pending_bracketed_paste() {
+ Self::raw_events_to_client_events(self.framer.push(&paste_bytes))
+ } else {
+ Self::raw_events_to_client_events(self.framer.push(&raw_bytes))
+ }
+ }
PlatformInputItem::PasteAwareKey { bytes, events } => {
if self.framer.has_pending_bracketed_paste() {
Self::raw_events_to_client_events(self.framer.push(&bytes))
@@ -334,6 +350,10 @@ impl WindowsInputMapper {
return Vec::new();
}
+ if self.key_record_is_raw_escape(key) {
+ return self.translate_win32_input_mode_bytes(&[0x1b]);
+ }
+
let repeat_count = key.repeat_count.max(1);
if key.virtual_key_code == 0 {
let mut items = Vec::new();
@@ -369,6 +389,24 @@ impl WindowsInputMapper {
self.with_pending_win32_flush(items)
}
+ fn key_record_is_raw_escape(&self, key: WindowsKeyRecord) -> bool {
+ let modifiers = windows_key_modifiers(key.control_key_state);
+ if !key.key_down
+ || key.repeat_count.max(1) != 1
+ || modifiers.contains(crossterm::event::KeyModifiers::ALT)
+ {
+ return false;
+ }
+
+ let bare_escape = modifiers.is_empty()
+ && (key.virtual_key_code == 0x1b || (key.virtual_key_code == 0 && key.unicode == 0x1b));
+ let ctrl_bracket = key.virtual_key_code == 0xdb
+ && key.unicode == 0x1b
+ && modifiers == crossterm::event::KeyModifiers::CONTROL;
+
+ bare_escape || ctrl_bracket
+ }
+
fn translate_win32_input_mode_bytes(&mut self, bytes: &[u8]) -> Vec<PlatformInputItem> {
let mut items = Vec::new();
for item in self.win32_input.push(bytes) {
@@ -377,16 +415,42 @@ impl WindowsInputMapper {
items.push(PlatformInputItem::Bytes(bytes))
}
WindowsWin32InputModeItem::Key { bytes, record } => {
- items.push(PlatformInputItem::PasteAwareKey {
- bytes,
- events: self.translate_semantic_key_events(record),
- })
+ if let Some(raw_bytes) = self.win32_input_mode_key_record_raw_bytes(record) {
+ items.push(PlatformInputItem::PasteAwareBytes {
+ paste_bytes: bytes,
+ raw_bytes,
+ });
+ } else {
+ items.push(PlatformInputItem::PasteAwareKey {
+ bytes,
+ events: self.translate_semantic_key_events(record),
+ })
+ }
}
}
}
items
}
+ fn win32_input_mode_key_record_raw_bytes(
+ &mut self,
+ record: WindowsKeyRecord,
+ ) -> Option<Vec<u8>> {
+ if self.key_record_is_raw_escape(record) {
+ return Some(vec![0x1b]);
+ }
+
+ if !record.key_down
+ || record.virtual_key_code != 0
+ || windows_key_modifiers(record.control_key_state).bits() != 0
+ {
+ return None;
+ }
+
+ self.synthetic_utf16_unit_to_bytes(record.unicode)
+ .map(|bytes| bytes.repeat(record.repeat_count.max(1) as usize))
+ }
+
fn with_pending_win32_flush(
&mut self,
mut items: Vec<PlatformInputItem>,
@@ -483,6 +547,18 @@ impl WindowsInputMapper {
kind: crate::protocol::ClientKeyKind,
) -> Option<crate::protocol::ClientInputEvent> {
let modifiers = windows_key_modifiers(key.control_key_state);
+ if modifiers.contains(crossterm::event::KeyModifiers::CONTROL)
+ && key.unicode == 0x000a
+ && (key.virtual_key_code == 0x4a || key.virtual_scan_code == 0x24)
+ {
+ self.pending_high_surrogate = None;
+ return Some(crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Char('j'),
+ modifiers: modifiers.bits(),
+ kind,
+ });
+ }
+
let code = if let Some(code) =
windows_virtual_key_to_key_code(key.virtual_key_code, modifiers)
{
@@ -703,7 +779,7 @@ impl WindowsWin32InputModeFramer {
fn parse_win32_input_mode_key_record(body: &str) -> Option<WindowsKeyRecord> {
let mut fields = body.split(';');
let virtual_key_code = fields.next()?.parse::<u16>().ok()?;
- let _scan_code = fields.next()?.parse::<u16>().ok()?;
+ let virtual_scan_code = fields.next()?.parse::<u16>().ok()?;
let unicode = fields.next()?.parse::<u16>().ok()?;
let key_down = fields.next()?.parse::<u16>().ok()? != 0;
let control_key_state = fields.next()?.parse::<u32>().ok()?;
@@ -716,6 +792,7 @@ fn parse_win32_input_mode_key_record(body: &str) -> Option<WindowsKeyRecord> {
key_down,
repeat_count,
virtual_key_code,
+ virtual_scan_code,
unicode,
control_key_state,
})
@@ -841,6 +918,7 @@ mod tests {
key_down: true,
repeat_count: 1,
virtual_key_code: 0,
+ virtual_scan_code: 0,
unicode: ch as u16,
control_key_state: 0,
})
@@ -855,6 +933,7 @@ mod tests {
key_down: true,
repeat_count: 1,
virtual_key_code: vk,
+ virtual_scan_code: 0,
unicode: ch as u16,
control_key_state,
})
@@ -865,6 +944,7 @@ mod tests {
key_down: true,
repeat_count: 1,
virtual_key_code: vk,
+ virtual_scan_code: 0,
unicode: unit,
control_key_state: 0,
})
@@ -875,16 +955,34 @@ mod tests {
key_down: true,
repeat_count: 1,
virtual_key_code: vk,
+ virtual_scan_code: 0,
unicode: unit,
control_key_state,
})
}
+ fn key_vk_with_scan_unicode(
+ vk: u16,
+ scan: u16,
+ ch: char,
+ control_key_state: u32,
+ ) -> WindowsInputRecord {
+ WindowsInputRecord::Key(WindowsKeyRecord {
+ key_down: true,
+ repeat_count: 1,
+ virtual_key_code: vk,
+ virtual_scan_code: scan,
+ unicode: ch as u16,
+ control_key_state,
+ })
+ }
+
fn key_vk_with_repeat(vk: u16, repeat_count: u16) -> WindowsInputRecord {
WindowsInputRecord::Key(WindowsKeyRecord {
key_down: true,
repeat_count,
virtual_key_code: vk,
+ virtual_scan_code: 0,
unicode: 0,
control_key_state: 0,
})
@@ -900,6 +998,7 @@ mod tests {
key_down: true,
repeat_count,
virtual_key_code: vk,
+ virtual_scan_code: 0,
unicode: ch as u16,
control_key_state,
})
@@ -910,6 +1009,23 @@ mod tests {
key_down: false,
repeat_count: 1,
virtual_key_code: vk,
+ virtual_scan_code: 0,
+ unicode: ch as u16,
+ control_key_state,
+ })
+ }
+
+ fn key_vk_up_with_scan_unicode(
+ vk: u16,
+ scan: u16,
+ ch: char,
+ control_key_state: u32,
+ ) -> WindowsInputRecord {
+ WindowsInputRecord::Key(WindowsKeyRecord {
+ key_down: false,
+ repeat_count: 1,
+ virtual_key_code: vk,
+ virtual_scan_code: scan,
unicode: ch as u16,
control_key_state,
})
@@ -925,6 +1041,18 @@ mod tests {
.collect()
}
+ fn win32_input_mode_encoded_raw_bytes(bytes: &[u8]) -> Vec<WindowsInputRecord> {
+ bytes
+ .iter()
+ .flat_map(|byte| {
+ format!("\x1b[0;0;{byte};1;0;1_")
+ .chars()
+ .map(key_char)
+ .collect::<Vec<_>>()
+ })
+ .collect()
+ }
+
#[test]
fn vti_bracketed_paste_records_emit_single_paste() {
let records = "\x1b[200~alpha\rbravo\rcharlie\x1b[201~"
@@ -986,7 +1114,6 @@ mod tests {
fn vti_real_non_letter_control_records_are_preserved() {
let cases = [
(0x20, 0x00, ' '),
- (0xdb, 0x1b, '['),
(0xdc, 0x1c, '\\'),
(0xdd, 0x1d, ']'),
(0x36, 0x1e, '^'),
@@ -1007,6 +1134,78 @@ mod tests {
}
#[test]
+ fn vti_ctrl_bracket_record_flushes_to_escape_after_idle() {
+ let mut translator = WindowsInputTranslator::default();
+ assert!(translator
+ .translate(key_vk_with_utf16_mods(0xdb, 0x1b, 0x0008))
+ .is_empty());
+ assert_eq!(
+ translator.idle(),
+ vec![crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: 0,
+ kind: crate::protocol::ClientKeyKind::Press,
+ }]
+ );
+ }
+
+ #[test]
+ fn vti_escape_key_record_flushes_to_escape_after_idle() {
+ let mut translator = WindowsInputTranslator::default();
+ assert!(translator.translate(key_vk(0x1b, 0)).is_empty());
+ assert_eq!(
+ translator.idle(),
+ vec![crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: 0,
+ kind: crate::protocol::ClientKeyKind::Press,
+ }]
+ );
+ }
+
+ #[test]
+ fn vti_repeated_escape_record_stays_semantic() {
+ assert_eq!(
+ translate([key_vk_with_repeat(0x1b, 3)]),
+ vec![
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: 0,
+ kind: crate::protocol::ClientKeyKind::Press,
+ },
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: 0,
+ kind: crate::protocol::ClientKeyKind::Repeat,
+ },
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: 0,
+ kind: crate::protocol::ClientKeyKind::Repeat,
+ },
+ ]
+ );
+ }
+
+ #[test]
+ fn vti_modified_escape_remains_semantic() {
+ assert_eq!(
+ translate([key_vk_with_utf16_mods(0x1b, 0, 0x0010)]),
+ vec![crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Esc,
+ modifiers: crossterm::event::KeyModifiers::SHIFT.bits(),
+ kind: crate::protocol::ClientKeyKind::Press,
+ }]
+ );
+ }
+
+ #[test]
+ fn vti_win32_input_mode_key_release_does_not_emit_raw_text() {
+ let records = "\x1b[0;0;97;0;0;1_".chars().map(key_char);
+ assert!(translate(records).is_empty());
+ }
+
+ #[test]
fn vti_enter_outside_paste_becomes_enter_key() {
assert_eq!(
translate([key_char('\r')]),
@@ -1215,6 +1414,50 @@ mod tests {
}
#[test]
+ fn vti_alacritty_ctrl_j_return_lf_record_preserves_lf_control_key() {
+ assert_eq!(
+ translate([
+ key_vk_with_scan_unicode(0x0d, 0x24, '\n', 0x0008),
+ key_vk_up_with_scan_unicode(0x0d, 0x24, '\n', 0x0008),
+ ]),
+ vec![
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Char('j'),
+ modifiers: crossterm::event::KeyModifiers::CONTROL.bits(),
+ kind: crate::protocol::ClientKeyKind::Press,
+ },
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Char('j'),
+ modifiers: crossterm::event::KeyModifiers::CONTROL.bits(),
+ kind: crate::protocol::ClientKeyKind::Release,
+ },
+ ]
+ );
+ }
+
+ #[test]
+ fn vti_ctrl_enter_return_lf_record_preserves_ctrl_enter() {
+ assert_eq!(
+ translate([
+ key_vk_with_scan_unicode(0x0d, 0x1c, '\n', 0x0008),
+ key_vk_up_with_scan_unicode(0x0d, 0x1c, '\n', 0x0008),
+ ]),
+ vec![
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Enter,
+ modifiers: crossterm::event::KeyModifiers::CONTROL.bits(),
+ kind: crate::protocol::ClientKeyKind::Press,
+ },
+ crate::protocol::ClientInputEvent::Key {
+ code: crate::protocol::ClientKeyCode::Enter,
+ modifiers: crossterm::event::KeyModifiers::CONTROL.bits(),
+ kind: crate::protocol::ClientKeyKind::Release,
+ },
+ ]
+ );
+ }
+
+ #[test]
fn vti_win32_input_mode_printable_key_becomes_char() {
let records = "\x1b[65;30;97;1;0;1_\x1b[65;30;97;0;0;1_"
.chars()
@@ -1252,6 +1495,20 @@ mod tests {
}
#[test]
+ fn vti_win32_input_mode_raw_sequence_inside_bracketed_paste_stays_payload() {
+ let records = "\x1b[200~alpha\x1b[0;0;97;1;0;1_bravo\x1b[201~"
+ .chars()
+ .map(key_char);
+
+ assert_eq!(
+ translate(records),
+ vec![crate::protocol::ClientInputEvent::Paste {
+ text: "alpha\x1b[0;0;97;1;0;1_bravo".into(),
+ }]
+ );
+ }
+
+ #[test]
fn vti_win32_input_mode_leaves_mouse_sequence_for_raw_parser() {
let records = "\x1b[<0;3;4M".chars().map(key_char);
@@ -1269,6 +1526,53 @@ mod tests {
}
#[test]
+ fn vti_ctrl_bracket_escape_starts_mouse_sequence() {
+ let records = [key_vk_with_utf16_mods(0xdb, 0x1b, 0x0008)]
+ .into_iter()
+ .chain("[<35;48;26M".chars().map(key_char));
+
+ assert_eq!(
+ translate(records),
+ vec![crate::protocol::ClientInputEvent::Mouse {
+ kind: crate::protocol::ClientMouseKind::Moved,
+ column: 47,
+ row: 25,
+ modifiers: 0,
+ }]
+ );
+ }
+
+ #[test]
+ fn vti_escape_key_record_without_unicode_starts_mouse_sequence() {
+ let records = [key_vk(0x1b, 0)]
+ .into_iter()
+ .chain("[<35;48;26M".chars().map(key_char));
+
+ assert_eq!(
+ translate(records),
+ vec![crate::protocol::ClientInputEvent::Mouse {
+ kind: crate::protocol::ClientMouseKind::Moved,
+ column: 47,
+ row: 25,
+ modifiers: 0,
+ }]
+ );
+ }
+
+ #[test]
+ fn vti_win32_input_mode_encoded_mouse_sequence() {
+ assert_eq!(
+ translate(win32_input_mode_encoded_raw_bytes(b"\x1b[<35;48;26M")),
+ vec![crate::protocol::ClientInputEvent::Mouse {
+ kind: crate::protocol::ClientMouseKind::Moved,
+ column: 47,
+ row: 25,
+ modifiers: 0,
+ }]
+ );
+ }
+
+ #[test]
fn vti_escape_arrow_sequence_becomes_arrow_key() {
let records = "\x1b[A".chars().map(key_char);
assert_eq!(
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 67726e68..d9d73b95 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -377,13 +377,7 @@ fn setup_terminal_with_capabilities(
}
let modify_other_keys_mode = enable_client_protocols
- .then(|| {
- crate::input::host_modify_other_keys_mode(
- std::env::var("TMUX").is_ok(),
- std::env::var("TERM_PROGRAM").ok().as_deref(),
- std::env::var_os("WEZTERM_PANE").is_some(),
- )
- })
+ .then(crate::input::host_modify_other_keys_mode)
.flatten();
if let Some(mode) = modify_other_keys_mode {
io::stdout().write_all(mode.set_sequence())?;
diff --git a/src/input/model.rs b/src/input/model.rs
index 1ce107d4..a3e74056 100644
--- a/src/input/model.rs
+++ b/src/input/model.rs
@@ -65,16 +65,34 @@ impl ModifyOtherKeysMode {
}
}
-pub fn host_modify_other_keys_mode(
+pub fn host_modify_other_keys_mode() -> Option<ModifyOtherKeysMode> {
+ #[cfg(windows)]
+ let alacritty_window_id = std::env::var_os("ALACRITTY_WINDOW_ID").is_some();
+ #[cfg(not(windows))]
+ let alacritty_window_id = false;
+
+ host_modify_other_keys_mode_for_env(
+ std::env::var("TMUX").is_ok(),
+ std::env::var("TERM_PROGRAM").ok().as_deref(),
+ std::env::var_os("WEZTERM_PANE").is_some(),
+ alacritty_window_id,
+ )
+}
+
+fn host_modify_other_keys_mode_for_env(
in_tmux: bool,
term_program: Option<&str>,
wezterm_pane: bool,
+ alacritty_window_id: bool,
) -> Option<ModifyOtherKeysMode> {
if in_tmux {
return Some(ModifyOtherKeysMode::Mode2);
}
- if wezterm_pane || term_program.is_some_and(|program| program.eq_ignore_ascii_case("wezterm")) {
+ if wezterm_pane
+ || alacritty_window_id
+ || term_program.is_some_and(|program| program.eq_ignore_ascii_case("wezterm"))
+ {
return Some(ModifyOtherKeysMode::Mode1);
}
@@ -159,7 +177,7 @@ mod tests {
#[test]
fn modify_other_keys_mode_is_enabled_for_tmux() {
assert_eq!(
- host_modify_other_keys_mode(true, Some("WezTerm"), true),
+ host_modify_other_keys_mode_for_env(true, Some("WezTerm"), true, true),
Some(ModifyOtherKeysMode::Mode2)
);
}
@@ -167,11 +185,19 @@ mod tests {
#[test]
fn modify_other_keys_mode_is_enabled_for_wezterm_hosts() {
assert_eq!(
- host_modify_other_keys_mode(false, Some("WezTerm"), false),
+ host_modify_other_keys_mode_for_env(false, Some("WezTerm"), false, false),
+ Some(ModifyOtherKeysMode::Mode1)
+ );
+ assert_eq!(
+ host_modify_other_keys_mode_for_env(false, None, true, false),
Some(ModifyOtherKeysMode::Mode1)
);
+ }
+
+ #[test]
+ fn modify_other_keys_mode_is_enabled_for_alacritty_hosts() {
assert_eq!(
- host_modify_other_keys_mode(false, None, true),
+ host_modify_other_keys_mode_for_env(false, None, false, true),
Some(ModifyOtherKeysMode::Mode1)
);
}
@@ -179,9 +205,12 @@ mod tests {
#[test]
fn modify_other_keys_mode_is_not_enabled_for_unknown_hosts() {
assert_eq!(
- host_modify_other_keys_mode(false, Some("ghostty"), false),
+ host_modify_other_keys_mode_for_env(false, Some("ghostty"), false, false),
+ None
+ );
+ assert_eq!(
+ host_modify_other_keys_mode_for_env(false, None, false, false),
None
);
- assert_eq!(host_modify_other_keys_mode(false, None, false), None);
}
}
diff --git a/src/main.rs b/src/main.rs
index ed4d4545..597b332e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -673,11 +673,7 @@ fn main() -> io::Result<()> {
Err(err) => return Err(err),
};
- let modify_other_keys_mode = crate::input::host_modify_other_keys_mode(
- std::env::var("TMUX").is_ok(),
- std::env::var("TERM_PROGRAM").ok().as_deref(),
- std::env::var_os("WEZTERM_PANE").is_some(),
- );
+ let modify_other_keys_mode = crate::input::host_modify_other_keys_mode();
let original_hook = std::panic::take_hook();
let panic_resets_modify_other_keys = modify_other_keys_mode.is_some();