Use raw terminal input without cursor translation
This commit is contained in:
+1
-103
@@ -555,10 +555,7 @@ 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 remote_terminal = vt100::Parser::new(last_size.1.max(1), last_size.0.max(1), 0);
|
|
||||||
let mut input_translator = UserInputTranslator::default();
|
|
||||||
if let Some(frame) = first_frame {
|
if let Some(frame) = first_frame {
|
||||||
remote_terminal.process(&frame.bytes);
|
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
if frame.closed {
|
if frame.closed {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -594,10 +591,6 @@ async fn run_terminal(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if cred.mode != "view-only" {
|
if cred.mode != "view-only" {
|
||||||
let bytes = input_translator.translate(
|
|
||||||
&bytes,
|
|
||||||
remote_terminal.screen().application_cursor(),
|
|
||||||
);
|
|
||||||
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,
|
||||||
@@ -620,7 +613,6 @@ async fn run_terminal(
|
|||||||
&& (cols, rows) != last_size
|
&& (cols, rows) != last_size
|
||||||
{
|
{
|
||||||
last_size = (cols, rows);
|
last_size = (cols, rows);
|
||||||
remote_terminal.set_size(rows.max(1), cols.max(1));
|
|
||||||
if cred.mode != "view-only" {
|
if cred.mode != "view-only" {
|
||||||
send_resize(&socket, addr, &cred, &mut send_seq, cols, rows).await?;
|
send_resize(&socket, addr, &cred, &mut send_seq, cols, rows).await?;
|
||||||
}
|
}
|
||||||
@@ -638,7 +630,6 @@ async fn run_terminal(
|
|||||||
PacketKind::Frame | PacketKind::ResumeOk => {
|
PacketKind::Frame | PacketKind::ResumeOk => {
|
||||||
let plain = protocol::decrypt_body(&packet, &cred.session_key, SERVER_TO_CLIENT)?;
|
let plain = protocol::decrypt_body(&packet, &cred.session_key, SERVER_TO_CLIENT)?;
|
||||||
let frame: Frame = protocol::from_body(&plain)?;
|
let frame: Frame = protocol::from_body(&plain)?;
|
||||||
remote_terminal.process(&frame.bytes);
|
|
||||||
render_frame(&frame)?;
|
render_frame(&frame)?;
|
||||||
cred.last_rendered_seq = frame.output_seq;
|
cred.last_rendered_seq = frame.output_seq;
|
||||||
send_ack(&socket, addr, &cred, &mut send_seq).await?;
|
send_ack(&socket, addr, &cred, &mut send_seq).await?;
|
||||||
@@ -682,54 +673,6 @@ async fn run_terminal(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct UserInputTranslator {
|
|
||||||
state: UserInputState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum UserInputState {
|
|
||||||
#[default]
|
|
||||||
Ground,
|
|
||||||
Esc,
|
|
||||||
Ss3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserInputTranslator {
|
|
||||||
fn translate(&mut self, bytes: &[u8], application_cursor: bool) -> Vec<u8> {
|
|
||||||
let mut out = Vec::with_capacity(bytes.len());
|
|
||||||
for &byte in bytes {
|
|
||||||
match self.state {
|
|
||||||
UserInputState::Ground => {
|
|
||||||
if byte == 0x1b {
|
|
||||||
self.state = UserInputState::Esc;
|
|
||||||
}
|
|
||||||
out.push(byte);
|
|
||||||
}
|
|
||||||
UserInputState::Esc => {
|
|
||||||
if byte == b'O' {
|
|
||||||
self.state = UserInputState::Ss3;
|
|
||||||
} else {
|
|
||||||
self.state = UserInputState::Ground;
|
|
||||||
out.push(byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UserInputState::Ss3 => {
|
|
||||||
self.state = UserInputState::Ground;
|
|
||||||
if !application_cursor && matches!(byte, b'A'..=b'D') {
|
|
||||||
out.push(b'[');
|
|
||||||
out.push(byte);
|
|
||||||
} else {
|
|
||||||
out.push(b'O');
|
|
||||||
out.push(byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_resize(
|
async fn send_resize(
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
@@ -840,7 +783,7 @@ impl RawMode {
|
|||||||
fn enter() -> Result<Self> {
|
fn enter() -> Result<Self> {
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = std::io::stdout();
|
let mut stdout = std::io::stdout();
|
||||||
stdout.write_all(TERMINAL_OPEN)?;
|
stdout.write_all(TERMINAL_CLEANUP)?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
@@ -855,23 +798,6 @@ impl Drop for RawMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const TERMINAL_OPEN: &[u8] = concat!(
|
|
||||||
"\x1b[?1l",
|
|
||||||
"\x1b[0m",
|
|
||||||
"\x1b[?25h",
|
|
||||||
"\x1b[?1003l",
|
|
||||||
"\x1b[?1002l",
|
|
||||||
"\x1b[?1001l",
|
|
||||||
"\x1b[?1000l",
|
|
||||||
"\x1b[?1015l",
|
|
||||||
"\x1b[?1006l",
|
|
||||||
"\x1b[?1005l",
|
|
||||||
"\x1b[?2004l",
|
|
||||||
"\x1b[?1049l",
|
|
||||||
"\x1b[?1h"
|
|
||||||
)
|
|
||||||
.as_bytes();
|
|
||||||
|
|
||||||
const TERMINAL_CLEANUP: &[u8] = concat!(
|
const TERMINAL_CLEANUP: &[u8] = concat!(
|
||||||
"\x1b[?1l",
|
"\x1b[?1l",
|
||||||
"\x1b[0m",
|
"\x1b[0m",
|
||||||
@@ -887,31 +813,3 @@ const TERMINAL_CLEANUP: &[u8] = concat!(
|
|||||||
"\x1b[?1049l"
|
"\x1b[?1049l"
|
||||||
)
|
)
|
||||||
.as_bytes();
|
.as_bytes();
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::UserInputTranslator;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn translates_application_cursor_arrows_for_normal_remote_mode() {
|
|
||||||
let mut translator = UserInputTranslator::default();
|
|
||||||
assert_eq!(translator.translate(b"\x1bOA", false), b"\x1b[A");
|
|
||||||
assert_eq!(translator.translate(b"\x1bOB", false), b"\x1b[B");
|
|
||||||
assert_eq!(translator.translate(b"\x1bOC", false), b"\x1b[C");
|
|
||||||
assert_eq!(translator.translate(b"\x1bOD", false), b"\x1b[D");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn preserves_application_cursor_arrows_for_remote_application_mode() {
|
|
||||||
let mut translator = UserInputTranslator::default();
|
|
||||||
assert_eq!(translator.translate(b"\x1bOA", true), b"\x1bOA");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn preserves_split_escape_state_between_reads() {
|
|
||||||
let mut translator = UserInputTranslator::default();
|
|
||||||
assert_eq!(translator.translate(b"\x1b", false), b"\x1b");
|
|
||||||
assert_eq!(translator.translate(b"O", false), b"");
|
|
||||||
assert_eq!(translator.translate(b"A", false), b"[A");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user