From 86e3e8c121bdc4ff2a2cf1a4430d563e0b6c46b1 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 7 Mar 2021 17:47:10 -0500 Subject: sketch out some input handling --- examples/tmux.rs | 182 ++++++++++++++++++++++--------------------------------- 1 file changed, 73 insertions(+), 109 deletions(-) (limited to 'examples') diff --git a/examples/tmux.rs b/examples/tmux.rs index 1bd2c3f..3d12c65 100644 --- a/examples/tmux.rs +++ b/examples/tmux.rs @@ -1,47 +1,14 @@ use pty_process::Command as _; use smol::io::{AsyncReadExt as _, AsyncWriteExt as _}; -use std::os::unix::io::AsRawFd as _; use textmode::TextmodeExt as _; -pub struct RawGuard { - termios: nix::sys::termios::Termios, -} - -#[allow(clippy::new_without_default)] -impl RawGuard { - pub fn new() -> Self { - let stdin = std::io::stdin().as_raw_fd(); - let termios = nix::sys::termios::tcgetattr(stdin).unwrap(); - let mut termios_raw = termios.clone(); - nix::sys::termios::cfmakeraw(&mut termios_raw); - nix::sys::termios::tcsetattr( - stdin, - nix::sys::termios::SetArg::TCSANOW, - &termios_raw, - ) - .unwrap(); - Self { termios } - } -} - -impl Drop for RawGuard { - fn drop(&mut self) { - let stdin = std::io::stdin().as_raw_fd(); - let _ = nix::sys::termios::tcsetattr( - stdin, - nix::sys::termios::SetArg::TCSANOW, - &self.termios, - ); - } -} - enum Command { NewWindow, NextWindow, } enum Event { - Input(Vec), + Input(textmode::Key), Output, WindowExit(usize), Command(Command), @@ -123,23 +90,75 @@ impl State { .detach(); } - fn spawn_input_task(&self, ex: &smol::Executor<'_>) { + fn spawn_input_task( + &self, + ex: &smol::Executor<'_>, + mut input: textmode::Input, + ) { let notify = self.wevents.clone(); ex.spawn(async move { let mut waiting_for_command = false; - let mut stdin = smol::Unblock::new(std::io::stdin()); - let mut buf = [0u8; 4096]; loop { - match stdin.read(&mut buf).await { - Ok(bytes) => { - waiting_for_command = Self::handle_input( - &buf[..bytes], - notify.clone(), - waiting_for_command, - ) - .await; + let want_single_char = waiting_for_command; + let key_input = smol::unblock(move || { + if want_single_char { + let key = input.read_key_char(); + (input, key) + } else { + let key = input.read_keys(); + (input, key) } - Err(e) => { + }); + match key_input.await { + (returned_input, Ok(Some(key))) => { + if waiting_for_command { + match key { + textmode::Key::Ctrl(b'n') => { + notify + .send(Event::Input(key)) + .await + .unwrap(); + } + textmode::Key::Char('c') => { + notify + .send(Event::Command( + Command::NewWindow, + )) + .await + .unwrap(); + } + textmode::Key::Char('n') => { + notify + .send(Event::Command( + Command::NextWindow, + )) + .await + .unwrap(); + } + _ => { + // ignore + } + } + waiting_for_command = false; + } else { + match key { + textmode::Key::Ctrl(b'n') => { + waiting_for_command = true; + } + _ => { + notify + .send(Event::Input(key)) + .await + .unwrap(); + } + } + } + input = returned_input; + } + (_, Ok(None)) => { + break; + } + (_, Err(e)) => { eprintln!("{}", e); break; } @@ -194,62 +213,6 @@ impl State { .detach(); } - async fn handle_input( - buf: &[u8], - notify: smol::channel::Sender, - mut waiting_for_command: bool, - ) -> bool { - let bytes = buf.len(); - let mut real_buf = Vec::with_capacity(bytes); - for &c in buf { - if waiting_for_command { - match c { - // ^N - 14 => { - real_buf.push(c); - } - // c - 99 => { - notify - .send(Event::Command(Command::NewWindow)) - .await - .unwrap(); - } - // n - 110 => { - notify - .send(Event::Command(Command::NextWindow)) - .await - .unwrap(); - } - _ => {} - } - waiting_for_command = false; - } else { - match c { - // ^N - 14 => { - if !real_buf.is_empty() { - notify - .send(Event::Input(real_buf.clone())) - .await - .unwrap(); - real_buf.clear(); - } - waiting_for_command = true; - } - _ => { - real_buf.push(c); - } - } - } - } - if !real_buf.is_empty() { - notify.send(Event::Input(real_buf.clone())).await.unwrap(); - } - return waiting_for_command; - } - async fn redraw_current_window(&mut self, tm: &mut textmode::Textmode) { let window = self.current_window(); tm.clear(); @@ -346,28 +309,28 @@ impl State { #[must_use] struct Tmux { - _raw: RawGuard, + input: textmode::Input, tm: textmode::Textmode, state: State, } impl Tmux { async fn new() -> Self { - let _raw = RawGuard::new(); + let input = textmode::Input::new(); let tm = textmode::Textmode::new().await.unwrap(); let state = State::new(); - Self { _raw, tm, state } + Self { input, tm, state } } async fn run(self, ex: &smol::Executor<'_>) { let Self { - _raw, + mut input, mut tm, mut state, } = self; state.new_window(ex, state.wevents.clone()); - state.spawn_input_task(ex); + state.spawn_input_task(ex, input.clone()); ex.run(async { loop { @@ -375,12 +338,12 @@ impl Tmux { Ok(Event::Output) => { state.update_current_window(&mut tm).await; } - Ok(Event::Input(buf)) => { + Ok(Event::Input(key)) => { state .current_window() .child .pty() - .write_all(&buf) + .write_all(&key.into_bytes()) .await .unwrap(); } @@ -430,6 +393,7 @@ impl Tmux { .await; tm.cleanup().await.unwrap(); + input.cleanup(); } } -- cgit v1.2.3-54-g00ecf