I'd like to use the mio crate to read keypresses as they arrive in an unbuffered fashion. I already have the code to unbuffer stdin, and I have scaffolded the event loop:
extern crate mio;
extern crate termios;
use termios::{Termios, TCSANOW, ICANON, ECHO, tcsetattr};
use mio::*;
use mio::unix::EventedFd;
fn unbuffer_stdin() {
let termios = Termios::from_fd(0).unwrap();
let mut new_termios = termios.clone();
new_termios.c_lflag &= !(ICANON | ECHO);
tcsetattr(0, TCSANOW, &mut new_termios).unwrap();
}
fn main() {
let stdin = 0;
unbuffer_stdin();
let poll = Poll::new().unwrap();
const STDIN: Token = Token(0);
let ev_fd = EventedFd(&stdin);
poll.register(&ev_fd, STDIN, Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
loop {
poll.poll(&mut events, None).unwrap();
for event in events.iter() {
match event.token() {
STDIN => {
println!("keypress");
// XXX read in ready codepoints to a buffer
}
_ => unreachable!(),
}
}
}
}
How do I implement the part marked XXX
? There are a couple of challenges:
- How do I know how many bytes to read? I'm not sure mio tells me this.
- How do I deal with partial codepoints, where the read spans a multi-byte character.
One solution that might work is to use oneshot
events instead of edge
events, then read one byte per event into a temporary buffer. Each time the buffer makes sense as a complete codepoint, I could then convert it to a char
and store it away, and clear the scratch buffer.
This seems a little inefficient though. What is the best way?