An adapter that exposes termion's input and key event iterators as asynchronous streams.
Compatible with Tokio v1.0.
use futures::StreamExt;
use std::future;
use termion::{event::Key, raw::IntoRawMode};
use termion_input_tokio::TermReadAsync;
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
// Disable line buffering, local echo, etc.
let _raw_term = std::io::stdout().into_raw_mode()?;
tokio::io::stdin()
.keys_stream()
// End the stream when 'q' is pressed.
.take_while(|event| {
future::ready(match event {
Ok(Key::Char('q')) => false,
_ => true,
})
})
// Print each key that was pressed.
.for_each(|event| async move {
println!("{:?}\r", event);
})
.await;
Ok(())
}
It is challenging to use true non-blocking reads with stdin
. In the common case both stdin
and stdout
refer to the same file, typically a PTY. Since non-blocking mode is a per-file property, rather than a per-file-descriptor one, using fcntl
with O_NONBLOCK
to change stdin
into non-blocking mode will also make stdout
non-blocking. Since most code is not prepared to deal with EWOULDBLOCK
when writing to stdout
, asynchronous reads from stdin
are typically typically performed using blocking operations on a secondary thread. This is how AsyncRead
for tokio::io::stdin() is implemented.
This is based on termion-tokio by Kayo Phoenix, which is in turn based on code within termion.