You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
3.1 KiB
117 lines
3.1 KiB
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<Self> {
|
|
let mut old_tio = unsafe { std::mem::zeroed::<Termios>() };
|
|
|
|
/* 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<InputEvent>) {
|
|
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<usize> {
|
|
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(())
|
|
}
|
|
|