diff --git a/CHANGELOG.md b/CHANGELOG.md index 36a9d79..6b9d42b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# Version 0.17.1 +- Reverted bug in 0.17.0: "Make terminal size function fallback to `STDOUT_FILENO` if `/dev/tty` is missing.". +- Support for querying whether the current instance is a TTY. + # Version 0.17 - Impl Display for MoveToColumn, MoveToNextLine, MoveToPreviousLine - Make unix event reader always use `/dev/tty`. diff --git a/README.md b/README.md index 8808fa2..0d9dad6 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ see [Tested Terminals](#tested-terminals) for more info). - Detailed documentation - Few dependencies - Full control over writing and flushing output buffer +- Is tty - Cursor - Move the cursor N times (up, down, left, right) - Move to previous / next line @@ -139,7 +140,7 @@ To optional feature flags. ```toml [dependencies.crossterm] -version = "0.14" +version = "0.17" features = ["event-stream"] ``` diff --git a/examples/README.md b/examples/README.md index c278384..4ae3323 100644 --- a/examples/README.md +++ b/examples/README.md @@ -16,7 +16,8 @@ The examples are compatible with the latest release. | :------- | :------- | :------- | | `examples/interactive-test` | interactive, walk trough, demo | cursor, style, event | `event-*`| event reading demos | (async) event reading -| `stderr` | crossterm over stderr demo, | raw mode, alternate screen, custom output +| `stderr` | crossterm over stderr demo | raw mode, alternate screen, custom output +| `is_tty` | Is this instance a tty ? | tty | ## Run examples diff --git a/examples/is_tty.rs b/examples/is_tty.rs new file mode 100644 index 0000000..108a134 --- /dev/null +++ b/examples/is_tty.rs @@ -0,0 +1,10 @@ +use crossterm::tty::IsTty; +use std::io::stdin; + +pub fn main() { + if stdin().is_tty() { + println!("Is TTY"); + } else { + println!("Is not TTY"); + } +} diff --git a/src/event/sys/unix/file_descriptor.rs b/src/event/sys/unix/file_descriptor.rs index 3e3e327..c805571 100644 --- a/src/event/sys/unix/file_descriptor.rs +++ b/src/event/sys/unix/file_descriptor.rs @@ -65,14 +65,18 @@ impl Drop for FileDesc { /// Creates a file descriptor pointing to the standard input or `/dev/tty`. pub fn tty_fd() -> Result { - let (fd, close_on_drop) = ( - fs::OpenOptions::new() - .read(true) - .write(true) - .open("/dev/tty")? - .into_raw_fd(), - true, - ); + let (fd, close_on_drop) = if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } { + (libc::STDIN_FILENO, false) + } else { + ( + fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/tty")? + .into_raw_fd(), + true, + ) + }; Ok(FileDesc::new(fd, close_on_drop)) } diff --git a/src/lib.rs b/src/lib.rs index 1fb2adc..e39e279 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -242,6 +242,9 @@ pub mod style; /// A module to work with the terminal. pub mod terminal; +/// A module to query if the current instance is a tty. +pub mod tty; + mod ansi; #[cfg(windows)] pub(crate) mod ansi_support; diff --git a/src/tty.rs b/src/tty.rs new file mode 100644 index 0000000..4bfabb2 --- /dev/null +++ b/src/tty.rs @@ -0,0 +1,38 @@ +//! Making it a little more convenient and safe to query whether +//! something is a terminal teletype or not. +//! This module defines the IsTty trait and the is_tty method to +//! return true if the item represents a terminal. + +#[cfg(unix)] +use std::os::unix::io::AsRawFd; +#[cfg(windows)] +use std::os::windows::io::AsRawHandle; +#[cfg(windows)] +use winapi::um::consoleapi::GetConsoleMode; + +/// Adds the `is_tty` method to types that might represent a terminal +pub trait IsTty { + /// Returns true when an instance is a terminal teletype, otherwise false. + fn is_tty(&self) -> bool; +} + +/// On unix, the `isatty()` function returns true if a file +/// descriptor is a terminal. +#[cfg(unix)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let fd = self.as_raw_fd(); + unsafe { libc::isatty(fd) == 1 } + } +} + +/// On windows, `GetConsoleMode` will return true if we are in a terminal. +/// Otherwise false. +#[cfg(windows)] +impl IsTty for S { + fn is_tty(&self) -> bool { + let mut mode = 0; + let ok = unsafe { GetConsoleMode(self.as_raw_handle() as *mut _, &mut mode) }; + ok == 1 + } +}