1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 //! Unix implementation of `tutil::screen`, tested on Linux, FreeBSD and macOS.
7 use super::{Width, Height};
9 use std::os::raw::c_ushort;
10 use libc::{ioctl, isatty, STDOUT_FILENO, TIOCGWINSZ};
12 /// The struct required by the `TIOCGWINSZ` syscall; specified in the following
13 /// [man page](http://www.delorie.com/djgpp/doc/libc/libc_495.html).
16 /// Rows, in characters.
18 /// Columns, in characters.
20 /// Horizontal size, in pixels.
22 /// Vertical size, in pixels.
26 /// Returns the terminal screen size (in columns and rows).
28 /// Returns `None` if the screen size is `(0, 0)` or is not able to be
30 pub fn size() -> Option<(Width, Height)> {
31 let is_tty = unsafe { isatty(STDOUT_FILENO) == 1 };
33 if !is_tty { return None; }
35 let mut winsize = WinSize { ws_row: 0, ws_col: 0, ws_xpixel: 0, ws_ypixel: 0 };
37 let success: bool = unsafe {
38 ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut winsize) != 0
42 Some((Width(winsize.ws_col), Height(winsize.ws_row)))
48 /// Returns the terminal screen width (in columns).
50 /// Returns `None` if the terminal width is detected as being <= 0 columns or is
51 /// not able to be determined at all.
52 pub fn width() -> Option<Width> {
55 if let Some((Width(width), Height(_))) = size {
62 /// Returns the terminal screen height (in rows).
64 /// Returns `None` if the terminal height is detected as being <= 0 rows or is
65 /// not able to be determined at all.
66 pub fn height() -> Option<Height> {
69 if let Some((Width(_), Height(height))) = size {
79 use super::super::{Width, Height};
81 use std::process::{Command, Stdio};
83 #[cfg(target_os = "linux")]
84 fn create_command() -> Command {
85 let mut cmd = Command::new("stty");
87 cmd.arg("/dev/stderr");
89 cmd.stderr(Stdio::inherit());
93 #[cfg(any(target_os = "freebsd", target_os = "macos"))]
94 fn create_command() -> Command {
95 let mut cmd = Command::new("stty");
97 cmd.arg("/dev/stderr");
99 cmd.stderr(Stdio::inherit());
105 let output = create_command().output().unwrap();
106 let stdout = String::from_utf8(output.stdout).unwrap();
107 assert!(output.status.success());
109 let cols = u16::from_str_radix(stdout.split_whitespace().last().unwrap(), 10).unwrap();
110 let rows = u16::from_str_radix(stdout.split_whitespace().next().unwrap(), 10).unwrap();
112 if let Some((Width(width), Height(height))) = size() {
113 assert_eq!(width, cols);
114 assert_eq!(height, rows);
120 let output = create_command().output().unwrap();
121 let stdout = String::from_utf8(output.stdout).unwrap();
122 assert!(output.status.success());
124 let cols = u16::from_str_radix(stdout.split_whitespace().last().unwrap(), 10).unwrap();
126 if let Some((Width(width), Height(_))) = size() {
127 assert_eq!(width, cols);
132 fn correct_height() {
133 let output = create_command().output().unwrap();
134 let stdout = String::from_utf8(output.stdout).unwrap();
135 assert!(output.status.success());
137 let rows = u16::from_str_radix(stdout.split_whitespace().next().unwrap(), 10).unwrap();
139 if let Some((Width(_), Height(height))) = size() {
140 assert_eq!(height, rows);