extern crate libc; use std::fs::File; use std::mem::MaybeUninit as MU; use std::ops::Deref; use std::os::fd::IntoRawFd; use libc::*; struct Fd(i32, bool); impl Drop for Fd { fn drop(&mut self) { if self.1 { // SAFETY: #[allow(clippy::undocumented_unsafe_blocks)] unsafe { close(self.0) }; } } } impl Fd { pub fn new(x: File) -> Self { Self(x.into_raw_fd(), true) } } impl From for Fd { fn from(value: i32) -> Self { Self(value, false) } } impl Deref for Fd { type Target = i32; fn deref(&self) -> &Self::Target { &self.0 } } pub fn size() -> Option<(u16, u16)> { // SAFETY: SYS unsafe { let mut size = MU::::uninit(); if ioctl( *File::open("/dev/tty") .map(Fd::new) .unwrap_or(Fd::from(STDIN_FILENO)), TIOCGWINSZ, size.as_mut_ptr(), ) != -1 { let winsize { ws_col, ws_row, .. } = size.assume_init(); return Some((ws_col as _, ws_row as _)).filter(|&(w, h)| w != 0 && h != 0); } tput("cols").and_then(|w| tput("lines").map(|h| (w, h))) } } pub fn tput(arg: &'static str) -> Option { let x = std::process::Command::new("tput").arg(arg).output().ok()?; String::from_utf8(x.stdout) .ok() .and_then(|x| x.parse::().ok()) } #[test] fn t() { println!("{:?}", size().unwrap()); }