diff --git a/examples/is_tty.rs b/examples/is_tty.rs index 628d9a73..85770e23 100644 --- a/examples/is_tty.rs +++ b/examples/is_tty.rs @@ -6,9 +6,9 @@ use crossterm::{ use std::io::{stdin, stdout}; pub fn main() { - println!("{:?}", size().unwrap()); + println!("size: {:?}", size().unwrap()); execute!(stdout(), SetSize(10, 10)).unwrap(); - println!("{:?}", size().unwrap()); + println!("resized: {:?}", size().unwrap()); if stdin().is_tty() { println!("Is TTY"); diff --git a/src/terminal.rs b/src/terminal.rs index 127244dc..49f413b1 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -137,6 +137,23 @@ pub fn size() -> io::Result<(u16, u16)> { sys::size() } +#[derive(Debug)] +pub struct WindowSize { + pub rows: u16, + pub columns: u16, + pub width: u16, + pub height: u16, +} + +/// Returns the terminal size `[WindowSize]`. +/// +/// The width and height in pixels may not be reliably implemented or default to 0. +/// For unix, https://man7.org/linux/man-pages/man4/tty_ioctl.4.html documents them as "unused". +/// For windows it is not implemented. +pub fn window_size() -> io::Result { + sys::window_size() +} + /// Disables line wrapping. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct DisableLineWrap; diff --git a/src/terminal/sys.rs b/src/terminal/sys.rs index 527e194c..47e80a52 100644 --- a/src/terminal/sys.rs +++ b/src/terminal/sys.rs @@ -4,14 +4,16 @@ #[cfg(feature = "events")] pub use self::unix::supports_keyboard_enhancement; #[cfg(unix)] -pub(crate) use self::unix::{disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, size}; +pub(crate) use self::unix::{ + disable_raw_mode, enable_raw_mode, window_size, is_raw_mode_enabled, size, +}; #[cfg(windows)] #[cfg(feature = "events")] pub use self::windows::supports_keyboard_enhancement; #[cfg(windows)] pub(crate) use self::windows::{ - clear, disable_raw_mode, enable_raw_mode, is_raw_mode_enabled, scroll_down, scroll_up, - set_size, set_window_title, size, + clear, disable_raw_mode, enable_raw_mode, window_size, is_raw_mode_enabled, scroll_down, + scroll_up, set_size, set_window_title, size, }; #[cfg(windows)] diff --git a/src/terminal/sys/unix.rs b/src/terminal/sys/unix.rs index 98d784b3..ed545c5b 100644 --- a/src/terminal/sys/unix.rs +++ b/src/terminal/sys/unix.rs @@ -1,6 +1,9 @@ //! UNIX related logic for terminal manipulation. -use crate::terminal::sys::file_descriptor::{tty_fd, FileDesc}; +use crate::terminal::{ + sys::file_descriptor::{tty_fd, FileDesc}, + WindowSize, +}; use libc::{ cfmakeraw, ioctl, tcgetattr, tcsetattr, termios as Termios, winsize, STDOUT_FILENO, TCSANOW, TIOCGWINSZ, @@ -20,8 +23,19 @@ pub(crate) fn is_raw_mode_enabled() -> bool { TERMINAL_MODE_PRIOR_RAW_MODE.lock().is_some() } +impl From for WindowSize { + fn from(size: winsize) -> WindowSize { + WindowSize { + columns: size.ws_col, + rows: size.ws_row, + width: size.ws_xpixel, + height: size.ws_ypixel, + } + } +} + #[allow(clippy::useless_conversion)] -pub(crate) fn size() -> io::Result<(u16, u16)> { +pub(crate) fn window_size() -> io::Result { // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc let mut size = winsize { ws_row: 0, @@ -38,11 +52,17 @@ pub(crate) fn size() -> io::Result<(u16, u16)> { STDOUT_FILENO }; - if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() - && size.ws_col != 0 - && size.ws_row != 0 - { - return Ok((size.ws_col, size.ws_row)); + if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() { + return Ok(size.into()); + } + + Err(std::io::Error::last_os_error().into()) +} + +#[allow(clippy::useless_conversion)] +pub(crate) fn size() -> io::Result<(u16, u16)> { + if let Ok(window_size) = window_size() { + return Ok((window_size.columns, window_size.rows)); } tput_size().ok_or_else(|| std::io::Error::last_os_error().into()) diff --git a/src/terminal/sys/windows.rs b/src/terminal/sys/windows.rs index ec671e74..7ce92571 100644 --- a/src/terminal/sys/windows.rs +++ b/src/terminal/sys/windows.rs @@ -9,7 +9,10 @@ use winapi::{ um::wincon::{SetConsoleTitleW, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ENABLE_PROCESSED_INPUT}, }; -use crate::{cursor, terminal::ClearType}; +use crate::{ + cursor, + terminal::{ClearType, WindowSize}, +}; /// bits which can't be set in raw mode const NOT_RAW_MODE_MASK: DWORD = ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT; @@ -58,6 +61,13 @@ pub(crate) fn size() -> io::Result<(u16, u16)> { )) } +pub(crate) fn window_size() -> io::Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Window pixel size not implemented for the Windows API.", + )) +} + /// Queries the terminal's support for progressive keyboard enhancement. /// /// This always returns `Ok(false)` on Windows.