#![cfg(unix)]
use std::io::Write;
pub(crate) struct RawModeGuard {
fd: i32,
original_termios: libc::termios,
cursor_hidden: bool,
}
impl RawModeGuard {
pub(crate) fn enter(fd: i32, hide_cursor: bool) -> Option<Self> {
let mut original_termios: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(fd, &mut original_termios) } != 0 {
return None;
}
let mut raw = original_termios;
unsafe { libc::cfmakeraw(&mut raw) };
if unsafe { libc::tcsetattr(fd, libc::TCSADRAIN, &raw) } != 0 {
return None;
}
if hide_cursor {
let stderr = std::io::stderr();
let mut handle = stderr.lock();
let _ = handle.write_all(b"\x1b[?25l");
let _ = handle.flush();
}
Some(Self {
fd,
original_termios,
cursor_hidden: hide_cursor,
})
}
}
impl Drop for RawModeGuard {
fn drop(&mut self) {
if self.cursor_hidden {
let stderr = std::io::stderr();
let mut handle = stderr.lock();
let _ = handle.write_all(b"\x1b[?25h");
let _ = handle.flush();
}
unsafe { libc::tcsetattr(self.fd, libc::TCSADRAIN, &self.original_termios) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enter_returns_none_on_bad_fd() {
assert!(RawModeGuard::enter(-1, false).is_none());
assert!(RawModeGuard::enter(-1, true).is_none());
}
}