1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#![warn(missing_docs)]
//! This crate provides a single function, `termsize()`,
//! which returns the size of the terminal in columns and rows.
//! # Usage
//! ```rust
//! extern crate termsize;
//! use termsize::termsize;
//!
//! fn main() {
//!     match termsize() {
//!         Some((columns, rows)) => println!("Size of terminal is {} x {}", columns, rows),
//!         None => println!("Not called from a terminal")
//!     }
//! }
//! ```

/// Returns the size of the terminal as a tuple `Option<(width, height)>`.
///
/// Width: Number of columns.
///
/// Height: Number of rows.
pub fn termsize() -> Option<(usize, usize)> {
    target::termsize()
}

#[cfg(target_os = "linux")]
mod target {
    extern crate libc;
    use self::libc::{c_int, c_ushort, STDOUT_FILENO};
    use self::libc::funcs::bsd44::ioctl;

    extern {
        static tiocgwinsz: c_int;
    }

    #[repr(C)]
    #[derive(Default)]
    struct WinSize {
        ws_row: c_ushort,
        ws_col: c_ushort,
        ws_xpixel: c_ushort, /* unused */
        ws_ypixel: c_ushort, /* unused */
    }

    pub fn termsize() -> Option<(usize, usize)> {
        let ws = &mut WinSize::default();
        unsafe {
            if ioctl(STDOUT_FILENO, tiocgwinsz, ws as *mut WinSize) == 0 {
                Some((ws.ws_col as usize, ws.ws_row as usize))
            } else {
                None
            }
        }
    }
}

#[cfg(target_os = "windows")]
mod target {
    extern crate winapi;
    extern crate kernel32;

    use std::mem;
    use self::winapi::{wincon, winbase};

    // TODO: this method works for cmd and powershell,
    // but returns (0, 0), if you use i.e. bash shell on windows.
    // Possible fix: implement a fallback method which utilizes tput, resize or stty.
    pub fn termsize() -> Option<(usize, usize)>{
        let csbi = unsafe {
            let mut csbi: wincon::CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
            let std_handle = kernel32::GetStdHandle(winbase::STD_OUTPUT_HANDLE);
            if kernel32::GetConsoleScreenBufferInfo(std_handle, &mut csbi as wincon::PCONSOLE_SCREEN_BUFFER_INFO) == 0 {
                return None
            }
            csbi
        };

        let columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
        let rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
        Some((columns as usize, rows as usize))
    }
}