From a0751f21b0b39368daf7beded5194ebbcbfe5329 Mon Sep 17 00:00:00 2001 From: Esption Date: Mon, 3 Oct 2016 00:15:12 -0500 Subject: [PATCH 1/6] Add Debug to color structs and Clone to Rgb / Fg / Bg (#51) --- src/color.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/color.rs b/src/color.rs index 5361f0f..861b233 100644 --- a/src/color.rs +++ b/src/color.rs @@ -27,7 +27,7 @@ pub trait Color { macro_rules! derive_color { ($doc:expr, $name:ident, $value:expr) => { #[doc = $doc] - #[derive(Copy, Clone)] + #[derive(Copy, Clone, Debug)] pub struct $name; impl Color for $name { @@ -62,7 +62,7 @@ derive_color!("High-intensity light cyan.", LightCyan, "14"); derive_color!("High-intensity light white.", LightWhite, "15"); /// An arbitrary ANSI color value. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct AnsiValue(pub u8); impl AnsiValue { @@ -100,6 +100,7 @@ impl Color for AnsiValue { } /// A truecolor RGB. +#[derive(Debug, Clone)] pub struct Rgb(pub u8, pub u8, pub u8); impl Color for Rgb { @@ -115,6 +116,7 @@ impl Color for Rgb { } /// Reset colors to defaults. +#[derive(Debug)] pub struct Reset; impl Color for Reset { @@ -130,6 +132,7 @@ impl Color for Reset { } /// A foreground color. +#[derive(Debug, Clone)] pub struct Fg(pub C); impl fmt::Display for Fg { @@ -139,6 +142,7 @@ impl fmt::Display for Fg { } /// A background color. +#[derive(Debug, Clone)] pub struct Bg(pub C); impl fmt::Display for Bg { From 0b24a3446d6500e08801a948d1b08e9101d89d92 Mon Sep 17 00:00:00 2001 From: j-browne Date: Fri, 7 Oct 2016 01:43:10 -0400 Subject: [PATCH 2/6] Change is_tty example to conform to new signature (#52) Commit 0d1025c532b06c59898908e3def509f63eaa3257 changed the signature of is_tty, but the example was not updated. --- examples/is_tty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/is_tty.rs b/examples/is_tty.rs index 7b64df6..52d1bc1 100644 --- a/examples/is_tty.rs +++ b/examples/is_tty.rs @@ -3,7 +3,7 @@ extern crate termion; use std::fs; fn main() { - if termion::is_tty(fs::File::create("/dev/stdout").unwrap()) { + if termion::is_tty(&fs::File::create("/dev/stdout").unwrap()) { println!("This is a TTY!"); } else { println!("This is not a TTY :("); From 4358ed6c48577e6e6daf8b006a7fac0cfa3dd6b9 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Sat, 8 Oct 2016 22:13:41 -0700 Subject: [PATCH 3/6] Derive Copy for color::{Rgb, Fg, Bg, Reset} (#54) --- src/color.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/color.rs b/src/color.rs index 861b233..b0ae819 100644 --- a/src/color.rs +++ b/src/color.rs @@ -100,7 +100,7 @@ impl Color for AnsiValue { } /// A truecolor RGB. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Rgb(pub u8, pub u8, pub u8); impl Color for Rgb { @@ -116,7 +116,7 @@ impl Color for Rgb { } /// Reset colors to defaults. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Reset; impl Color for Reset { @@ -132,7 +132,7 @@ impl Color for Reset { } /// A foreground color. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Fg(pub C); impl fmt::Display for Fg { @@ -142,7 +142,7 @@ impl fmt::Display for Fg { } /// A background color. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Bg(pub C); impl fmt::Display for Bg { From 5085815f586c26f1d19a6c6218306282e093c150 Mon Sep 17 00:00:00 2001 From: David Irvine Date: Thu, 20 Oct 2016 13:28:34 +0100 Subject: [PATCH 4/6] bug/lib.rs Allow musl builds (#58) --- src/size.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/size.rs b/src/size.rs index 926f32f..2f17719 100644 --- a/src/size.rs +++ b/src/size.rs @@ -15,17 +15,25 @@ struct TermSize { // Since attributes on non-item statements is not stable yet, we use a function. #[cfg(not(target_os = "redox"))] #[cfg(target_pointer_width = "64")] +#[cfg(not(target_env = "musl"))] fn tiocgwinsz() -> u64 { use termios::TIOCGWINSZ; TIOCGWINSZ as u64 } #[cfg(not(target_os = "redox"))] #[cfg(target_pointer_width = "32")] +#[cfg(not(target_env = "musl"))] fn tiocgwinsz() -> u32 { use termios::TIOCGWINSZ; TIOCGWINSZ as u32 } +#[cfg(target_env = "musl")] +fn tiocgwinsz() -> i32 { + use termios::TIOCGWINSZ; + TIOCGWINSZ as i32 +} + /// Get the size of the terminal. #[cfg(not(target_os = "redox"))] @@ -50,8 +58,12 @@ pub fn terminal_size() -> io::Result<(u16, u16)> { pub fn terminal_size() -> io::Result<(u16, u16)> { use std::env; - let width = try!(env::var("COLUMNS").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x))).parse().unwrap_or(0); - let height = try!(env::var("LINES").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x))).parse().unwrap_or(0); + let width = try!(env::var("COLUMNS").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x))) + .parse() + .unwrap_or(0); + let height = try!(env::var("LINES").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x))) + .parse() + .unwrap_or(0); Ok((width, height)) } From 654db645cbfb0d63684097f1127daea2d7c9b34a Mon Sep 17 00:00:00 2001 From: Jordan MacDonald Date: Sat, 22 Oct 2016 02:40:06 -0400 Subject: [PATCH 5/6] Derive PartialEq for Rgb type (#56) --- src/color.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/color.rs b/src/color.rs index b0ae819..e154ae2 100644 --- a/src/color.rs +++ b/src/color.rs @@ -100,7 +100,7 @@ impl Color for AnsiValue { } /// A truecolor RGB. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Rgb(pub u8, pub u8, pub u8); impl Color for Rgb { From ea06c6fd5672bae7918c7c9925410b2c143b03f0 Mon Sep 17 00:00:00 2001 From: Matthew Nicholson Date: Wed, 26 Oct 2016 05:53:36 -0400 Subject: [PATCH 6/6] Modify Keys and Events to detect Esc key presses (#45) * modify Keys and Events to detect Esc key presses The strategy used here is to read two bytes at a time, going on the assumption that escape sequences will consist of multi byte reads and solitary Esc key presses will consist of single byte reads. Tests had to be modified to account for these new multi byte reads by including dummy bytes when a single byte was previously expected. Fixes ticki/termion#43 * expand keys example to include Esc key presses * add test for Esc key press --- examples/keys.rs | 1 + src/event.rs | 2 ++ src/input.rs | 68 +++++++++++++++++++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/examples/keys.rs b/examples/keys.rs index a67f931..1b44bc7 100644 --- a/examples/keys.rs +++ b/examples/keys.rs @@ -23,6 +23,7 @@ fn main() { Key::Char(c) => println!("{}", c), Key::Alt(c) => println!("^{}", c), Key::Ctrl(c) => println!("*{}", c), + Key::Esc => println!("ESC"), Key::Left => println!("←"), Key::Right => println!("→"), Key::Up => println!("↑"), diff --git a/src/event.rs b/src/event.rs index 5760228..7d0a970 100644 --- a/src/event.rs +++ b/src/event.rs @@ -90,6 +90,8 @@ pub enum Key { Ctrl(char), /// Null byte. Null, + /// Esc key. + Esc, #[allow(missing_docs)] #[doc(hidden)] diff --git a/src/input.rs b/src/input.rs index 6d57cdb..26abd3e 100644 --- a/src/input.rs +++ b/src/input.rs @@ -7,11 +7,11 @@ use event::{parse_event, Event, Key}; use raw::IntoRawMode; /// An iterator over input keys. -pub struct Keys { - iter: Events, +pub struct Keys { + iter: Events, } -impl>> Iterator for Keys { +impl Iterator for Keys { type Item = Result; fn next(&mut self) -> Option> { @@ -27,29 +27,59 @@ impl>> Iterator for Keys { } /// An iterator over input events. -pub struct Events { - bytes: I, +pub struct Events { + source: R, + leftover: Option, } -impl>> Iterator for Events { +impl Iterator for Events { type Item = Result; fn next(&mut self) -> Option> { - let iter = &mut self.bytes; - match iter.next() { - Some(item) => Some(parse_event(item, iter).or(Ok(Event::Unsupported))), - None => None, + let mut source = &mut self.source; + + if let Some(c) = self.leftover { + // we have a leftover byte, use it + self.leftover = None; + return Some(parse_event(Ok(c), &mut source.bytes()).or(Ok(Event::Unsupported))); } + + // Here we read two bytes at a time. We need to distinguish between single ESC key presses, + // and escape sequences (which start with ESC or a x1B byte). The idea is that if this is + // an escape sequence, we will read multiple bytes (the first byte being ESC) but if this + // is a single ESC keypress, we will only read a single byte. + let mut buf = [0u8; 2]; + let res = match source.read(&mut buf) { + Ok(0) => return None, + Ok(1) => match buf[0] { + b'\x1B' => Ok(Event::Key(Key::Esc)), + c => parse_event(Ok(c), &mut source.bytes()), + }, + Ok(2) => { + if buf[0] != b'\x1B' { + // this is not an escape sequence, but we read two bytes, save the second byte + // for later + self.leftover = Some(buf[1]); + } + + let mut iter = buf[1..2].iter().map(|c| Ok(*c)).chain(source.bytes()); + parse_event(Ok(buf[0]), &mut iter) + } + Ok(_) => unreachable!(), + Err(e) => Err(e), + }; + + Some(res.or(Ok(Event::Unsupported))) } } /// Extension to `Read` trait. pub trait TermRead { /// An iterator over input events. - fn events(self) -> Events> where Self: Sized; + fn events(self) -> Events where Self: Sized; /// An iterator over key inputs. - fn keys(self) -> Keys> where Self: Sized; + fn keys(self) -> Keys where Self: Sized; /// Read a line. /// @@ -68,12 +98,13 @@ pub trait TermRead { } impl TermRead for R { - fn events(self) -> Events> { + fn events(self) -> Events { Events { - bytes: self.bytes(), + source: self, + leftover: None, } } - fn keys(self) -> Keys> { + fn keys(self) -> Keys { Keys { iter: self.events(), } @@ -213,6 +244,13 @@ mod test { assert!(st.next().is_none()); } + #[test] + fn test_esc_key() { + let mut st = b"\x1B".keys(); + assert_eq!(st.next().unwrap().unwrap(), Key::Esc); + assert!(st.next().is_none()); + } + fn line_match(a: &str, b: Option<&str>) { let mut sink = io::sink();