diff options
Diffstat (limited to 'src/pane')
| -rw-r--r-- | src/pane/input.rs | 8 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 112 |
2 files changed, 95 insertions, 25 deletions
diff --git a/src/pane/input.rs b/src/pane/input.rs index 6906e77d..38e8138b 100644 --- a/src/pane/input.rs +++ b/src/pane/input.rs @@ -1,5 +1,5 @@ pub(super) fn ghostty_key_event_from_terminal_key( - key: crate::input::TerminalKey, + key: &crate::input::TerminalKey, ) -> Option<crate::ghostty::KeyEvent> { let mut event = crate::ghostty::KeyEvent::new().ok()?; event.set_action(match key.kind { @@ -32,7 +32,7 @@ pub(super) fn ghostty_key_event_from_terminal_key( Some(event) } -pub(super) fn ghostty_prefers_herdr_text_encoding(key: crate::input::TerminalKey) -> bool { +pub(super) fn ghostty_prefers_herdr_text_encoding(key: &crate::input::TerminalKey) -> bool { matches!(key.code, crossterm::event::KeyCode::Char(_)) } @@ -168,7 +168,7 @@ pub(super) fn ghostty_mouse_event_from_wheel_kind( Some(event) } -fn ghostty_key_text(key: crate::input::TerminalKey) -> Option<String> { +fn ghostty_key_text(key: &crate::input::TerminalKey) -> Option<String> { match key.code { crossterm::event::KeyCode::Char(c) => Some( key.shifted_codepoint @@ -180,7 +180,7 @@ fn ghostty_key_text(key: crate::input::TerminalKey) -> Option<String> { } } -fn ghostty_unshifted_codepoint(key: crate::input::TerminalKey) -> Option<u32> { +fn ghostty_unshifted_codepoint(key: &crate::input::TerminalKey) -> Option<u32> { match key.code { crossterm::event::KeyCode::Char(c) => Some(c as u32), _ => None, diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index 5a2180bf..3f8d8aea 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -1635,22 +1635,40 @@ impl GhosttyPaneTerminal { protocol: crate::input::KeyboardProtocol, ) -> Vec<u8> { #[cfg(windows)] - if let Some(bytes) = crate::platform::encode_windows_conpty_shift_enter(key) { - if self.core.lock().is_ok_and(|core| { - core.terminal - .kitty_keyboard_flags() - .is_ok_and(|flags| flags == 0) - && !core.kitty_keyboard.modify_other_keys_enabled() - }) { + if self.core.lock().is_ok_and(|core| { + core.terminal + .kitty_keyboard_flags() + .is_ok_and(|flags| flags == 0) + && !core.kitty_keyboard.modify_other_keys_enabled() + }) { + if let Some(bytes) = crate::platform::encode_windows_conpty_fallback(&key) { return bytes; } } - if ghostty_prefers_herdr_text_encoding(key) { + let repeat_count = key.repeat_count; + let first = key.with_repeat_count(1); + let mut bytes = self.encode_terminal_key_once(first.clone(), protocol); + if repeat_count > 1 && first.kind != crossterm::event::KeyEventKind::Release { + let repeated = first.with_kind(crossterm::event::KeyEventKind::Repeat); + let repeated_bytes = self.encode_terminal_key_once(repeated, protocol); + for _ in 1..repeat_count { + bytes.extend_from_slice(&repeated_bytes); + } + } + bytes + } + + fn encode_terminal_key_once( + &self, + key: crate::input::TerminalKey, + protocol: crate::input::KeyboardProtocol, + ) -> Vec<u8> { + if ghostty_prefers_herdr_text_encoding(&key) { return crate::input::encode_terminal_key(key, protocol); } - let Some(event) = ghostty_key_event_from_terminal_key(key) else { + let Some(event) = ghostty_key_event_from_terminal_key(&key) else { return crate::input::encode_terminal_key(key, protocol); }; @@ -1659,7 +1677,8 @@ impl GhosttyPaneTerminal { }; match encoder.encode(&event) { Ok(bytes) - if !bytes.is_empty() && encoded_key_preserves_event_kind(&bytes, key, protocol) => + if !bytes.is_empty() + && encoded_key_preserves_event_kind(&bytes, &key, protocol) => { bytes } @@ -1969,7 +1988,7 @@ impl GhosttyPaneTerminal { fn encoded_key_preserves_event_kind( bytes: &[u8], - key: crate::input::TerminalKey, + key: &crate::input::TerminalKey, protocol: crate::input::KeyboardProtocol, ) -> bool { if !protocol.reports_event_types() || key.kind == crossterm::event::KeyEventKind::Press { @@ -3890,11 +3909,51 @@ mod tests { assert_eq!(encoded, b"\x1bOA"); let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap(); - let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let encoded = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_eq!(encoded, b"\x1b[27;2;13~"); } #[test] + fn grouped_semantic_key_repeats_expand_at_the_destination() { + let (tx, _rx) = mpsc::channel(4); + let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + let key = crate::input::TerminalKey::new( + crossterm::event::KeyCode::Char('x'), + crossterm::event::KeyModifiers::empty(), + ) + .with_repeat_count(3); + + assert_eq!( + pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy), + b"xxx" + ); + } + + #[test] + fn grouped_release_is_encoded_once() { + let (tx, _rx) = mpsc::channel(4); + let mut terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); + terminal.write(b"\x1b[>11u"); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + let protocol = pane.keyboard_protocol().unwrap(); + let release = crate::input::TerminalKey::new( + crossterm::event::KeyCode::Up, + crossterm::event::KeyModifiers::empty(), + ) + .with_kind(crossterm::event::KeyEventKind::Release); + let expected = pane.encode_terminal_key(release.clone(), protocol); + + assert!(!expected.is_empty()); + let mut malformed_release = release; + malformed_release.repeat_count = 3; + assert_eq!( + pane.encode_terminal_key(malformed_release, protocol), + expected + ); + } + + #[test] fn ghostty_key_encoder_updates_after_terminal_mode_changes() { let (tx, _rx) = mpsc::channel(4); let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); @@ -3933,9 +3992,9 @@ mod tests { crossterm::event::KeyModifiers::CONTROL | crossterm::event::KeyModifiers::SHIFT, ); - let before = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let before = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); pane.process_pty_bytes(pane_id, 0, b"\x1b[>1u", &tx); - let after = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let after = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_ne!(before, after); assert_eq!(after, b"\x1b[13;6u"); @@ -3950,7 +4009,7 @@ mod tests { pane.process_pty_bytes(pane_id, 0, b"\x1b[>5u", &tx); let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap(); - let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let encoded = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_eq!( pane.keyboard_protocol(), @@ -3968,7 +4027,7 @@ mod tests { pane.seed_keyboard_protocol_flags(5); let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap(); - let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let encoded = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_eq!( pane.keyboard_protocol(), @@ -4005,21 +4064,29 @@ mod tests { ); } + #[cfg(windows)] #[test] - fn ghostty_modify_other_keys_mode_one_preserves_shift_enter() { + fn windows_ghostty_default_pane_preserves_synthesized_shift_enter_fallback() { let (tx, _rx) = mpsc::channel(4); let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap(); - #[cfg(windows)] assert_eq!( pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy), b"\x1b[13;28;13;1;16;1_" ); + } + + #[test] + fn ghostty_modify_other_keys_mode_one_preserves_shift_enter() { + let (tx, _rx) = mpsc::channel(4); + let terminal = crate::ghostty::Terminal::new(80, 24, 0).unwrap(); + let pane = GhosttyPaneTerminal::new(terminal, tx).unwrap(); + let key = crate::input::parse_terminal_key_sequence("\x1b[13;2u").unwrap(); pane.seed_history_ansi("\x1b[>4;1m"); - let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let encoded = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_eq!(encoded, b"\x1b[27;2;13~"); } @@ -4033,7 +4100,7 @@ mod tests { pane.process_pty_bytes(pane_id, 0, b"\x1b[>1u", &tx); let key = crate::input::parse_terminal_key_sequence("\x1b\x7f").unwrap(); - let encoded = pane.encode_terminal_key(key, crate::input::KeyboardProtocol::Legacy); + let encoded = pane.encode_terminal_key(key.clone(), crate::input::KeyboardProtocol::Legacy); assert_eq!(encoded, b"\x1b[127;3u"); } @@ -4052,7 +4119,10 @@ mod tests { crossterm::event::KeyModifiers::CONTROL, ); assert_eq!( - legacy.encode_terminal_key(ctrl_backspace, crate::input::KeyboardProtocol::Legacy), + legacy.encode_terminal_key( + ctrl_backspace.clone(), + crate::input::KeyboardProtocol::Legacy + ), b"\x08" ); |
