1#![expect(unsafe_code)]
10
11use std::io::{self, BufRead};
12use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
13
14pub use libc::termios as TermMode;
16use libc::{SA_SIGINFO, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TIOCGWINSZ, VMIN, VTIME};
17use libc::{c_int, c_void, sigaction, sighandler_t, siginfo_t, winsize};
18
19use crate::Error;
20pub use crate::xdg::*;
21
22fn cerr(err: c_int) -> io::Result<()> {
23 if err < 0 { Err(io::Error::last_os_error()) } else { Ok(()) }
24}
25
26pub fn get_window_size() -> Result<(usize, usize), Error> {
32 let mut maybe_ws = std::mem::MaybeUninit::<winsize>::uninit();
33 cerr(unsafe { libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, maybe_ws.as_mut_ptr()) })
34 .map_or(None, |()| unsafe { Some(maybe_ws.assume_init()) })
35 .filter(|ws| ws.ws_col != 0 && ws.ws_row != 0)
36 .map_or(Err(Error::InvalidWindowSize), |ws| Ok((ws.ws_row as usize, ws.ws_col as usize)))
37}
38
39static WSC: AtomicBool = AtomicBool::new(false);
42
43extern "C" fn handle_wsize(_: c_int, _: *mut siginfo_t, _: *mut c_void) { WSC.store(true, Relaxed) }
45
46#[allow(dead_code)]
47#[expect(clippy::fn_to_numeric_cast_any)]
48pub fn register_winsize_change_signal_handler() -> io::Result<()> {
52 return Ok(());
53 unsafe {
54 let mut maybe_sa = std::mem::MaybeUninit::<sigaction>::uninit();
55 cerr(libc::sigemptyset(&raw mut (*maybe_sa.as_mut_ptr()).sa_mask))?;
56 (*maybe_sa.as_mut_ptr()).sa_flags = SA_SIGINFO;
59 (*maybe_sa.as_mut_ptr()).sa_sigaction = handle_wsize as *const () as sighandler_t;
60 cerr(sigaction(libc::SIGWINCH, maybe_sa.as_ptr(), std::ptr::null_mut()))
61 }
62}
63
64pub fn has_window_size_changed() -> bool { WSC.swap(false, Relaxed) }
68
69pub fn set_term_mode(term: &TermMode) -> io::Result<()> {
71 cerr(unsafe { libc::tcsetattr(STDIN_FILENO, TCSADRAIN, term) })
72}
73
74pub fn enable_raw_mode() -> io::Result<TermMode> {
78 let mut maybe_term = std::mem::MaybeUninit::<TermMode>::uninit();
79 cerr(unsafe { libc::tcgetattr(STDIN_FILENO, maybe_term.as_mut_ptr()) })?;
80 let orig_term = unsafe { maybe_term.assume_init() };
81 let mut term = orig_term;
82 unsafe { libc::cfmakeraw(&raw mut term) };
83 (term.c_cc[VMIN], term.c_cc[VTIME]) = (0, 1);
86 set_term_mode(&term)?;
87 Ok(orig_term)
88}
89
90pub fn stdin() -> io::Result<impl BufRead> { Ok(io::stdin().lock()) }
98
99pub fn path(filename: &str) -> std::path::PathBuf { std::path::PathBuf::from(filename) }