rs-kilo/src/helpers.rs

111 lines
2.7 KiB
Rust

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::IGNCR
| InputFlags::IGNBRK
| InputFlags::ICRNL
| InputFlags::INLCR
| InputFlags::INPCK
| InputFlags::ISTRIP
| InputFlags::IXON
| InputFlags::PARMRK,
);
raw.output_flags.remove(OutputFlags::OPOST);
raw.local_flags.remove(
LocalFlags::ECHO
| LocalFlags::ECHONL
| LocalFlags::ICANON
| LocalFlags::IEXTEN
| LocalFlags::ISIG,
);
raw.control_flags
.remove(ControlFlags::CSIZE | ControlFlags::PARENB);
// 8 bit characters
raw.control_flags |= termios::ControlFlags::CS8;
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();
}
pub fn get_term_size() -> Option<TermSize> {
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
}
}