use std::io::{Read, stdin, stdout, Write}; use std::os::unix::io::RawFd; use anes::parser::{KeyCode, Sequence}; use termios::{tcgetattr, tcsetattr, Termios}; const STDIN_FILENO : RawFd = 0; struct StdinNonblockingGuard { original_termios : Termios, } impl StdinNonblockingGuard { pub fn new() -> std::io::Result { let mut old_tio = unsafe { std::mem::zeroed::() }; /* get the terminal settings for stdin */ tcgetattr(STDIN_FILENO, &mut old_tio)?; /* we want to keep the old setting to restore them a the end */ let mut new_tio = old_tio; /* disable canonical mode (buffered i/o) and local echo */ new_tio.c_lflag &= !termios::ICANON & !termios::ECHO; /* set the new settings immediately */ tcsetattr(STDIN_FILENO, termios::TCSANOW, &new_tio)?; Ok(Self { original_termios: old_tio, }) } } impl Drop for StdinNonblockingGuard { fn drop(&mut self) { /* restore the former settings */ let _ = tcsetattr(STDIN_FILENO, termios::TCSANOW, &self.original_termios); } } enum InputEvent { Key(anes::parser::Sequence), EndOfStream, } struct TerminalInterface { } impl TerminalInterface { pub fn new() -> (Self, crossbeam_channel::Receiver) { let (sender, receiver) = crossbeam_channel::bounded(0); std::thread::spawn(move || { let stdin_guard = StdinNonblockingGuard::new().unwrap(); let mut sin = stdin().lock(); let mut parser = anes::parser::Parser::default(); 'input: loop { let mut buf = [0u8; 64]; // TODO add a way to kill the thread from outside let numch = match sin.read(&mut buf[..]) { Err(_) | Ok(0) => { let _ = sender.send(InputEvent::EndOfStream); break 'input; }, Ok(numch) => numch }; let received_slice = &buf[0..numch]; parser.advance(received_slice, numch == buf.len()); while let Some(item) = parser.next() { if sender.send(InputEvent::Key(item)).is_err() { break 'input; } } } drop(stdin_guard); }); (Self {}, receiver) } } impl Write for TerminalInterface { fn write(&mut self, buf: &[u8]) -> std::io::Result { stdout().write(buf) } fn flush(&mut self) -> std::io::Result<()> { stdout().flush() } } fn main() -> std::io::Result<()> { let (mut term, receiver) = TerminalInterface::new(); 'lp: loop { match receiver.recv() { Ok(InputEvent::EndOfStream) | Err(_) => { break 'lp; } Ok(InputEvent::Key(k)) => { write!(term, "{:?}\n", k)?; if let Sequence::Key(KeyCode::Esc, _) = k { write!(term, "ESC, exit\n")?; break 'lp; } } } } Ok(()) }