aboutsummaryrefslogtreecommitdiffstats
path: root/examples/tmux.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/tmux.rs')
-rw-r--r--examples/tmux.rs182
1 files changed, 73 insertions, 109 deletions
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<u8>),
+ 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<Event>,
- 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();
}
}