use std::{
io::{self, Write},
ops,
os::fd::AsFd,
};
use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
use sys::Termios;
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
pub struct RawTerminal<W: Write + AsFd> {
prev_ios: Termios,
output: W,
}
impl<W: Write + AsFd> Drop for RawTerminal<W> {
fn drop(&mut self) {
let _ = set_terminal_attr(self.output.as_fd(), &self.prev_ios);
}
}
impl<W: Write + AsFd> ops::Deref for RawTerminal<W> {
type Target = W;
fn deref(&self) -> &W {
&self.output
}
}
impl<W: Write + AsFd> ops::DerefMut for RawTerminal<W> {
fn deref_mut(&mut self) -> &mut W {
&mut self.output
}
}
impl<W: Write + AsFd> Write for RawTerminal<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
}
#[cfg(unix)]
mod unix_impl {
use super::*;
use std::os::unix::io::{AsFd, BorrowedFd};
impl<W: Write + AsFd> AsFd for RawTerminal<W> {
fn as_fd(&self) -> BorrowedFd {
self.output.as_fd()
}
}
}
pub trait IntoRawMode: Write + AsFd + Sized {
fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
}
impl<W: Write + AsFd> IntoRawMode for W {
fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
let mut ios = get_terminal_attr(self.as_fd())?;
let prev_ios = ios;
raw_terminal_attr(&mut ios);
set_terminal_attr(self.as_fd(), &ios)?;
Ok(RawTerminal {
prev_ios,
output: self,
})
}
}
impl<W: Write + AsFd> RawTerminal<W> {
pub fn suspend_raw_mode(&self) -> io::Result<()> {
set_terminal_attr(self.as_fd(), &self.prev_ios)?;
Ok(())
}
pub fn activate_raw_mode(&self) -> io::Result<()> {
let mut ios = get_terminal_attr(self.as_fd())?;
raw_terminal_attr(&mut ios);
set_terminal_attr(self.as_fd(), &ios)?;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use std::io::{stdout, Write};
#[test]
fn test_into_raw_mode() {
let mut out = stdout().into_raw_mode().unwrap();
out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
drop(out);
}
}