1use super::{Height, Width};
2use std::os::unix::io::{AsFd, BorrowedFd, RawFd};
3
4pub fn terminal_size() -> Option<(Width, Height)> {
10 if let Some(size) = terminal_size_of(std::io::stdout()) {
11 Some(size)
12 } else if let Some(size) = terminal_size_of(std::io::stderr()) {
13 Some(size)
14 } else if let Some(size) = terminal_size_of(std::io::stdin()) {
15 Some(size)
16 } else {
17 None
18 }
19}
20
21pub fn terminal_size_of<Fd: AsFd>(fd: Fd) -> Option<(Width, Height)> {
25 use rustix::termios::{isatty, tcgetwinsize};
26
27 if !isatty(&fd) {
28 return None;
29 }
30
31 let winsize = tcgetwinsize(&fd).ok()?;
32
33 let rows = winsize.ws_row;
34 let cols = winsize.ws_col;
35
36 if rows > 0 && cols > 0 {
37 Some((Width(cols), Height(rows)))
38 } else {
39 None
40 }
41}
42
43#[deprecated(note = "Use `terminal_size_of` instead.
53 Use `BorrowedFd::borrow_raw` to convert a raw fd into a `BorrowedFd` if needed.")]
54pub unsafe fn terminal_size_using_fd(fd: RawFd) -> Option<(Width, Height)> {
55 terminal_size_of(BorrowedFd::borrow_raw(fd))
56}
57
58#[test]
59fn compare_with_stty() {
61 use std::process::Command;
62 use std::process::Stdio;
63
64 let (rows, cols) = if cfg!(target_os = "illumos") {
65 let output = Command::new("stty")
68 .stdin(Stdio::inherit())
69 .output()
70 .unwrap();
71 assert!(output.status.success());
72
73 let vals = String::from_utf8(output.stdout)
75 .unwrap()
76 .lines()
77 .map(|line| {
78 line.split(';')
80 .map(str::trim)
81 .map(str::to_string)
82 .collect::<Vec<_>>()
83 })
84 .flatten()
85 .filter_map(|term| {
86 match term.splitn(2, " = ").collect::<Vec<_>>().as_slice() {
88 ["rows", n] | ["columns", n] => Some(n.parse().unwrap()),
89 _ => None,
90 }
91 })
92 .collect::<Vec<_>>();
93 (vals[0], vals[1])
94 } else {
95 let output = if cfg!(target_os = "linux") {
96 Command::new("stty")
97 .arg("size")
98 .arg("-F")
99 .arg("/dev/stderr")
100 .stderr(Stdio::inherit())
101 .output()
102 .unwrap()
103 } else {
104 Command::new("stty")
105 .arg("-f")
106 .arg("/dev/stderr")
107 .arg("size")
108 .stderr(Stdio::inherit())
109 .output()
110 .unwrap()
111 };
112
113 assert!(output.status.success());
114 let stdout = String::from_utf8(output.stdout).unwrap();
115 let mut data = stdout.split_whitespace();
117 println!("{}", stdout);
118 let rows = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
119 let cols = u16::from_str_radix(data.next().unwrap(), 10).unwrap();
120 (rows, cols)
121 };
122 println!("{} {}", rows, cols);
123
124 if let Some((Width(w), Height(h))) = terminal_size() {
125 assert_eq!(rows, h);
126 assert_eq!(cols, w);
127 } else {
128 panic!("terminal_size() return None");
129 }
130}