1use crossterm::terminal::size as term_size;
11use std::sync::{Mutex, OnceLock};
12
13static SAVED_TERMIOS: OnceLock<Mutex<Option<libc::termios>>> = OnceLock::new();
16
17pub fn enable_raw_mode() -> std::io::Result<()> {
22 use std::os::fd::AsRawFd;
23 let fd = std::io::stdin().as_raw_fd();
24 let mut t: libc::termios = unsafe { std::mem::zeroed() };
25 if unsafe { libc::tcgetattr(fd, &mut t) } != 0 {
26 return Err(std::io::Error::last_os_error());
27 }
28 *SAVED_TERMIOS
29 .get_or_init(|| Mutex::new(None))
30 .lock()
31 .unwrap() = Some(t);
32
33 t.c_iflag &= !(libc::IGNBRK
36 | libc::BRKINT
37 | libc::PARMRK
38 | libc::ISTRIP
39 | libc::INLCR
40 | libc::IGNCR
41 | libc::ICRNL
42 | libc::IXON);
43 t.c_oflag &= !libc::OPOST;
44 t.c_lflag &= !(libc::ECHO | libc::ECHONL | libc::ICANON | libc::ISIG | libc::IEXTEN);
45 t.c_cflag &= !(libc::CSIZE | libc::PARENB);
46 t.c_cflag |= libc::CS8;
47 t.c_cc[libc::VMIN] = 1;
48 t.c_cc[libc::VTIME] = 0;
49 if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 {
50 return Err(std::io::Error::last_os_error());
51 }
52 Ok(())
53}
54
55pub fn disable_raw_mode() -> std::io::Result<()> {
58 use std::os::fd::AsRawFd;
59 let saved = SAVED_TERMIOS
60 .get_or_init(|| Mutex::new(None))
61 .lock()
62 .unwrap()
63 .take();
64 if let Some(t) = saved {
65 let fd = std::io::stdin().as_raw_fd();
66 if unsafe { libc::tcsetattr(fd, libc::TCSANOW, &t) } != 0 {
67 return Err(std::io::Error::last_os_error());
68 }
69 }
70 Ok(())
71}
72
73pub fn init_terminal() -> Result<(), Box<dyn std::error::Error>> {
75 enable_raw_mode()?;
76 Ok(())
77}
78
79pub fn restore_terminal() -> Result<(), Box<dyn std::error::Error>> {
81 disable_raw_mode()?;
82 Ok(())
83}
84
85pub fn get_window_size() -> Result<(u16, u16), Box<dyn std::error::Error>> {
87 let (w, h) = term_size()?;
88 Ok((w, h))
89}