From 1df3643331f71656b554cb9f095f83190f902309 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Sun, 24 Sep 2017 17:20:25 +0100 Subject: [PATCH 1/4] Add unix win_size Also use impl Trait --- Cargo.toml | 3 ++ examples/kitchen_sink.rs | 13 ++++++ src/lib.rs | 22 +++++++++- src/terminfo/mod.rs | 91 ++++++++++++++++++++++++++++++++++++++++ src/unix.rs | 30 +++++++++++++ src/win.rs | 5 +++ tests/winsize.rs | 10 +++++ 7 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 examples/kitchen_sink.rs create mode 100644 src/unix.rs create mode 100644 tests/winsize.rs diff --git a/Cargo.toml b/Cargo.toml index 240e5878..0b194643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,8 @@ appveyor = { repository = "Stebalien/term" } winapi = "0.2" kernel32-sys = "0.2" +[target.'cfg(unix)'.dependencies] +libc = "0.2" + [features] default=[] diff --git a/examples/kitchen_sink.rs b/examples/kitchen_sink.rs new file mode 100644 index 00000000..3b87d2e9 --- /dev/null +++ b/examples/kitchen_sink.rs @@ -0,0 +1,13 @@ +// An example of the avaiable functionality in term + +extern crate term; + +fn main() { + let mut t = term::stdout().unwrap(); + + print!("Dims: "); + t.fg(term::color::GREEN).unwrap(); + print!("{:?}", t.dims().unwrap()); + t.reset().unwrap(); + println!(); +} diff --git a/src/lib.rs b/src/lib.rs index a5824d4b..404abf9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,7 +29,8 @@ //! //! and this to your crate root: //! -//! ```rust +//! ``` +//! # #[allow(unused_extern_crates)] //! extern crate term; //! ``` //! @@ -75,6 +76,8 @@ pub mod terminfo; #[cfg(windows)] mod win; +#[cfg(unix)] +mod unix; /// Alias for stdout terminals. pub type StdoutTerminal = Terminal + Send; @@ -168,6 +171,20 @@ pub enum Attr { BackgroundColor(color::Color), } +/// A struct representing the number of columns and rows of the terminal, and, if available, the +/// width and height of the terminal in pixels +#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)] +pub struct Dims { + /// The number of rows in the terminal + pub rows: u16, + /// The number of columns in the terminal + pub columns: u16, + /// If available, the pixel width of the terminal + pub pixel_width: Option, + /// If available, the pixel height of the terminal + pub pixel_height: Option, +} + /// An error arising from interacting with the terminal. #[derive(Debug)] pub enum Error { @@ -390,6 +407,9 @@ pub trait Terminal: Write { /// Returns `Ok(true)` if the deletion code was printed, or `Err(e)` if there was an error. fn carriage_return(&mut self) -> Result<()>; + /// Gets the size of the terminal window, if available + fn dims(&self) -> Result; + /// Gets an immutable reference to the stream inside fn get_ref(&self) -> &Self::Output; diff --git a/src/terminfo/mod.rs b/src/terminfo/mod.rs index 71cdd644..4bcf2085 100644 --- a/src/terminfo/mod.rs +++ b/src/terminfo/mod.rs @@ -19,6 +19,7 @@ use std::io::BufReader; use std::path::Path; use Attr; +use Dims; use color; use Terminal; use Result; @@ -27,6 +28,11 @@ use self::parser::compiled::parse; use self::parm::{expand, Variables, Param}; use self::Error::*; +#[cfg(unix)] +use std::os::unix::io::AsRawFd; +#[cfg(unix)] +use unix::win_size; + /// Returns true if the named terminal supports basic ANSI escape codes. fn is_ansi(name: &str) -> bool { @@ -283,6 +289,87 @@ pub struct TerminfoTerminal { ti: TermInfo, } +#[cfg(unix)] +impl Terminal for TerminfoTerminal { + type Output = T; + fn fg(&mut self, color: color::Color) -> Result<()> { + let color = self.dim_if_necessary(color); + if self.num_colors > color { + return self.ti.apply_cap("setaf", &[Param::Number(color as i32)], &mut self.out); + } + Err(::Error::ColorOutOfRange) + } + + fn bg(&mut self, color: color::Color) -> Result<()> { + let color = self.dim_if_necessary(color); + if self.num_colors > color { + return self.ti.apply_cap("setab", &[Param::Number(color as i32)], &mut self.out); + } + Err(::Error::ColorOutOfRange) + } + + fn attr(&mut self, attr: Attr) -> Result<()> { + match attr { + Attr::ForegroundColor(c) => self.fg(c), + Attr::BackgroundColor(c) => self.bg(c), + _ => self.ti.apply_cap(cap_for_attr(attr), &[], &mut self.out), + } + } + + fn supports_attr(&self, attr: Attr) -> bool { + match attr { + Attr::ForegroundColor(_) | Attr::BackgroundColor(_) => self.num_colors > 0, + _ => { + let cap = cap_for_attr(attr); + self.ti.strings.get(cap).is_some() + } + } + } + + fn reset(&mut self) -> Result<()> { + self.ti.reset(&mut self.out) + } + + fn supports_reset(&self) -> bool { + ["sgr0", "sgr", "op"].iter().any(|&cap| self.ti.strings.get(cap).is_some()) + } + + fn supports_color(&self) -> bool { + self.num_colors > 0 && self.supports_reset() + } + + fn cursor_up(&mut self) -> Result<()> { + self.ti.apply_cap("cuu1", &[], &mut self.out) + } + + fn delete_line(&mut self) -> Result<()> { + self.ti.apply_cap("el", &[], &mut self.out) + } + + fn carriage_return(&mut self) -> Result<()> { + self.ti.apply_cap("cr", &[], &mut self.out) + } + + fn dims(&self) -> Result { + win_size(self.out.as_raw_fd()).map(|s| s.into()).ok_or(::Error::NotSupported) + } + + fn get_ref(&self) -> &T { + &self.out + } + + fn get_mut(&mut self) -> &mut T { + &mut self.out + } + + fn into_inner(self) -> T + where Self: Sized + { + self.out + } +} + +#[cfg(windows)] impl Terminal for TerminfoTerminal { type Output = T; fn fg(&mut self, color: color::Color) -> Result<()> { @@ -343,6 +430,10 @@ impl Terminal for TerminfoTerminal { self.ti.apply_cap("cr", &[], &mut self.out) } + fn dims(&self) -> Result { + Err(::Error::NotSupported) + } + fn get_ref(&self) -> &T { &self.out } diff --git a/src/unix.rs b/src/unix.rs new file mode 100644 index 00000000..1c072bf1 --- /dev/null +++ b/src/unix.rs @@ -0,0 +1,30 @@ +extern crate libc; + +use std::os::unix::io::RawFd; +use std::mem; +use Dims; + +/// Get the window size from a file descriptor (0 for stdout) +/// +/// Option is returned as there is no distinction between errors (all ENOSYS) +pub fn win_size(fd: RawFd) -> Option { + unsafe { + let ws: libc::winsize = mem::uninitialized(); + if libc::ioctl(fd, libc::TIOCGWINSZ, &ws) == 0 { + Some(ws) + } else { + None + } + } +} + +impl From for Dims { + fn from(sz: libc::winsize) -> Dims { + Dims { + rows: sz.ws_row as u16, + columns: sz.ws_col as u16, + pixel_width: Some(sz.ws_xpixel as u32), + pixel_height: Some(sz.ws_ypixel as u32), + } + } +} diff --git a/src/win.rs b/src/win.rs index 3d571d50..e200a469 100644 --- a/src/win.rs +++ b/src/win.rs @@ -24,6 +24,7 @@ use Error; use Result; use Terminal; use color; +use Dims; /// A Terminal implementation which uses the Win32 Console API. pub struct WinConsole { @@ -285,6 +286,10 @@ impl Terminal for WinConsole { } } + fn dims(&self) -> Result { + Err(::Error::NotSupported) + } + fn get_ref<'a>(&'a self) -> &'a T { &self.buf } diff --git a/tests/winsize.rs b/tests/winsize.rs new file mode 100644 index 00000000..6846a82e --- /dev/null +++ b/tests/winsize.rs @@ -0,0 +1,10 @@ +extern crate term; + +#[cfg(unix)] +#[test] +fn test_winsize() { + let t = term::stdout().unwrap(); + // This makes sure we don't try to provide dims on an incorrect platform, it also may trigger + // any memory errors. + let _dims = t.dims().unwrap(); +} From 5c1b9cf80694b04783b186d362cb0b7a6170d86f Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 13 Oct 2017 21:49:46 +0100 Subject: [PATCH 2/4] Add size support for WinConsole I can't get it to work for TerminfoTerm (mintty) in windows - it reports incorrect information. --- src/lib.rs | 2 +- src/win.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 404abf9d..4db18dfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,7 +173,7 @@ pub enum Attr { /// A struct representing the number of columns and rows of the terminal, and, if available, the /// width and height of the terminal in pixels -#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Hash, Eq, Copy, Clone, Default)] pub struct Dims { /// The number of rows in the terminal pub rows: u16, diff --git a/src/win.rs b/src/win.rs index e200a469..b30b417c 100644 --- a/src/win.rs +++ b/src/win.rs @@ -287,7 +287,19 @@ impl Terminal for WinConsole { } fn dims(&self) -> Result { - Err(::Error::NotSupported) + let handle = try!(conout()); + unsafe { + let mut buffer_info = ::std::mem::uninitialized(); + if kernel32::GetConsoleScreenBufferInfo(handle, &mut buffer_info) != 0 { + Ok(Dims { + rows: (buffer_info.srWindow.Bottom - buffer_info.srWindow.Top + 1) as u16, + columns: (buffer_info.srWindow.Right - buffer_info.srWindow.Left + 1) as u16, + ..Default::default() + }) + } else { + Err(io::Error::last_os_error().into()) + } + } } fn get_ref<'a>(&'a self) -> &'a T { From 33176da89da927b5c3ab7225f1beba8075176347 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 13 Oct 2017 21:57:59 +0100 Subject: [PATCH 3/4] Use last_os_err like rest of code. --- src/terminfo/mod.rs | 2 +- src/unix.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/terminfo/mod.rs b/src/terminfo/mod.rs index 4bcf2085..e50cb8a2 100644 --- a/src/terminfo/mod.rs +++ b/src/terminfo/mod.rs @@ -351,7 +351,7 @@ impl Terminal for TerminfoTerminal { } fn dims(&self) -> Result { - win_size(self.out.as_raw_fd()).map(|s| s.into()).ok_or(::Error::NotSupported) + win_size(self.out.as_raw_fd()).map(|s| s.into()) } fn get_ref(&self) -> &T { diff --git a/src/unix.rs b/src/unix.rs index 1c072bf1..5dc25cf2 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -3,17 +3,18 @@ extern crate libc; use std::os::unix::io::RawFd; use std::mem; use Dims; +use Result; /// Get the window size from a file descriptor (0 for stdout) /// /// Option is returned as there is no distinction between errors (all ENOSYS) -pub fn win_size(fd: RawFd) -> Option { +pub fn win_size(fd: RawFd) -> Result { unsafe { let ws: libc::winsize = mem::uninitialized(); if libc::ioctl(fd, libc::TIOCGWINSZ, &ws) == 0 { - Some(ws) + Ok(ws) } else { - None + Err(io::Error::last_os_error().into()) } } } From 1db0a1e7e3d48f43c7838e0710c2e7986f893aa5 Mon Sep 17 00:00:00 2001 From: Richard Dodd Date: Fri, 13 Oct 2017 21:59:52 +0100 Subject: [PATCH 4/4] Fix last commit --- src/unix.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/unix.rs b/src/unix.rs index 5dc25cf2..2bf2acd0 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -1,6 +1,7 @@ extern crate libc; use std::os::unix::io::RawFd; +use std::io; use std::mem; use Dims; use Result;