termsize/
nix.rs

1extern crate libc;
2
3use std::io::IsTerminal;
4
5use self::{
6    super::Size,
7    libc::{c_ushort, ioctl, STDOUT_FILENO, TIOCGWINSZ},
8};
9
10/// A representation of the size of the current terminal
11#[repr(C)]
12#[derive(Debug)]
13pub struct UnixSize {
14    /// number of rows
15    pub rows: c_ushort,
16    /// number of columns
17    pub cols: c_ushort,
18    x: c_ushort,
19    y: c_ushort,
20}
21
22/// Gets the current terminal size
23pub fn get() -> Option<Size> {
24    // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
25    if !std::io::stdout().is_terminal() {
26        return None;
27    }
28    let mut us = UnixSize {
29        rows: 0,
30        cols: 0,
31        x: 0,
32        y: 0,
33    };
34    let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut us) };
35    if r == 0 {
36        Some(Size {
37            rows: us.rows,
38            cols: us.cols,
39        })
40    } else {
41        None
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::{super::Size, get};
48    use std::process::{Command, Output, Stdio};
49
50    #[cfg(target_os = "macos")]
51    fn stty_size() -> Output {
52        Command::new("stty")
53            .arg("-f")
54            .arg("/dev/stderr")
55            .arg("size")
56            .stderr(Stdio::inherit())
57            .output()
58            .expect("expected stty output")
59    }
60
61    #[cfg(not(target_os = "macos"))]
62    fn stty_size() -> Output {
63        Command::new("stty")
64            .arg("-F")
65            .arg("/dev/stderr")
66            .arg("size")
67            .stderr(Stdio::inherit())
68            .output()
69            .expect("expected stty output")
70    }
71
72    #[test]
73    fn test_shell() {
74        let output = stty_size();
75        assert!(output.status.success());
76        let stdout = String::from_utf8(output.stdout).expect("expected utf8");
77        let mut data = stdout.split_whitespace();
78        let rs = data
79            .next()
80            .expect("expected row")
81            .parse::<u16>()
82            .expect("expected u16 col");
83        let cs = data
84            .next()
85            .expect("expected col")
86            .parse::<u16>()
87            .expect("expected u16 col");
88        if let Some(Size { rows, cols }) = get() {
89            assert_eq!(rows, rs);
90            assert_eq!(cols, cs);
91        }
92    }
93}