#![expect(unsafe_code)]
use std::io::{self, BufRead};
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};
pub use libc::termios as TermMode;
use libc::{SA_SIGINFO, STDIN_FILENO, STDOUT_FILENO, TCSADRAIN, TIOCGWINSZ, VMIN, VTIME};
use libc::{c_int, c_void, sigaction, sighandler_t, siginfo_t, winsize};
use crate::Error;
pub use crate::xdg::*;
fn cerr(err: c_int) -> io::Result<()> {
if err < 0 { Err(io::Error::last_os_error()) } else { Ok(()) }
}
pub fn get_window_size() -> Result<(usize, usize), Error> {
let mut maybe_ws = std::mem::MaybeUninit::<winsize>::uninit();
cerr(unsafe { libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, maybe_ws.as_mut_ptr()) })
.map_or(None, |()| unsafe { Some(maybe_ws.assume_init()) })
.filter(|ws| ws.ws_col != 0 && ws.ws_row != 0)
.map_or(Err(Error::InvalidWindowSize), |ws| Ok((ws.ws_row as usize, ws.ws_col as usize)))
}
static WSC: AtomicBool = AtomicBool::new(false);
extern "C" fn handle_wsize(_: c_int, _: *mut siginfo_t, _: *mut c_void) { WSC.store(true, Relaxed) }
#[expect(clippy::fn_to_numeric_cast_any)]
pub fn register_winsize_change_signal_handler() -> io::Result<()> {
unsafe {
let mut maybe_sa = std::mem::MaybeUninit::<sigaction>::uninit();
cerr(libc::sigemptyset(&raw mut (*maybe_sa.as_mut_ptr()).sa_mask))?;
(*maybe_sa.as_mut_ptr()).sa_flags = SA_SIGINFO;
(*maybe_sa.as_mut_ptr()).sa_sigaction = handle_wsize as *const () as sighandler_t;
cerr(sigaction(libc::SIGWINCH, maybe_sa.as_ptr(), std::ptr::null_mut()))
}
}
pub fn has_window_size_changed() -> bool { WSC.swap(false, Relaxed) }
pub fn set_term_mode(term: &TermMode) -> io::Result<()> {
cerr(unsafe { libc::tcsetattr(STDIN_FILENO, TCSADRAIN, term) })
}
pub fn enable_raw_mode() -> io::Result<TermMode> {
let mut maybe_term = std::mem::MaybeUninit::<TermMode>::uninit();
cerr(unsafe { libc::tcgetattr(STDIN_FILENO, maybe_term.as_mut_ptr()) })?;
let orig_term = unsafe { maybe_term.assume_init() };
let mut term = orig_term;
unsafe { libc::cfmakeraw(&raw mut term) };
(term.c_cc[VMIN], term.c_cc[VTIME]) = (0, 1);
set_term_mode(&term)?;
Ok(orig_term)
}
pub fn stdin() -> io::Result<impl BufRead> { Ok(io::stdin().lock()) }
pub fn path(filename: &str) -> std::path::PathBuf { std::path::PathBuf::from(filename) }