diff options
Diffstat (limited to 'src/protocol')
| -rw-r--r-- | src/protocol/wire.rs | 129 |
1 files changed, 114 insertions, 15 deletions
diff --git a/src/protocol/wire.rs b/src/protocol/wire.rs index 0d4b14bb..655f47cb 100644 --- a/src/protocol/wire.rs +++ b/src/protocol/wire.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; // --------------------------------------------------------------------------- /// Current protocol version. Bumped when wire format changes incompatibly. -pub const PROTOCOL_VERSION: u32 = 18; +pub const PROTOCOL_VERSION: u32 = 19; /// Maximum allowed frame payload size (2 MB). Frames larger than this are /// rejected to prevent denial-of-service via oversized length prefixes. @@ -115,10 +115,11 @@ pub enum ClientInputEvent { code: ClientKeyCode, modifiers: u8, kind: ClientKeyKind, + repeat_count: u16, + generated_text: Option<String>, + source: ClientKeySource, }, - Text { - codepoint: char, - }, + TextCommit(String), Mouse { kind: ClientMouseKind, column: u16, @@ -132,6 +133,17 @@ pub enum ClientInputEvent { FocusLost, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum ClientKeySource { + Synthesized, + Vt { + bytes: Vec<u8>, + }, + WindowsConsole { + record: crate::input::WindowsKeyRecord, + }, +} + impl ClientKeyKind { #[cfg(any(windows, test))] pub(crate) fn from_crossterm(kind: crossterm::event::KeyEventKind) -> Self { @@ -261,6 +273,9 @@ impl ClientInputEvent { code: ClientKeyCode::from_crossterm(key.code)?, modifiers: key.modifiers.bits(), kind: ClientKeyKind::from_crossterm(key.kind), + repeat_count: 1, + generated_text: None, + source: ClientKeySource::Synthesized, }), crossterm::event::Event::Mouse(mouse) => Some(Self::Mouse { kind: ClientMouseKind::from_crossterm(mouse.kind)?, @@ -281,20 +296,28 @@ impl ClientInputEvent { code, modifiers, kind, - } => crate::raw_input::RawInputEvent::Key( - crate::input::TerminalKey::new( + repeat_count, + generated_text, + source, + } => { + let mut key = crate::input::TerminalKey::new( code.to_crossterm(), crossterm::event::KeyModifiers::from_bits_truncate(*modifiers), ) - .with_kind(kind.to_crossterm()), - ), - Self::Text { codepoint } => crate::raw_input::RawInputEvent::Key( - crate::input::TerminalKey::new( - crossterm::event::KeyCode::Char(*codepoint), - crossterm::event::KeyModifiers::empty(), - ) - .as_text_commit(), - ), + .with_generated_text(generated_text.clone()); + key = match source { + ClientKeySource::Synthesized => key, + ClientKeySource::Vt { bytes } => key.with_vt_bytes(bytes.clone()), + ClientKeySource::WindowsConsole { record } => key.with_windows_record(*record), + }; + key = key + .with_repeat_count(*repeat_count) + .with_kind(kind.to_crossterm()); + crate::raw_input::RawInputEvent::Key(key) + } + Self::TextCommit(text) => { + crate::raw_input::RawInputEvent::Text(crate::input::TextCommit::new(text.clone())) + } Self::Mouse { kind, column, @@ -1077,12 +1100,39 @@ mod tests { code: ClientKeyCode::Char('N'), modifiers: crossterm::event::KeyModifiers::SHIFT.bits(), kind: ClientKeyKind::Press, + + repeat_count: 1, + generated_text: None, + source: crate::protocol::ClientKeySource::Synthesized, }, ClientInputEvent::Key { code: ClientKeyCode::Backspace, modifiers: 0, kind: ClientKeyKind::Press, + repeat_count: 3, + generated_text: None, + source: crate::protocol::ClientKeySource::Vt { + bytes: b"\x1b[127;1u".to_vec(), + }, + }, + ClientInputEvent::Key { + code: ClientKeyCode::Esc, + modifiers: 0, + kind: ClientKeyKind::Release, + repeat_count: 1, + generated_text: None, + source: crate::protocol::ClientKeySource::WindowsConsole { + record: crate::input::WindowsKeyRecord { + key_down: false, + repeat_count: 1, + virtual_key_code: 27, + virtual_scan_code: 1, + unicode: 27, + control_key_state: 0, + }, + }, }, + ClientInputEvent::TextCommit("你🙂".to_owned()), ClientInputEvent::Mouse { kind: ClientMouseKind::Down(ClientMouseButton::Left), column: 3, @@ -1092,17 +1142,58 @@ mod tests { ], }; let encoded = bincode::serde::encode_to_vec(&msg, bincode::config::standard()).unwrap(); + // Freeze the protocol 19 input envelope before it is published. + assert_eq!( + encoded, + vec![ + 7, 5, 0, 15, 78, 1, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 8, 27, 91, 49, 50, 55, 59, 49, + 117, 0, 14, 0, 2, 1, 0, 2, 0, 1, 27, 1, 27, 0, 1, 7, 228, 189, 160, 240, 159, 153, + 130, 2, 0, 0, 3, 4, 0, + ] + ); let (decoded, _): (ClientMessage, _) = bincode::serde::decode_from_slice(&encoded, bincode::config::standard()).unwrap(); assert_eq!(msg, decoded); } #[test] + fn wire_release_cannot_restore_a_grouped_repeat_count() { + let event = ClientInputEvent::Key { + code: ClientKeyCode::Esc, + modifiers: 0, + kind: ClientKeyKind::Release, + repeat_count: 3, + generated_text: Some("ignored".to_owned()), + source: ClientKeySource::Synthesized, + }; + + match event.to_raw_input_event() { + crate::raw_input::RawInputEvent::Key(key) => { + assert_eq!(key.kind, crossterm::event::KeyEventKind::Release); + assert_eq!(key.repeat_count, 1); + assert_eq!(key.generated_text, None); + } + other => panic!("expected key event, got {other:?}"), + } + } + + #[test] fn client_input_events_convert_to_raw_keys() { + let record = crate::input::WindowsKeyRecord { + key_down: false, + repeat_count: 1, + virtual_key_code: 78, + virtual_scan_code: 49, + unicode: 78, + control_key_state: 16, + }; let shifted = ClientInputEvent::Key { code: ClientKeyCode::Char('N'), modifiers: crossterm::event::KeyModifiers::SHIFT.bits(), kind: ClientKeyKind::Press, + repeat_count: 1, + generated_text: None, + source: ClientKeySource::WindowsConsole { record }, } .to_raw_input_event(); match shifted { @@ -1110,6 +1201,10 @@ mod tests { assert_eq!(key.code, crossterm::event::KeyCode::Char('N')); assert_eq!(key.modifiers, crossterm::event::KeyModifiers::SHIFT); assert_eq!(key.kind, crossterm::event::KeyEventKind::Press); + assert_eq!( + key.windows_record().map(|record| record.key_down), + Some(false) + ); } other => panic!("expected shifted key event, got {other:?}"), } @@ -1118,6 +1213,10 @@ mod tests { code: ClientKeyCode::Backspace, modifiers: 0, kind: ClientKeyKind::Press, + + repeat_count: 1, + generated_text: None, + source: crate::protocol::ClientKeySource::Synthesized, } .to_raw_input_event(); match backspace { |
