pub(crate) mod io;
pub(crate) mod sizing;
extern "C" {
pub fn tcsetattr(file_des: u32, optional_actions: u32, termios: *const Termios) -> isize;
pub fn tcgetattr(file_des: u32, termios: *mut Termios) -> isize;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub struct TcFlag(u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub struct Speed(u64);
#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[repr(C)]
pub struct Termios {
c_iflag: TcFlag,
c_oflag: TcFlag,
c_cflag: TcFlag,
c_lflag: TcFlag,
c_cc: [u8; CONTROL_CHARS_LEN],
c_ispeed: Speed,
c_ospeed: Speed,
}
impl Termios {
pub fn enable_raw_mode() -> Result<Self, TermiosError> {
let mut orig_termios: Termios = Termios::default();
let result = unsafe { tcgetattr(io::STDIN_FILENO, &mut orig_termios) };
if result == -1 {
return Err(TermiosError::TcGetAttr);
}
let mut raw_termios = orig_termios.clone();
raw_termios.c_iflag.0 &= !(input_flags::IGNBRK
| input_flags::BRKINT
| input_flags::PARMRK
| input_flags::ISTRIP
| input_flags::IGNCR
| input_flags::ICRNL
| input_flags::IXON);
raw_termios.c_cflag.0 |= control_mode::CS8;
raw_termios.c_lflag.0 &= !(local_flags::ECHO | local_flags::ICANON | local_flags::ISIG | local_flags::IEXTEN);
raw_termios.c_cc[ctrl_char_idx::VMIN] = 1;
raw_termios.c_cc[ctrl_char_idx::VTIME] = 0;
let result = unsafe { tcsetattr(io::STDIN_FILENO, term_cmd::TC_SET_ATTR_FLUSH, &raw_termios) };
if result == -1 {
return Err(TermiosError::TcSetAttr);
}
Ok(orig_termios)
}
pub(crate) fn restore(&mut self) {
let _ = unsafe { tcsetattr(io::STDIN_FILENO, term_cmd::TC_SET_ATTR_FLUSH, self) };
}
}
#[derive(Debug)]
pub enum TermiosError {
TcGetAttr,
TcSetAttr,
ReadStdInFailed,
UnknownLetter(io::UnknownLetter),
UnknownKey,
}
impl From<io::UnknownLetter> for TermiosError {
fn from(err: io::UnknownLetter) -> Self {
Self::UnknownLetter(err)
}
}
const CONTROL_CHARS_LEN: usize = 20;
mod input_flags {
pub const IGNBRK: u64 = 0x0000_0001;
pub const BRKINT: u64 = 0x0000_0002;
pub const _IGNPAR: u64 = 0x0000_0004;
pub const PARMRK: u64 = 0x0000_0008;
pub const _INPCK: u64 = 0x0000_0010;
pub const ISTRIP: u64 = 0x0000_0020;
pub const _INLCR: u64 = 0x0000_0040;
pub const IGNCR: u64 = 0x0000_0080;
pub const ICRNL: u64 = 0x0000_0100;
pub const IXON: u64 = 0x0000_0200;
pub const _IXOFF: u64 = 0x0000_0400;
pub const _IXANY: u64 = 0x0000_0800;
pub const _IMAXBEL: u64 = 0x0000_2000;
pub const _IUTF8: u64 = 0x0000_4000;
}
mod output_flags {
pub const _OPOST: u64 = 0x0000_0001;
}
mod control_mode {
pub const _CSIZE: u64 = 0x0000_0300;
pub const CS8: u64 = 0x0000_0300;
pub const _CREAD: u64 = 0x0000_0800;
pub const _PARENB: u64 = 0x0000_1000;
pub const _CLOCAL: u64 = 0x0000_8000;
}
mod local_flags {
pub const ECHO: u64 = 0x0000_0008;
pub const _ECHONL: u64 = 0x0000_0010;
pub const ISIG: u64 = 0x0000_0080;
pub const ICANON: u64 = 0x0000_0100;
pub const IEXTEN: u64 = 0x0000_0400;
}
mod term_cmd {
pub const _TC_SET_ATTR_NOW: u32 = 0;
pub const _TC_SET_ATTR_DRAIN: u32 = 1;
pub const TC_SET_ATTR_FLUSH: u32 = 2;
}
mod ctrl_char_idx {
pub const VMIN: usize = 16;
pub const VTIME: usize = 17;
}