use libc::ioctl; use libc::{c_ushort, STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ}; use nix::sys::termios; use nix::sys::termios::{ ControlFlags, InputFlags, LocalFlags, OutputFlags, SpecialCharacterIndices, Termios, }; use std::os::unix::io::RawFd; #[derive(Debug)] pub struct TermSize { /// number of rows pub rows: u16, /// number of columns pub cols: u16, } #[repr(C)] #[derive(Debug)] struct UnixTermSize { /// number of rows pub rows: c_ushort, /// number of columns pub cols: c_ushort, x: c_ushort, y: c_ushort, } /// Convert Ctrl+letter chords to their /// ASCII table equivalents #[inline] pub fn ctrl_key(c: char) -> char { let key = c as u8; if !c.is_ascii() { panic!("CTRL_KEY only accepts ASCII characters"); } // Intentionally "overflow" u8 to wrap around to the // beginning of the ASCII table. Ctrl+a is 1, Ctrl+b is 2, etc. (key & 0x1f) as char } /// Get a `Termios` struct, for getting/setting terminal flags pub fn get_termios(fd: RawFd) -> Termios { termios::tcgetattr(fd).unwrap() } /// Put terminal into raw mode so there is full control of terminal output pub fn enable_raw_mode() { let mut raw = get_termios(STDIN_FILENO); raw.input_flags.remove( InputFlags::BRKINT | InputFlags::ICRNL | InputFlags::INPCK | InputFlags::ISTRIP | InputFlags::IXON, ); raw.output_flags.remove(OutputFlags::OPOST); // 8 bit characters raw.control_flags |= ControlFlags::CS8; raw.local_flags .remove(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG); raw.control_chars[SpecialCharacterIndices::VMIN as usize] = 0; raw.control_chars[SpecialCharacterIndices::VTIME as usize] = 1; // Raw mode or bust! termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, &raw).unwrap(); } /// Restore terminal to "cooked"/canonical mode pub fn disable_raw_mode(original: &Termios) { termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, original).unwrap(); } /// Attempt to get the size of the terminal (in rows and columns) from an `ioctl` call pub fn get_term_size() -> Option { let raw = UnixTermSize { rows: 0, cols: 0, x: 0, y: 0, }; let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &raw) }; if r == 0 { Some(TermSize { rows: raw.rows, cols: raw.cols, }) } else { None } }