aboutsummaryrefslogtreecommitdiff
path: root/src/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol')
-rw-r--r--src/protocol/wire.rs129
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 {