Forward terminal input bytes without cursor rewriting
This commit is contained in:
+1
-74
@@ -605,7 +605,6 @@ async fn run_terminal(
|
|||||||
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
||||||
let mut resize_tick = tokio::time::interval(Duration::from_millis(250));
|
let mut resize_tick = tokio::time::interval(Duration::from_millis(250));
|
||||||
let mut last_size = size().unwrap_or((80, 24));
|
let mut last_size = size().unwrap_or((80, 24));
|
||||||
let mut input_normalizer = InputNormalizer::default();
|
|
||||||
if let Some(frame) = first_frame {
|
if let Some(frame) = first_frame {
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
if frame.closed {
|
if frame.closed {
|
||||||
@@ -642,7 +641,6 @@ async fn run_terminal(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if cred.mode != "view-only" {
|
if cred.mode != "view-only" {
|
||||||
let bytes = input_normalizer.normalize(&bytes);
|
|
||||||
let body = protocol::to_body(&Input { bytes })?;
|
let body = protocol::to_body(&Input { bytes })?;
|
||||||
let packet = protocol::encode_encrypted(
|
let packet = protocol::encode_encrypted(
|
||||||
PacketKind::Input,
|
PacketKind::Input,
|
||||||
@@ -725,54 +723,6 @@ async fn run_terminal(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct InputNormalizer {
|
|
||||||
state: InputNormalizerState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum InputNormalizerState {
|
|
||||||
#[default]
|
|
||||||
Ground,
|
|
||||||
Esc,
|
|
||||||
Ss3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InputNormalizer {
|
|
||||||
fn normalize(&mut self, bytes: &[u8]) -> Vec<u8> {
|
|
||||||
let mut out = Vec::with_capacity(bytes.len());
|
|
||||||
for &byte in bytes {
|
|
||||||
match self.state {
|
|
||||||
InputNormalizerState::Ground => {
|
|
||||||
out.push(byte);
|
|
||||||
if byte == 0x1b {
|
|
||||||
self.state = InputNormalizerState::Esc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InputNormalizerState::Esc => {
|
|
||||||
if byte == b'O' {
|
|
||||||
self.state = InputNormalizerState::Ss3;
|
|
||||||
} else {
|
|
||||||
out.push(byte);
|
|
||||||
self.state = InputNormalizerState::Ground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
InputNormalizerState::Ss3 => {
|
|
||||||
if matches!(byte, b'A'..=b'D') {
|
|
||||||
out.push(b'[');
|
|
||||||
out.push(byte);
|
|
||||||
} else {
|
|
||||||
out.push(b'O');
|
|
||||||
out.push(byte);
|
|
||||||
}
|
|
||||||
self.state = InputNormalizerState::Ground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_resize(
|
async fn send_resize(
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
@@ -916,30 +866,7 @@ const TERMINAL_CLEANUP: &[u8] = concat!(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{InputNormalizer, parse_ssh_config_hostname, ssh_destination_host};
|
use super::{parse_ssh_config_hostname, ssh_destination_host};
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn normalizes_ss3_cursor_keys_to_csi() {
|
|
||||||
let mut normalizer = InputNormalizer::default();
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1bOA"), b"\x1b[A");
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1bOB"), b"\x1b[B");
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1bOC"), b"\x1b[C");
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1bOD"), b"\x1b[D");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn preserves_split_ss3_cursor_state_between_reads() {
|
|
||||||
let mut normalizer = InputNormalizer::default();
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1b"), b"\x1b");
|
|
||||||
assert_eq!(normalizer.normalize(b"O"), b"");
|
|
||||||
assert_eq!(normalizer.normalize(b"B"), b"[B");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn leaves_non_cursor_ss3_sequences_raw() {
|
|
||||||
let mut normalizer = InputNormalizer::default();
|
|
||||||
assert_eq!(normalizer.normalize(b"\x1bOP"), b"\x1bOP");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parses_ssh_config_hostname() {
|
fn parses_ssh_config_hostname() {
|
||||||
|
|||||||
Reference in New Issue
Block a user