blob: 59ac2768c8da440e836627ace7172702efe3ae33 [file] [log] [blame]
use super::{Height, Width};
use std::os::unix::io::RawFd;
/// Returns the size of the terminal defaulting to STDOUT, if available.
///
/// If STDOUT is not a tty, returns `None`
pub fn terminal_size() -> Option<(Width, Height)> {
terminal_size_using_fd(libc::STDOUT_FILENO)
}
/// Returns the size of the terminal using the given file descriptor, if available.
///
/// If the given file descriptor is not a tty, returns `None`
pub fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
use libc::ioctl;
use libc::isatty;
use libc::{winsize as WinSize, TIOCGWINSZ};
let is_tty: bool = unsafe { isatty(fd) == 1 };
if !is_tty {
return None;
}
let mut winsize = WinSize {
ws_row: 0,
ws_col: 0,
ws_xpixel: 0,
ws_ypixel: 0,
};
if unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut winsize) } == -1 {
return None;
}
let rows = winsize.ws_row;
let cols = winsize.ws_col;
if rows > 0 && cols > 0 {
Some((Width(cols), Height(rows)))
} else {
None
}
}
#[test]
/// Compare with the output of `stty size`
fn compare_with_stty() {
use std::process::Command;
use std::process::Stdio;
let (rows, cols) = if cfg!(target_os = "illumos") {
// illumos stty(1) does not accept a device argument, instead using
// stdin unconditionally:
let output = Command::new("stty")
.stdin(Stdio::inherit())
.output()
.unwrap();
assert!(output.status.success());
// stdout includes the row and columns thus: "rows = 80; columns = 24;"
let vals = String::from_utf8(output.stdout)
.unwrap()
.lines()
.map(|line| {
// Split each line on semicolons to get "k = v" strings:
line.split(';')
.map(str::trim)
.map(str::to_string)
.collect::<Vec<_>>()
})
.flatten()
.filter_map(|term| {
// split each "k = v" string and look for rows/columns:
match term.splitn(2, " = ").collect::<Vec<_>>().as_slice() {
["rows", n] | ["columns", n] => Some(n.parse().unwrap()),
_ => None,
}
})
.collect::<Vec<_>>();
(vals[0], vals[1])
} else {
let output = if cfg!(target_os = "linux") {
Command::new("stty")
.arg("size")
.arg("-F")
.arg("/dev/stderr")
.stderr(Stdio::inherit())
.output()
.unwrap()
} else {
Command::new("stty")
.arg("-f")
.arg("/dev/stderr")
.arg("size")
.stderr(Stdio::inherit())
.output()
.unwrap()
};
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).unwrap();
// stdout is "rows cols"
let mut data = stdout.split_whitespace();
println!("{}", stdout);
let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
(rows, cols)
};
println!("{} {}", rows, cols);
if let Some((Width(w), Height(h))) = terminal_size() {
assert_eq!(rows, h);
assert_eq!(cols, w);
} else {
panic!("terminal_size() return None");
}
}