diff --git a/examples/is_tty.rs b/examples/is_tty.rs index dcf56f0a..bebacdf0 100644 --- a/examples/is_tty.rs +++ b/examples/is_tty.rs @@ -1,6 +1,6 @@ use crossterm::{ execute, - terminal::{font_size, size, SetSize}, + terminal::{size, window_size, SetSize}, tty::IsTty, }; use std::io::{stdin, stdout}; @@ -10,7 +10,7 @@ pub fn main() { execute!(stdout(), SetSize(10, 10)).unwrap(); println!("resized: {:?}", size().unwrap()); - println!("font_size: {:?}", font_size().unwrap()); + println!("pixel size: {:?}", window_size().unwrap()); if stdin().is_tty() { println!("Is TTY"); diff --git a/src/terminal.rs b/src/terminal.rs index 83fd48f0..a7a65bcb 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -137,9 +137,20 @@ pub fn size() -> io::Result<(u16, u16)> { sys::size() } -/// Returns the terminal font size in pixels. -pub fn font_size() -> io::Result<(u16, u16)> { - sys::font_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 and default to 0, as +/// https://man7.org/linux/man-pages/man4/tty_ioctl.4.html documents them as "unused". +pub fn window_size() -> io::Result { + sys::window_size() } /// Disables line wrapping. diff --git a/src/terminal/sys.rs b/src/terminal/sys.rs index 9741a9ca..8dcc7db5 100644 --- a/src/terminal/sys.rs +++ b/src/terminal/sys.rs @@ -5,7 +5,7 @@ pub use self::unix::supports_keyboard_enhancement; #[cfg(unix)] pub(crate) use self::unix::{ - disable_raw_mode, enable_raw_mode, font_size, is_raw_mode_enabled, size, + disable_raw_mode, enable_raw_mode, window_size, is_raw_mode_enabled, size, }; #[cfg(windows)] #[cfg(feature = "events")] diff --git a/src/terminal/sys/unix.rs b/src/terminal/sys/unix.rs index 582e27fc..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,36 +23,19 @@ pub(crate) fn is_raw_mode_enabled() -> bool { TERMINAL_MODE_PRIOR_RAW_MODE.lock().is_some() } -#[allow(clippy::useless_conversion)] -pub(crate) fn size() -> io::Result<(u16, u16)> { - // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc - let mut size = winsize { - ws_row: 0, - ws_col: 0, - ws_xpixel: 0, - ws_ypixel: 0, - }; - - let file = File::open("/dev/tty").map(|file| (FileDesc::new(file.into_raw_fd(), true))); - let fd = if let Ok(file) = &file { - file.raw_fd() - } else { - // Fallback to libc::STDOUT_FILENO if /dev/tty is missing - 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)); +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, + } } - - tput_size().ok_or_else(|| std::io::Error::last_os_error().into()) } #[allow(clippy::useless_conversion)] -pub(crate) fn font_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, @@ -67,12 +53,21 @@ pub(crate) fn font_size() -> io::Result<(u16, u16)> { }; if wrap_with_result(unsafe { ioctl(fd, TIOCGWINSZ.into(), &mut size) }).is_ok() { - return Ok((size.ws_xpixel / size.ws_col, size.ws_ypixel / size.ws_row)); + 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()) +} + pub(crate) fn enable_raw_mode() -> io::Result<()> { let mut original_mode = TERMINAL_MODE_PRIOR_RAW_MODE.lock(); diff --git a/src/terminal/sys/windows.rs b/src/terminal/sys/windows.rs index 2a27b6da..5d0c2eed 100644 --- a/src/terminal/sys/windows.rs +++ b/src/terminal/sys/windows.rs @@ -58,9 +58,16 @@ pub(crate) fn size() -> io::Result<(u16, u16)> { )) } -pub(crate) fn font_size() -> io::Result<(u16, u16)> { - let size = ScreenBuffer::current()?.font_info()?.size(); - Ok((size.width as u16, size.height as u16)) +pub(crate) fn window_size() -> io::Result { + let (columns, rows) = size()?; + let font_size = ScreenBuffer::current()?.font_info()?.size(); + Ok(WindowSize { + columns, + rows, + // `(columns,rows)` start at index 1. + width: (columns - 1) * font_size.0, + height: (rows - 1) * font_size.1, + }) } /// Queries the terminal's support for progressive keyboard enhancement.