use std::cmp::Ordering;
#[derive(Debug, Copy, Clone)]
pub struct PixelSize {
pub x: u32,
pub y: u32,
}
impl PixelSize {
pub fn from_xy((x, y): (u32, u32)) -> Self {
Self { x, y }
}
}
impl PartialEq for PixelSize {
fn eq(&self, other: &Self) -> bool {
matches!(self.partial_cmp(other), Some(Ordering::Equal))
}
}
impl PartialOrd for PixelSize {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.x == other.x && self.y == other.y {
Some(Ordering::Equal)
} else if self.x < other.x && self.y < other.y {
Some(Ordering::Less)
} else if self.x > other.x && self.y > other.y {
Some(Ordering::Greater)
} else {
None
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct TerminalSize {
pub columns: usize,
pub rows: usize,
pub pixels: Option<PixelSize>,
}
impl Default for TerminalSize {
fn default() -> Self {
TerminalSize {
columns: 80,
rows: 24,
pixels: None,
}
}
}
#[cfg(unix)]
extern "C" {
pub fn ctermid(c: *mut libc::c_char) -> *mut libc::c_char;
}
#[cfg(unix)]
#[inline]
fn from_terminal_impl() -> Option<TerminalSize> {
unsafe {
let mut winsize = libc::winsize {
ws_row: 0,
ws_col: 0,
ws_xpixel: 0,
ws_ypixel: 0,
};
let cterm_path = ctermid(std::ptr::null_mut());
if cterm_path.is_null() {
None
} else {
let fd = libc::open(cterm_path, libc::O_RDONLY);
#[allow(clippy::useless_conversion)]
let result = libc::ioctl(fd, libc::TIOCGWINSZ.into(), &mut winsize);
libc::close(fd);
if result == -1 || winsize.ws_row == 0 || winsize.ws_col == 0 {
None
} else {
Some(winsize)
}
}
}
.map(|winsize| {
let window = if winsize.ws_xpixel != 0 && winsize.ws_ypixel != 0 {
Some(PixelSize {
x: winsize.ws_xpixel as u32,
y: winsize.ws_ypixel as u32,
})
} else {
None
};
TerminalSize {
columns: winsize.ws_col as usize,
rows: winsize.ws_row as usize,
pixels: window,
}
})
}
#[cfg(windows)]
#[inline]
fn from_terminal_impl() -> Option<TerminalSize> {
use terminal_size::{terminal_size, Height, Width};
terminal_size().map(|(Width(w), Height(h))| TerminalSize {
rows: h as usize,
columns: w as usize,
pixels: None,
})
}
impl TerminalSize {
pub fn from_env() -> Option<Self> {
let columns = std::env::var("COLUMNS")
.ok()
.and_then(|value| value.parse::<usize>().ok());
let rows = std::env::var("LINES")
.ok()
.and_then(|value| value.parse::<usize>().ok());
match (columns, rows) {
(Some(columns), Some(rows)) => Some(Self {
columns,
rows,
pixels: None,
}),
_ => None,
}
}
pub fn from_terminal() -> Option<Self> {
from_terminal_impl()
}
pub fn detect() -> Option<Self> {
Self::from_terminal().or_else(Self::from_env)
}
}