mod parse;
mod types;
pub use types::*;
use std::io::{self, Read};
use std::time::Duration;
pub fn read() -> io::Result<Event> {
let mut reader = EventReader::new();
reader.read_event()
}
pub fn poll(timeout: Duration) -> io::Result<bool> {
poll_stdin(timeout)
}
struct EventReader {
buf: Vec<u8>,
}
impl EventReader {
fn new() -> Self {
Self {
buf: Vec::with_capacity(64),
}
}
fn read_event(&mut self) -> io::Result<Event> {
loop {
if let Some((event, consumed)) = parse::parse(&self.buf) {
self.buf.drain(..consumed);
return Ok(event);
}
if self.buf.len() == 1
&& self.buf[0] == b'\x1b'
&& !poll_stdin(Duration::from_millis(50))?
{
self.buf.clear();
return Ok(parse::esc_event());
}
let mut tmp = [0u8; 64];
match io::stdin().lock().read(&mut tmp) {
Ok(0) => {
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "stdin closed"));
}
Ok(n) => {
self.buf.extend_from_slice(&tmp[..n]);
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
poll_stdin(Duration::from_millis(100))?;
continue;
}
Err(e) => return Err(e),
}
}
}
}
#[cfg(any(unix, target_os = "wasi"))]
fn poll_stdin(timeout: Duration) -> io::Result<bool> {
use std::os::fd::AsRawFd;
let fd = io::stdin().as_raw_fd();
let millis = timeout.as_millis().min(i32::MAX as u128) as i32;
unsafe {
let mut pfd = libc::pollfd {
fd,
events: libc::POLLIN,
revents: 0,
};
let ret = libc::poll(&mut pfd, 1, millis);
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(ret > 0 && (pfd.revents & libc::POLLIN) != 0)
}
}
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
fn poll_stdin(timeout: Duration) -> io::Result<bool> {
let _ = timeout;
Ok(true)
}