use libc;
use tokio_core;
use nix;
use mio;
use termios;
use mio::unix::EventedFd;
use tokio_core::reactor::PollEvented;
use std::io;
static STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
static STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
static STDERR_FILENO: libc::c_int = libc::STDERR_FILENO;
pub type Mode = termios::Termios;
pub struct StdioFd(mio::unix::EventedFd<'static>);
impl StdioFd {
fn stdin() -> Self {
StdioFd(EventedFd(&STDIN_FILENO)).set_nonblocking()
}
fn stdout() -> Self {
StdioFd(EventedFd(&STDOUT_FILENO)).set_nonblocking()
}
fn stderr() -> Self {
StdioFd(EventedFd(&STDERR_FILENO)).set_nonblocking()
}
fn set_nonblocking(self) -> Self {
use nix::fcntl::{fcntl, FcntlArg, O_NONBLOCK};
fcntl(*(self.0).0, FcntlArg::F_SETFL(O_NONBLOCK)).expect("fcntl");
self
}
}
impl mio::Evented for StdioFd {
fn register(&self, poll: &mio::Poll, token: mio::Token, interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
self.0.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &mio::Poll, token: mio::Token, interest: mio::Ready, opts: mio::PollOpt) -> io::Result<()> {
self.0.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
self.0.deregister(poll)
}
}
impl io::Read for StdioFd {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {
match nix::unistd::read(*(self.0).0, buf) {
Err(nix::Error::Sys(nix::errno::EINTR)) => {
},
Err(nix::Error::Sys(e)) => return Err(e.into()),
Err(nix::Error::InvalidPath) => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid path")),
Ok(count) => return Ok(count),
}
}
}
}
impl io::Write for StdioFd {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
loop {
match nix::unistd::write(*(self.0).0, buf) {
Err(nix::Error::Sys(nix::errno::EINTR)) => {
},
Err(nix::Error::Sys(e)) => return Err(e.into()),
Err(nix::Error::InvalidPath) => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid path")),
Ok(count) => return Ok(count),
}
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct RawStdio {
stdin : PollEvented<StdioFd>,
stdout : PollEvented<StdioFd>,
stderr : PollEvented<StdioFd>,
stdin_isatty : bool,
}
pub type PollFd = PollEvented<StdioFd>;
impl RawStdio {
pub fn new(handle : &tokio_core::reactor::Handle) -> io::Result<Self> {
let stdin_poll_evented = PollEvented::new(StdioFd::stdin(), handle)?;
let stdout_poll_evented = PollEvented::new(StdioFd::stdout(), handle)?;
let stderr_poll_evented = PollEvented::new(StdioFd::stderr(), handle)?;
let raw_stdio = RawStdio {
stdin : stdin_poll_evented,
stdout : stdout_poll_evented,
stderr : stderr_poll_evented,
stdin_isatty : true,
};
raw_stdio.enable_raw_mode()?;
Ok(raw_stdio)
}
fn enable_raw_mode(&self) -> io::Result<termios::Termios> {
let mut orig_term = termios::Termios::from_fd(STDIN_FILENO)?;
use nix::errno::Errno::ENOTTY;
use termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP,
IXON, VMIN, VTIME};
if !self.stdin_isatty {
try!(Err(nix::Error::from_errno(ENOTTY)));
}
termios::tcgetattr(STDIN_FILENO, &mut orig_term)?;
let mut raw = orig_term;
raw.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_cflag |= CS8; raw.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; try!(termios::tcsetattr(STDIN_FILENO, termios::TCSADRAIN, &raw));
Ok(orig_term)
}
pub fn split(self) -> (PollEvented<StdioFd>, PollEvented<StdioFd>, PollEvented<StdioFd>) {
(self.stdin, self.stdout, self.stderr)
}
}