diff --git a/Cargo.toml b/Cargo.toml index 6e88e81f2aac5daf2753fad3a00c5c2bfdefcc9c..957bffdbadc2ab733a6f13b385fca060b6ce5c16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["tty", "color", "terminal", "password", "tui"] exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"] [dependencies] -numtoa = { version = "0.1.0", features = ["std"]} +numtoa = "0.2.3" [target.'cfg(not(target_os = "redox"))'.dependencies] libc = "0.2.8" diff --git a/examples/alternate_screen.rs b/examples/alternate_screen.rs index 91d28b1702a2812869b3b2f8e5ab0e8b296e7218..96257d89c1ae52ec330702a2dbb5fb07b850ab21 100644 --- a/examples/alternate_screen.rs +++ b/examples/alternate_screen.rs @@ -1,8 +1,8 @@ extern crate termion; +use std::io::{stdout, Write}; +use std::{thread, time}; use termion::screen::*; -use std::io::{Write, stdout}; -use std::{time, thread}; fn main() { { diff --git a/examples/alternate_screen_raw.rs b/examples/alternate_screen_raw.rs index 7ba7888804c7860832ee6c8bbe3cb80eb711dc15..a1b441199647b3a99d748912c1c271d339e23fca 100644 --- a/examples/alternate_screen_raw.rs +++ b/examples/alternate_screen_raw.rs @@ -1,10 +1,10 @@ extern crate termion; +use std::io::{stdin, stdout, Write}; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; use termion::screen::*; -use std::io::{Write, stdout, stdin}; fn write_alt_screen_msg(screen: &mut W) { write!(screen, "{}{}Welcome to the alternate screen.{}Press '1' to switch to the main screen or '2' to switch to the alternate screen.{}Press 'q' to exit (and switch back to the main screen).", diff --git a/examples/async.rs b/examples/async.rs index b16bb786181e8eb267214750c590312fdeac85cc..c1ea89d2055d6c65321e880ffe8435fb9db1cf7f 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -1,21 +1,23 @@ extern crate termion; -use termion::raw::IntoRawMode; -use termion::async_stdin; -use std::io::{Read, Write, stdout}; +use std::io::{stdout, Read, Write}; use std::thread; use std::time::Duration; +use termion::async_stdin; +use termion::raw::IntoRawMode; fn main() { let stdout = stdout(); let mut stdout = stdout.lock().into_raw_mode().unwrap(); let mut stdin = async_stdin().bytes(); - write!(stdout, - "{}{}", - termion::clear::All, - termion::cursor::Goto(1, 1)) - .unwrap(); + write!( + stdout, + "{}{}", + termion::clear::All, + termion::cursor::Goto(1, 1) + ) + .unwrap(); loop { write!(stdout, "{}", termion::clear::CurrentLine).unwrap(); diff --git a/examples/click.rs b/examples/click.rs index fe903d837f754cdc229c9414a258971429fc4d70..f7a04f741c603bbc2e7cb499d7f934677eb477a4 100644 --- a/examples/click.rs +++ b/examples/click.rs @@ -1,33 +1,33 @@ extern crate termion; -use termion::event::{Key, Event, MouseEvent}; -use termion::input::{TermRead, MouseTerminal}; +use std::io::{stdin, stdout, Write}; +use termion::event::{Event, Key, MouseEvent}; +use termion::input::{MouseTerminal, TermRead}; use termion::raw::IntoRawMode; -use std::io::{Write, stdout, stdin}; fn main() { let stdin = stdin(); let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap()); - write!(stdout, - "{}{}q to exit. Click, click, click!", - termion::clear::All, - termion::cursor::Goto(1, 1)) - .unwrap(); + write!( + stdout, + "{}{}q to exit. Click, click, click!", + termion::clear::All, + termion::cursor::Goto(1, 1) + ) + .unwrap(); stdout.flush().unwrap(); for c in stdin.events() { let evt = c.unwrap(); match evt { Event::Key(Key::Char('q')) => break, - Event::Mouse(me) => { - match me { - MouseEvent::Press(_, x, y) => { - write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap(); - } - _ => (), + Event::Mouse(me) => match me { + MouseEvent::Press(_, x, y) => { + write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap(); } - } + _ => (), + }, _ => {} } stdout.flush().unwrap(); diff --git a/examples/commie.rs b/examples/commie.rs index 32f8820e502ad86e1e65e7a69c158ed7019c94c1..edb810383f3bd37b978a8fbad7df65c285f727a6 100644 --- a/examples/commie.rs +++ b/examples/commie.rs @@ -2,7 +2,7 @@ extern crate termion; use termion::{clear, color, cursor}; -use std::{time, thread}; +use std::{thread, time}; const COMMUNISM: &'static str = r#" !######### # @@ -28,20 +28,26 @@ const COMMUNISM: &'static str = r#" fn main() { let mut state = 0; - println!("\n{}{}{}{}{}{}", - cursor::Hide, - clear::All, - cursor::Goto(1, 1), - color::Fg(color::Black), - color::Bg(color::Red), - COMMUNISM); + println!( + "\n{}{}{}{}{}{}", + cursor::Hide, + clear::All, + cursor::Goto(1, 1), + color::Fg(color::Black), + color::Bg(color::Red), + COMMUNISM + ); loop { - println!("{}{} ☭ GAY ☭ SPACE ☭ COMMUNISM ☭ ", - cursor::Goto(1, 1), - color::Bg(color::AnsiValue(state))); - println!("{}{} WILL PREVAIL, COMRADES! ", - cursor::Goto(1, 20), - color::Bg(color::AnsiValue(state))); + println!( + "{}{} ☭ GAY ☭ SPACE ☭ COMMUNISM ☭ ", + cursor::Goto(1, 1), + color::Bg(color::AnsiValue(state)) + ); + println!( + "{}{} WILL PREVAIL, COMRADES! ", + cursor::Goto(1, 20), + color::Bg(color::AnsiValue(state)) + ); state += 1; state %= 8; diff --git a/examples/detect_color.rs b/examples/detect_color.rs index ecfdd5b45a0fcdf1a02db6d47778602c0256033a..9ba6b1e091d3da077717bd1e9ae0da7f6cab0931 100644 --- a/examples/detect_color.rs +++ b/examples/detect_color.rs @@ -1,8 +1,8 @@ extern crate termion; -use termion::color::{DetectColors, AnsiValue, Bg}; -use termion::raw::IntoRawMode; use std::io::stdout; +use termion::color::{AnsiValue, Bg, DetectColors}; +use termion::raw::IntoRawMode; fn main() { let count; diff --git a/examples/keys.rs b/examples/keys.rs index 272ffd1b9aaec393769a58021b3029512da1c699..f8d421564464cc7ae1e54b2f0253367b2311098f 100644 --- a/examples/keys.rs +++ b/examples/keys.rs @@ -1,28 +1,32 @@ extern crate termion; +use std::io::{stdin, stdout, Write}; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; -use std::io::{Write, stdout, stdin}; fn main() { let stdin = stdin(); let mut stdout = stdout().into_raw_mode().unwrap(); - write!(stdout, - "{}{}q to exit. Type stuff, use alt, and so on.{}", - termion::clear::All, - termion::cursor::Goto(1, 1), - termion::cursor::Hide) - .unwrap(); + write!( + stdout, + "{}{}q to exit. Type stuff, use alt, and so on.{}", + termion::clear::All, + termion::cursor::Goto(1, 1), + termion::cursor::Hide + ) + .unwrap(); stdout.flush().unwrap(); for c in stdin.keys() { - write!(stdout, - "{}{}", - termion::cursor::Goto(1, 1), - termion::clear::CurrentLine) - .unwrap(); + write!( + stdout, + "{}{}", + termion::cursor::Goto(1, 1), + termion::clear::CurrentLine + ) + .unwrap(); match c.unwrap() { Key::Char('q') => break, diff --git a/examples/mouse.rs b/examples/mouse.rs index cbe8baf4649bb4ca177781679bd439a41a931f90..7f084286d6154efe217c6b1751f1af7e7bec8c95 100644 --- a/examples/mouse.rs +++ b/examples/mouse.rs @@ -1,43 +1,43 @@ extern crate termion; -use termion::event::*; +use std::io::{self, Write}; use termion::cursor::{self, DetectCursorPos}; -use termion::input::{TermRead, MouseTerminal}; +use termion::event::*; +use termion::input::{MouseTerminal, TermRead}; use termion::raw::IntoRawMode; -use std::io::{self, Write}; fn main() { let stdin = io::stdin(); let mut stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap()); - writeln!(stdout, - "{}{}q to exit. Type stuff, use alt, click around...", - termion::clear::All, - termion::cursor::Goto(1, 1)) - .unwrap(); + writeln!( + stdout, + "{}{}q to exit. Type stuff, use alt, click around...", + termion::clear::All, + termion::cursor::Goto(1, 1) + ) + .unwrap(); for c in stdin.events() { let evt = c.unwrap(); match evt { Event::Key(Key::Char('q')) => break, - Event::Mouse(me) => { - match me { - MouseEvent::Press(_, a, b) | - MouseEvent::Release(a, b) | - MouseEvent::Hold(a, b) => { - write!(stdout, "{}", cursor::Goto(a, b)).unwrap(); - let (x, y) = stdout.cursor_pos().unwrap(); - write!(stdout, - "{}{}Cursor is at: ({},{}){}", - cursor::Goto(5, 5), - termion::clear::UntilNewline, - x, - y, - cursor::Goto(a, b)) - .unwrap(); - } + Event::Mouse(me) => match me { + MouseEvent::Press(_, a, b) | MouseEvent::Release(a, b) | MouseEvent::Hold(a, b) => { + write!(stdout, "{}", cursor::Goto(a, b)).unwrap(); + let (x, y) = stdout.cursor_pos().unwrap(); + write!( + stdout, + "{}{}Cursor is at: ({},{}){}", + cursor::Goto(5, 5), + termion::clear::UntilNewline, + x, + y, + cursor::Goto(a, b) + ) + .unwrap(); } - } + }, _ => {} } diff --git a/examples/rainbow.rs b/examples/rainbow.rs index 4ee4000d016e497f12f38ec804e6b7eaa1eec710..86c98f0d069a2d325ed9fba3497ef7c0a108291e 100644 --- a/examples/rainbow.rs +++ b/examples/rainbow.rs @@ -1,25 +1,29 @@ extern crate termion; +use std::io::{stdin, stdout, Write}; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; -use std::io::{Write, stdout, stdin}; fn rainbow(stdout: &mut W, blue: u8) { - write!(stdout, - "{}{}", - termion::cursor::Goto(1, 1), - termion::clear::All) - .unwrap(); + write!( + stdout, + "{}{}", + termion::cursor::Goto(1, 1), + termion::clear::All + ) + .unwrap(); for red in 0..32 { let red = red * 8; for green in 0..64 { let green = green * 4; - write!(stdout, - "{} ", - termion::color::Bg(termion::color::Rgb(red, green, blue))) - .unwrap(); + write!( + stdout, + "{} ", + termion::color::Bg(termion::color::Rgb(red, green, blue)) + ) + .unwrap(); } write!(stdout, "\n\r").unwrap(); } @@ -31,12 +35,14 @@ fn main() { let stdin = stdin(); let mut stdout = stdout().into_raw_mode().unwrap(); - writeln!(stdout, - "{}{}{}Use the up/down arrow keys to change the blue in the rainbow.", - termion::clear::All, - termion::cursor::Goto(1, 1), - termion::cursor::Hide) - .unwrap(); + writeln!( + stdout, + "{}{}{}Use the up/down arrow keys to change the blue in the rainbow.", + termion::clear::All, + termion::cursor::Goto(1, 1), + termion::cursor::Hide + ) + .unwrap(); let mut blue = 172u8; diff --git a/examples/read.rs b/examples/read.rs index 8d53e1bf72e0ca8c10fe5a9aa4219e49b9a27379..479575001b54d37b16b633458abb1933fee9db45 100644 --- a/examples/read.rs +++ b/examples/read.rs @@ -1,7 +1,7 @@ extern crate termion; +use std::io::{stdin, stdout, Write}; use termion::input::TermRead; -use std::io::{Write, stdout, stdin}; fn main() { let stdout = stdout(); diff --git a/examples/simple.rs b/examples/simple.rs index 93ef1df3ab7dc0a8968827ed1f289a612076232a..2a28344897fd195fb5dff4bb9eaa91f1451902e5 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,8 +1,8 @@ extern crate termion; +use std::io::{stdin, stdout, Read, Write}; use termion::color; use termion::raw::IntoRawMode; -use std::io::{Read, Write, stdout, stdin}; fn main() { // Initialize 'em all. @@ -11,14 +11,16 @@ fn main() { let stdin = stdin(); let stdin = stdin.lock(); - write!(stdout, - "{}{}{}yo, 'q' will exit.{}{}", - termion::clear::All, - termion::cursor::Goto(5, 5), - termion::style::Bold, - termion::style::Reset, - termion::cursor::Goto(20, 10)) - .unwrap(); + write!( + stdout, + "{}{}{}yo, 'q' will exit.{}{}", + termion::clear::All, + termion::cursor::Goto(5, 5), + termion::style::Bold, + termion::style::Reset, + termion::cursor::Goto(20, 10) + ) + .unwrap(); stdout.flush().unwrap(); let mut bytes = stdin.bytes(); @@ -26,16 +28,16 @@ fn main() { let b = bytes.next().unwrap().unwrap(); match b { - // Quit - b'q' => return, - // Clear the screen - b'c' => write!(stdout, "{}", termion::clear::All), - // Set red color - b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))), - // Write it to stdout. - a => write!(stdout, "{}", a), - } - .unwrap(); + // Quit + b'q' => return, + // Clear the screen + b'c' => write!(stdout, "{}", termion::clear::All), + // Set red color + b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))), + // Write it to stdout. + a => write!(stdout, "{}", a), + } + .unwrap(); stdout.flush().unwrap(); } diff --git a/examples/truecolor.rs b/examples/truecolor.rs index ba5c8685b89d2011c033b466ce5e6cea08b7fb30..8be0aa3ab4889e4a45cbe5109d1e1548e185e706 100644 --- a/examples/truecolor.rs +++ b/examples/truecolor.rs @@ -1,7 +1,7 @@ extern crate termion; -use termion::{color, cursor, clear}; use std::{thread, time}; +use termion::{clear, color, cursor}; fn main() { for r in 0..255 { diff --git a/src/async.rs b/src/async.rs index ea024891640682443424dd9e907409168b5a37cb..6536d893fcb8d9699b1ab2166f039df07005d587 100644 --- a/src/async.rs +++ b/src/async.rs @@ -11,16 +11,21 @@ use sys::tty::get_tty; pub fn async_stdin_until(delimiter: u8) -> AsyncReader { let (send, recv) = mpsc::channel(); - thread::spawn(move || for i in get_tty().unwrap().bytes() { - - match i { - Ok(byte) => { - let end_of_stream = &byte == &delimiter; - let send_error = send.send(Ok(byte)).is_err(); - - if end_of_stream || send_error { return; } - }, - Err(_) => { return; } + thread::spawn(move || { + for i in get_tty().unwrap().bytes() { + match i { + Ok(byte) => { + let end_of_stream = &byte == &delimiter; + let send_error = send.send(Ok(byte)).is_err(); + + if end_of_stream || send_error { + return; + } + } + Err(_) => { + return; + } + } } }); @@ -40,11 +45,13 @@ pub fn async_stdin_until(delimiter: u8) -> AsyncReader { pub fn async_stdin() -> AsyncReader { let (send, recv) = mpsc::channel(); - thread::spawn(move || for i in get_tty().unwrap().bytes() { - if send.send(i).is_err() { - return; - } - }); + thread::spawn(move || { + for i in get_tty().unwrap().bytes() { + if send.send(i).is_err() { + return; + } + } + }); AsyncReader { recv: recv } } diff --git a/src/color.rs b/src/color.rs index 73776c22ebdc929377234589c467a900715f0df0..885af0699151b26fd203bc2bafce7610f0728bf8 100644 --- a/src/color.rs +++ b/src/color.rs @@ -12,13 +12,13 @@ //! } //! ``` -use std::fmt; -use raw::CONTROL_SEQUENCE_TIMEOUT; -use std::io::{self, Write, Read}; -use std::time::{SystemTime, Duration}; use async::async_stdin; -use std::env; use numtoa::NumToA; +use raw::CONTROL_SEQUENCE_TIMEOUT; +use std::env; +use std::fmt; +use std::io::{self, Read, Write}; +use std::time::{Duration, SystemTime}; /// A terminal color. pub trait Color { @@ -49,11 +49,15 @@ macro_rules! derive_color { impl $name { #[inline] /// Returns the ANSI escape sequence as a string. - pub fn fg_str(&self) -> &'static str { csi!("38;5;", $value, "m") } + pub fn fg_str(&self) -> &'static str { + csi!("38;5;", $value, "m") + } #[inline] /// Returns the ANSI escape sequences as a string. - pub fn bg_str(&self) -> &'static str { csi!("48;5;", $value, "m") } + pub fn bg_str(&self) -> &'static str { + csi!("48;5;", $value, "m") + } } }; } @@ -94,15 +98,21 @@ pub struct AnsiValue(pub u8); impl AnsiValue { /// 216-color (r, g, b ≤ 5) RGB. pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue { - debug_assert!(r <= 5, - "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.", - r); - debug_assert!(g <= 5, - "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.", - g); - debug_assert!(b <= 5, - "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.", - b); + debug_assert!( + r <= 5, + "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.", + r + ); + debug_assert!( + g <= 5, + "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.", + g + ); + debug_assert!( + b <= 5, + "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.", + b + ); AnsiValue(16 + 36 * r + 6 * g + b) } @@ -112,10 +122,12 @@ impl AnsiValue { /// There are 24 shades of gray. pub fn grayscale(shade: u8) -> AnsiValue { // Unfortunately, there are a little less than fifty shades. - debug_assert!(shade < 24, - "Grayscale out of bound (shade = {}). There are only 24 shades of \ - gray.", - shade); + debug_assert!( + shade < 24, + "Grayscale out of bound (shade = {}). There are only 24 shades of \ + gray.", + shade + ); AnsiValue(0xE8 + shade) } @@ -124,14 +136,14 @@ impl AnsiValue { impl AnsiValue { /// Returns the ANSI sequence as a string. pub fn fg_string(self) -> String { - let mut x = [0u8; 20]; + let mut x = [0u8; 3]; let x = self.0.numtoa_str(10, &mut x); [csi!("38;5;"), x, "m"].concat() } /// Returns the ANSI sequence as a string. pub fn bg_string(self) -> String { - let mut x = [0u8; 20]; + let mut x = [0u8; 3]; let x = self.0.numtoa_str(10, &mut x); [csi!("48;5;"), x, "m"].concat() } @@ -156,7 +168,7 @@ pub struct Rgb(pub u8, pub u8, pub u8); impl Rgb { /// Returns the ANSI sequence as a string. pub fn fg_string(self) -> String { - let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]); + let (mut x, mut y, mut z) = ([0u8; 3], [0u8; 3], [0u8; 3]); let (x, y, z) = ( self.0.numtoa_str(10, &mut x), self.1.numtoa_str(10, &mut y), @@ -168,7 +180,7 @@ impl Rgb { /// Returns the ANSI sequence as a string. pub fn bg_string(self) -> String { - let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]); + let (mut x, mut y, mut z) = ([0u8; 3], [0u8; 3], [0u8; 3]); let (x, y, z) = ( self.0.numtoa_str(10, &mut x), self.1.numtoa_str(10, &mut y), @@ -200,9 +212,13 @@ const RESET_BG: &str = csi!("49m"); impl Reset { /// Returns the ANSI sequence as a string. - pub fn fg_str(self) -> &'static str { RESET_FG } + pub fn fg_str(self) -> &'static str { + RESET_FG + } /// Returns the ANSI sequence as a string. - pub fn bg_str(self) -> &'static str { RESET_BG } + pub fn bg_str(self) -> &'static str { + RESET_BG + } } impl Color for Reset { @@ -268,15 +284,15 @@ impl DetectColors for W { } else { // OSC 4 is not supported, trust TERM contents. Ok(match env::var_os("TERM") { - Some(val) => { - if val.to_str().unwrap_or("").contains("256color") { - 256 - } else { - 8 - } - } - None => 8, - }) + Some(val) => { + if val.to_str().unwrap_or("").contains("256color") { + 256 + } else { + 8 + } + } + None => 8, + }) } } } diff --git a/src/cursor.rs b/src/cursor.rs index b9791a2760c0c7b69518951a686692669f652fed..52142356a5d706bfbcf501da1deb15dacd43da34 100644 --- a/src/cursor.rs +++ b/src/cursor.rs @@ -1,11 +1,11 @@ //! Cursor movement. -use std::fmt; -use std::io::{self, Write, Error, ErrorKind, Read}; use async::async_stdin_until; -use std::time::{SystemTime, Duration}; -use raw::CONTROL_SEQUENCE_TIMEOUT; use numtoa::NumToA; +use raw::CONTROL_SEQUENCE_TIMEOUT; +use std::fmt; +use std::io::{self, Error, ErrorKind, Read, Write}; +use std::time::{Duration, SystemTime}; derive_csi_sequence!("Hide the cursor.", Hide, "?25l"); derive_csi_sequence!("Show the cursor.", Show, "?25h"); @@ -35,8 +35,15 @@ pub struct Goto(pub u16, pub u16); impl From for String { fn from(this: Goto) -> String { - let (mut x, mut y) = ([0u8; 20], [0u8; 20]); - ["\x1B[", this.1.numtoa_str(10, &mut x), ";", this.0.numtoa_str(10, &mut y), "H"].concat() + let (mut x, mut y) = ([0u8; 5], [0u8; 5]); + [ + "\x1B[", + this.1.numtoa_str(10, &mut x), + ";", + this.0.numtoa_str(10, &mut y), + "H", + ] + .concat() } } @@ -59,7 +66,7 @@ pub struct Left(pub u16); impl From for String { fn from(this: Left) -> String { - let mut buf = [0u8; 20]; + let mut buf = [0u8; 5]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "D"].concat() } } @@ -76,7 +83,7 @@ pub struct Right(pub u16); impl From for String { fn from(this: Right) -> String { - let mut buf = [0u8; 20]; + let mut buf = [0u8; 5]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "C"].concat() } } @@ -93,7 +100,7 @@ pub struct Up(pub u16); impl From for String { fn from(this: Up) -> String { - let mut buf = [0u8; 20]; + let mut buf = [0u8; 5]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "A"].concat() } } @@ -110,7 +117,7 @@ pub struct Down(pub u16); impl From for String { fn from(this: Down) -> String { - let mut buf = [0u8; 20]; + let mut buf = [0u8; 5]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "B"].concat() } } @@ -151,7 +158,10 @@ impl DetectCursorPos for W { } if read_chars.is_empty() { - return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out.")); + return Err(Error::new( + ErrorKind::Other, + "Cursor position detection timed out.", + )); } // The answer will look like `ESC [ Cy ; Cx R`. @@ -162,14 +172,8 @@ impl DetectCursorPos for W { let coords: String = read_str.chars().skip(beg + 1).collect(); let mut nums = coords.split(';'); - let cy = nums.next() - .unwrap() - .parse::() - .unwrap(); - let cx = nums.next() - .unwrap() - .parse::() - .unwrap(); + let cy = nums.next().unwrap().parse::().unwrap(); + let cx = nums.next().unwrap().parse::().unwrap(); Ok((cx, cy)) } diff --git a/src/event.rs b/src/event.rs index 6e383a1cdb9bbd9f740845028c4c17768d46225d..5bed57c2ae6b70e33b0f168ac8f570fb93df22ad 100644 --- a/src/event.rs +++ b/src/event.rs @@ -97,31 +97,31 @@ pub enum Key { } /// Parse an Event from `item` and possibly subsequent bytes through `iter`. +/// +/// Note that this will /not/ parse `\x1B` as `Key::Esc`, since we can't ensure that a +/// control sequence may follow, and if checked we'd potentially lose a byte of input. pub fn parse_event(item: u8, iter: &mut I) -> Result - where I: Iterator> +where + I: Iterator>, { - let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { b'\x1B' => { // This is an escape character, leading a control sequence. - Ok(match iter.next() { - Some(Ok(b'O')) => { - match iter.next() { - // F1-F4 - Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')), - _ => return Err(error), + match iter.next() { + Some(Ok(b'O')) => { + match iter.next() { + // F1-F4 + Some(Ok(val @ b'P'...b'S')) => Ok(Event::Key(Key::F(1 + val - b'P'))), + _ => Err(Error::new(ErrorKind::Other, "Could not parse an event")), + } } - } - Some(Ok(b'[')) => { // This is a CSI sequence. - parse_csi(iter).ok_or(error)? - } - Some(Ok(c)) => { - let ch = parse_utf8_char(c, iter); - Event::Key(Key::Alt(try!(ch))) + Some(Ok(b'[')) => parse_csi(iter), + Some(Ok(c)) => Ok(Event::Key(Key::Alt(parse_utf8_char(c, iter)?))), + Some(Err(_)) | None => { + Err(Error::new(ErrorKind::Other, "Could not parse an event")) + } } - Some(Err(_)) | None => return Err(error), - }) } b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))), b'\t' => Ok(Event::Key(Key::Char('\t'))), @@ -129,211 +129,288 @@ pub fn parse_event(item: u8, iter: &mut I) -> Result c @ b'\x01'...b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))), c @ b'\x1C'...b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))), b'\0' => Ok(Event::Key(Key::Null)), - c => { - Ok({ - let ch = parse_utf8_char(c, iter); - Event::Key(Key::Char(try!(ch))) - }) - } + c => Ok(Event::Key(Key::Char(parse_utf8_char(c, iter)?))), } } /// Parses a CSI sequence, just after reading ^[ /// /// Returns None if an unrecognized sequence is found. -fn parse_csi(iter: &mut I) -> Option - where I: Iterator> +fn parse_csi(iter: &mut I) -> Result +where + I: Iterator>, { - Some(match iter.next() { - Some(Ok(b'[')) => match iter.next() { - Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), - _ => return None, - }, - Some(Ok(b'D')) => Event::Key(Key::Left), - Some(Ok(b'C')) => Event::Key(Key::Right), - Some(Ok(b'A')) => Event::Key(Key::Up), - Some(Ok(b'B')) => Event::Key(Key::Down), - Some(Ok(b'H')) => Event::Key(Key::Home), - Some(Ok(b'F')) => Event::Key(Key::End), - Some(Ok(b'M')) => { + Ok(match iter.next() { + Some(Ok(b'[')) => match iter.next() { + Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + }, + Some(Ok(b'D')) => Event::Key(Key::Left), + Some(Ok(b'C')) => Event::Key(Key::Right), + Some(Ok(b'A')) => Event::Key(Key::Up), + Some(Ok(b'B')) => Event::Key(Key::Down), + Some(Ok(b'H')) => Event::Key(Key::Home), + Some(Ok(b'F')) => Event::Key(Key::End), // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). - let mut next = || iter.next().unwrap().unwrap(); + Some(Ok(b'M')) => { + let mut next = || { + iter.next() + .ok_or_else(|| Error::new(ErrorKind::InvalidData, "Invalid CSI Seqeunce"))? + }; - let cb = next() as i8 - 32; - // (1, 1) are the coords for upper left. - let cx = next().saturating_sub(32) as u16; - let cy = next().saturating_sub(32) as u16; - Event::Mouse(match cb & 0b11 { - 0 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelUp, cx, cy) - } else { - MouseEvent::Press(MouseButton::Left, cx, cy) - } - } - 1 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelDown, cx, cy) - } else { - MouseEvent::Press(MouseButton::Middle, cx, cy) - } - } - 2 => MouseEvent::Press(MouseButton::Right, cx, cy), - 3 => MouseEvent::Release(cx, cy), - _ => return None, - }) - } - Some(Ok(b'<')) => { + let cb = next()? as i8 - 32; + // (1, 1) are the coords for upper left. + let cx = next()?.saturating_sub(32) as u16; + let cy = next()?.saturating_sub(32) as u16; + Event::Mouse(match cb & 0b11 { + 0 => { + if cb & 0x40 != 0 { + MouseEvent::Press(MouseButton::WheelUp, cx, cy) + } else { + MouseEvent::Press(MouseButton::Left, cx, cy) + } + } + 1 => { + if cb & 0x40 != 0 { + MouseEvent::Press(MouseButton::WheelDown, cx, cy) + } else { + MouseEvent::Press(MouseButton::Middle, cx, cy) + } + } + 2 => MouseEvent::Press(MouseButton::Right, cx, cy), + 3 => MouseEvent::Release(cx, cy), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + }) + } // xterm mouse encoding: // ESC [ < Cb ; Cx ; Cy (;) (M or m) - let mut buf = Vec::new(); - let mut c = iter.next().unwrap().unwrap(); - while match c { - b'm' | b'M' => false, - _ => true, - } { - buf.push(c); - c = iter.next().unwrap().unwrap(); - } - let str_buf = String::from_utf8(buf).unwrap(); - let nums = &mut str_buf.split(';'); - - let cb = nums.next() - .unwrap() - .parse::() - .unwrap(); - let cx = nums.next() - .unwrap() - .parse::() - .unwrap(); - let cy = nums.next() - .unwrap() - .parse::() - .unwrap(); + Some(Ok(b'<')) => { + // Parse coordinate bytes, each coordinate is a u16 + let mut next = |break_early| -> Result<(u16, u8), Error> { + let mut len = 0; + let mut end = b'\0'; + let mut bytes = [b'\0'; 5]; + for i in 0..5 { + match iter.next().ok_or_else(|| { + Error::new(ErrorKind::InvalidData, "Incomplete CSI sequence") + })?? { + b';' => { + len = i; + if break_early { + break; + } + } + c @ b'm' | c @ b'M' if !break_early => { + if len == 0 { + len = i; + } + end = c; + break; + } + c => { + bytes[i] = c; + } + } + } + Ok(( + str::from_utf8(&bytes[..len]) + .map_err(|_| { + Error::new(ErrorKind::InvalidData, "CSI sequence is not valid UTF-8") + })? + .parse() + .map_err(|_| { + Error::new(ErrorKind::InvalidData, "CSI sequence contains invalid u16") + })?, + end, + )) + }; - let event = match cb { - 0...2 | 64...65 => { - let button = match cb { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - 64 => MouseButton::WheelUp, - 65 => MouseButton::WheelDown, - _ => unreachable!(), - }; - match c { - b'M' => MouseEvent::Press(button, cx, cy), - b'm' => MouseEvent::Release(cx, cy), - _ => return None, + let (cb, cx, (cy, end)) = (next(true)?.0, next(true)?.0, next(false)?); + let event = match cb { + 0...2 | 64...65 => { + let button = match cb { + 0 => MouseButton::Left, + 1 => MouseButton::Middle, + 2 => MouseButton::Right, + 64 => MouseButton::WheelUp, + 65 => MouseButton::WheelDown, + _ => unreachable!(), + }; + match end { + b'M' => MouseEvent::Press(button, cx, cy), + b'm' => MouseEvent::Release(cx, cy), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + } } - } - 32 => MouseEvent::Hold(cx, cy), - 3 => MouseEvent::Release(cx, cy), - _ => return None, - }; + 32 => MouseEvent::Hold(cx, cy), + 3 => MouseEvent::Release(cx, cy), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + }; - Event::Mouse(event) - } - Some(Ok(c @ b'0'...b'9')) => { - // Numbered escape code. - let mut buf = Vec::new(); - buf.push(c); - let mut c = iter.next().unwrap().unwrap(); - // The final byte of a CSI sequence can be in the range 64-126, so - // let's keep reading anything else. - while c < 64 || c > 126 { - buf.push(c); - c = iter.next().unwrap().unwrap(); + Event::Mouse(event) } + // Numbered escape code. + Some(Ok(c @ b'0'...b'9')) => { + let mut count = 0; + let mut bytes = [b'\0'; 20]; + bytes[0] = c; - match c { - // rxvt mouse encoding: - // ESC [ Cb ; Cx ; Cy ; M - b'M' => { - let str_buf = String::from_utf8(buf).unwrap(); - - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); - - let cb = nums[0]; - let cx = nums[1]; - let cy = nums[2]; + for i in 1..20 { + match iter.next().ok_or_else(|| { + Error::new(ErrorKind::InvalidData, "Incomplete CSI sequence") + })?? { + c @ b'M' | c @ b'~' => { + bytes[i] = c; + count = i; + break; + } + c @ _ => { + bytes[i] = c; + } + } + } - let event = match cb { - 32 => MouseEvent::Press(MouseButton::Left, cx, cy), - 33 => MouseEvent::Press(MouseButton::Middle, cx, cy), - 34 => MouseEvent::Press(MouseButton::Right, cx, cy), - 35 => MouseEvent::Release(cx, cy), - 64 => MouseEvent::Hold(cx, cy), - 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), - _ => return None, - }; + match bytes[count] { + // rxvt mouse encoding: + // ESC [ Cb ; Cx ; Cy ; M + b'M' => { + let mut next = |offset| -> Result<(u16, _), Error> { + let mut len = 0; + for i in 1..5 { + match bytes[offset + i] { + b';' | b'M' | b'\0' => { + len = i; + break; + } + _ => (), + } + } + Ok(( + str::from_utf8(&bytes[offset..(offset + len)]) + .map_err(|_| { + Error::new( + ErrorKind::InvalidData, + "CSI sequence is not valid UTF-8", + ) + })? + .parse() + .map_err(|e| { + Error::new( + ErrorKind::InvalidData, + "CSI sequence contains invalid u16", + ) + })?, + offset + len + 1, + )) + }; - Event::Mouse(event) - } - // Special key code. - b'~' => { - let str_buf = String::from_utf8(buf).unwrap(); + let (cb, off) = next(0)?; + let (cx, off) = next(off)?; + let (cy, _) = next(off)?; - // This CSI sequence can be a list of semicolon-separated - // numbers. - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); + let event = match cb { + 32 => MouseEvent::Press(MouseButton::Left, cx, cy), + 33 => MouseEvent::Press(MouseButton::Middle, cx, cy), + 34 => MouseEvent::Press(MouseButton::Right, cx, cy), + 35 => MouseEvent::Release(cx, cy), + 64 => MouseEvent::Hold(cx, cy), + 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + }; - if nums.is_empty() { - return None; + Event::Mouse(event) } + // Special key code. + // This CSI sequence can be a list of semicolon-separated numbers. + b'~' => { + let mut next = |offset| -> Result<(u8, _), Error> { + let mut len = 0; + for i in 1..3 { + match bytes[offset + i] { + b';' | b'~' | b'\0' => { + len = i; + break; + } + _ => (), + } + } + Ok(( + str::from_utf8(&bytes[offset..offset + len]) + .map_err(|_| { + Error::new( + ErrorKind::InvalidData, + "CSI sequence is not valid UTF-8", + ) + })? + .parse() + .map_err(|_| { + Error::new( + ErrorKind::InvalidData, + "CSI sequence contains invalid u16", + ) + })?, + offset + len, + )) + }; - // TODO: handle multiple values for key modififiers (ex: values - // [3, 2] means Shift+Delete) - if nums.len() > 1 { - return None; - } + let (num, off) = next(0)?; - match nums[0] { - 1 | 7 => Event::Key(Key::Home), - 2 => Event::Key(Key::Insert), - 3 => Event::Key(Key::Delete), - 4 | 8 => Event::Key(Key::End), - 5 => Event::Key(Key::PageUp), - 6 => Event::Key(Key::PageDown), - v @ 11...15 => Event::Key(Key::F(v - 10)), - v @ 17...21 => Event::Key(Key::F(v - 11)), - v @ 23...24 => Event::Key(Key::F(v - 12)), - _ => return None, + // TODO: handle multiple values for key modifiers (ex: values + // [3, 2] means Shift+Delete) + if next(off).is_ok() { + return Err(Error::new(ErrorKind::Other, "CSI sequences with a special key code andmultiple key modifiers not yet supported")); + } + + match num { + 1 | 7 => Event::Key(Key::Home), + 2 => Event::Key(Key::Insert), + 3 => Event::Key(Key::Delete), + 4 | 8 => Event::Key(Key::End), + 5 => Event::Key(Key::PageUp), + 6 => Event::Key(Key::PageDown), + v @ 11...15 => Event::Key(Key::F(v - 10)), + v @ 17...21 => Event::Key(Key::F(v - 11)), + v @ 23...24 => Event::Key(Key::F(v - 12)), + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + } } + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), } - _ => return None, } - } - _ => return None, - }) - + _ => return Err(Error::new(ErrorKind::InvalidData, "Invalid CSI sequence")), + }) } /// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char. fn parse_utf8_char(c: u8, iter: &mut I) -> Result - where I: Iterator> +where + I: Iterator>, { - let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8")); if c.is_ascii() { Ok(c as char) } else { - let bytes = &mut Vec::new(); - bytes.push(c); - - loop { + let mut bytes = [c, b'\0', b'\0', b'\0']; + for i in 1..4 { match iter.next() { Some(Ok(next)) => { - bytes.push(next); - if let Ok(st) = str::from_utf8(bytes) { + bytes[i] = next; + if let Ok(st) = str::from_utf8(&bytes[..i + 1]) { return Ok(st.chars().next().unwrap()); } - if bytes.len() >= 4 { - return error; - } } - _ => return error, + _ => { + return Err(Error::new( + ErrorKind::InvalidData, + "Input character is not valid UTF-8", + )) + } } } + + Err(Error::new( + ErrorKind::InvalidData, + "Input character is not valid UTF-8", + )) } } diff --git a/src/input.rs b/src/input.rs index 5c8ecf4666a8560bba2ccc907856f74a8a84febc..9aa49a9aa36e80d7d865ec956bbed00ebed88583 100644 --- a/src/input.rs +++ b/src/input.rs @@ -27,15 +27,17 @@ impl Iterator for Keys { } /// An iterator over input events. -pub struct Events { - inner: EventsAndRaw +pub struct Events { + inner: EventsAndRaw, } impl Iterator for Events { type Item = Result; fn next(&mut self) -> Option> { - self.inner.next().map(|tuple| tuple.map(|(event, _raw)| event)) + self.inner + .next() + .map(|tuple| tuple.map(|(event, _raw)| event)) } } @@ -64,12 +66,10 @@ impl Iterator for EventsAndRaw { 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), vec![b'\x1B'])), - c => parse_event(c, &mut source.bytes()), - } - } + Ok(1) => match buf[0] { + b'\x1B' => Ok((Event::Key(Key::Esc), vec![b'\x1B'])), + c => parse_event(c, &mut source.bytes()), + }, Ok(2) => { let mut option_iter = &mut Some(buf[1]).into_iter(); let result = { @@ -89,26 +89,34 @@ impl Iterator for EventsAndRaw { } fn parse_event(item: u8, iter: &mut I) -> Result<(Event, Vec), io::Error> - where I: Iterator> +where + I: Iterator>, { let mut buf = vec![item]; let result = { - let mut iter = iter.inspect(|byte| if let &Ok(byte) = byte { - buf.push(byte); - }); + let mut iter = iter.inspect(|byte| { + if let &Ok(byte) = byte { + buf.push(byte); + } + }); event::parse_event(item, &mut iter) }; - result.or(Ok(Event::Unsupported(buf.clone()))).map(|e| (e, buf)) + result + .or(Ok(Event::Unsupported(buf.clone()))) + .map(|e| (e, buf)) } - /// 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. /// @@ -126,15 +134,16 @@ pub trait TermRead { } } - impl TermRead for R { fn events(self) -> Events { Events { - inner: self.events_and_raw() + inner: self.events_and_raw(), } } fn keys(self) -> Keys { - Keys { iter: self.events() } + Keys { + iter: self.events(), + } } fn read_line(&mut self) -> io::Result> { @@ -152,8 +161,8 @@ impl TermRead for R { } } - let string = try!(String::from_utf8(buf) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))); + let string = + try!(String::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))); Ok(Some(string)) } } @@ -161,7 +170,9 @@ impl TermRead for R { /// Extension to `TermRead` trait. A separate trait in order to maintain backwards compatibility. pub trait TermReadEventsAndRaw { /// An iterator over input events and the bytes that define them. - fn events_and_raw(self) -> EventsAndRaw where Self: Sized; + fn events_and_raw(self) -> EventsAndRaw + where + Self: Sized; } impl TermReadEventsAndRaw for R { @@ -227,8 +238,8 @@ impl Write for MouseTerminal { #[cfg(test)] mod test { use super::*; + use event::{Event, Key, MouseButton, MouseEvent}; use std::io; - use event::{Key, Event, MouseEvent, MouseButton}; #[test] fn test_keys() { @@ -244,27 +255,38 @@ mod test { #[test] fn test_events() { - let mut i = - b"\x1B[\x00bc\x7F\x1B[D\ + let mut i = b"\x1B[\x00bc\x7F\x1B[D\ \x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb" - .events(); + .events(); - assert_eq!(i.next().unwrap().unwrap(), - Event::Unsupported(vec![0x1B, b'[', 0x00])); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Unsupported(vec![0x1B, b'[', 0x00]) + ); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b'))); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('c'))); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Backspace)); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Left)); - assert_eq!(i.next().unwrap().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); - assert_eq!(i.next().unwrap().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)) + ); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Release(2, 4)) + ); + assert_eq!( + i.next().unwrap().unwrap(), + Event::Mouse(MouseEvent::Release(2, 4)) + ); assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b'))); assert!(i.next().is_none()); } @@ -275,25 +297,36 @@ mod test { \x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb"; let mut output = Vec::::new(); { - let mut i = input.events_and_raw().map(|res| res.unwrap()) - .inspect(|&(_, ref raw)| { output.extend(raw); }).map(|(event, _)| event); - - assert_eq!(i.next().unwrap(), - Event::Unsupported(vec![0x1B, b'[', 0x00])); + let mut i = input + .events_and_raw() + .map(|res| res.unwrap()) + .inspect(|&(_, ref raw)| { + output.extend(raw); + }) + .map(|(event, _)| event); + + assert_eq!( + i.next().unwrap(), + Event::Unsupported(vec![0x1B, b'[', 0x00]) + ); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b'))); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('c'))); assert_eq!(i.next().unwrap(), Event::Key(Key::Backspace)); assert_eq!(i.next().unwrap(), Event::Key(Key::Left)); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); - assert_eq!(i.next().unwrap(), - Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)) + ); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!( + i.next().unwrap(), + Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)) + ); + assert_eq!(i.next().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); + assert_eq!(i.next().unwrap(), Event::Mouse(MouseEvent::Release(2, 4))); assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b'))); assert!(i.next().is_none()); } @@ -310,7 +343,7 @@ mod test { let mut st = b"\x1B[11~\x1B[12~\x1B[13~\x1B[14~\x1B[15~\ \x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~" - .keys(); + .keys(); for i in 1..13 { assert_eq!(st.next().unwrap().unwrap(), Key::F(i)); } @@ -365,18 +398,26 @@ mod test { #[test] fn test_backspace() { - line_match("this is the\x7f first\x7f\x7f test", - Some("this is th fir test")); - line_match("this is the seco\x7fnd test\x7f", - Some("this is the secnd tes")); + line_match( + "this is the\x7f first\x7f\x7f test", + Some("this is th fir test"), + ); + line_match( + "this is the seco\x7fnd test\x7f", + Some("this is the secnd tes"), + ); } #[test] fn test_end() { - line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ", - Some("abc")); - line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA", - Some("hello")); + line_match( + "abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ", + Some("abc"), + ); + line_match( + "hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA", + Some("hello"), + ); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 1d8f66fa1622d71cc5cb9e36a3cdec4ed68c97f8..ea655b80107af987492ece437a3d964c03a25847 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,18 +14,18 @@ extern crate numtoa; #[cfg(target_os = "redox")] -#[path="sys/redox/mod.rs"] +#[path = "sys/redox/mod.rs"] mod sys; #[cfg(unix)] -#[path="sys/unix/mod.rs"] +#[path = "sys/unix/mod.rs"] mod sys; pub use sys::size::terminal_size; -pub use sys::tty::{is_tty, get_tty}; +pub use sys::tty::{get_tty, is_tty}; mod async; -pub use async::{AsyncReader, async_stdin}; +pub use async::{async_stdin, AsyncReader}; #[macro_use] mod macros; diff --git a/src/macros.rs b/src/macros.rs index 5fd70b933a9e23eafdd2eae8d0548766eb805db2..3b47b59f6e846e58bfd612c8ebff5550d55fc778 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -17,11 +17,15 @@ macro_rules! derive_csi_sequence { } impl AsRef<[u8]> for $name { - fn as_ref(&self) -> &'static [u8] { csi!($value).as_bytes() } + fn as_ref(&self) -> &'static [u8] { + csi!($value).as_bytes() + } } impl AsRef for $name { - fn as_ref(&self) -> &'static str { csi!($value) } + fn as_ref(&self) -> &'static str { + csi!($value) + } } }; } diff --git a/src/raw.rs b/src/raw.rs index 0dbfb569e2f6cdf8ec75c1cee83138f6cd9b8b0f..f47b426c58877d318c54569798e67806c174c080 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -25,8 +25,8 @@ use std::io::{self, Write}; use std::ops; -use sys::Termios; use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr}; +use sys::Termios; /// The timeout of an escape code control sequence, in milliseconds. pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100; @@ -118,7 +118,7 @@ impl RawTerminal { #[cfg(test)] mod test { use super::*; - use std::io::{Write, stdout}; + use std::io::{stdout, Write}; #[test] fn test_into_raw_mode() { diff --git a/src/screen.rs b/src/screen.rs index 822399e27c6a620d672e66ccc699c6907af4e234..50786ef4a646a81f75ad9307cdf845508673e7a5 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -19,9 +19,9 @@ //! } //! ``` +use std::fmt; use std::io::{self, Write}; use std::ops; -use std::fmt; /// Switch to the main screen buffer of the terminal. pub struct ToMainScreen; diff --git a/src/style.rs b/src/style.rs index 58e9a78b87847879d41c839e1851e6f27a0b0360..f3ea9239e156441a6cbad063cc0e55cc268a6ccc 100644 --- a/src/style.rs +++ b/src/style.rs @@ -16,7 +16,9 @@ derive_csi_sequence!("Undo italic text.", NoItalic, "23m"); derive_csi_sequence!("Undo underlined text.", NoUnderline, "24m"); derive_csi_sequence!("Undo blinking text (not widely supported).", NoBlink, "25m"); derive_csi_sequence!("Undo inverted colors (negative mode).", NoInvert, "27m"); -derive_csi_sequence!("Undo crossed out text (not widely supported).", - NoCrossedOut, - "29m"); +derive_csi_sequence!( + "Undo crossed out text (not widely supported).", + NoCrossedOut, + "29m" +); derive_csi_sequence!("Framed text (not widely supported).", Framed, "51m"); diff --git a/src/sys/redox/attr.rs b/src/sys/redox/attr.rs index c6489a510c4862e45e40489f014a44f339bffea6..864ccbcb675ff0feb5d396c51910beda89ff5b1d 100644 --- a/src/sys/redox/attr.rs +++ b/src/sys/redox/attr.rs @@ -12,7 +12,10 @@ pub fn get_terminal_attr() -> io::Result { if res? == termios.len() { Ok(termios) } else { - Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes.")) + Err(io::Error::new( + io::ErrorKind::Other, + "Unable to get the terminal attributes.", + )) } } @@ -24,7 +27,10 @@ pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> { if res? == termios.len() { Ok(()) } else { - Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes.")) + Err(io::Error::new( + io::ErrorKind::Other, + "Unable to set the terminal attributes.", + )) } } diff --git a/src/sys/redox/size.rs b/src/sys/redox/size.rs index 07f64a24379139b3f834cfaff6e537cb2fac32c0..babae19ce196ce35d4e5152814a7121a9f753bbc 100644 --- a/src/sys/redox/size.rs +++ b/src/sys/redox/size.rs @@ -13,6 +13,9 @@ pub fn terminal_size() -> io::Result<(u16, u16)> { if res? == winsize.len() { Ok((winsize.ws_col, winsize.ws_row)) } else { - Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size.")) + Err(io::Error::new( + io::ErrorKind::Other, + "Unable to get the terminal size.", + )) } } diff --git a/src/sys/redox/tty.rs b/src/sys/redox/tty.rs index 9179b3962553fecea204f958e70bb778f5e23c72..f57f8d1e97c8c91f9393327dce343cc657284a90 100644 --- a/src/sys/redox/tty.rs +++ b/src/sys/redox/tty.rs @@ -1,5 +1,5 @@ -use std::{env, fs, io}; use std::os::unix::io::AsRawFd; +use std::{env, fs, io}; use super::syscall; diff --git a/src/sys/unix/attr.rs b/src/sys/unix/attr.rs index 5e21fbac8ef8e69e69fb34b7899826182219b486..3c34d60d253aa6745429f2205d22a310390da984 100644 --- a/src/sys/unix/attr.rs +++ b/src/sys/unix/attr.rs @@ -1,7 +1,7 @@ use std::{io, mem}; -use super::{cvt, Termios}; use super::libc::c_int; +use super::{cvt, Termios}; pub fn get_terminal_attr() -> io::Result { extern "C" { diff --git a/src/sys/unix/tty.rs b/src/sys/unix/tty.rs index 2be9363470374e7d14b43472932a1d7f2215a7cf..8baf083cf31e9be7e18174790773076c07561e37 100644 --- a/src/sys/unix/tty.rs +++ b/src/sys/unix/tty.rs @@ -1,9 +1,8 @@ -use std::{fs, io}; use std::os::unix::io::AsRawFd; +use std::{fs, io}; use super::libc; - /// Is this stream a TTY? pub fn is_tty(stream: &T) -> bool { unsafe { libc::isatty(stream.as_raw_fd()) == 1 } @@ -13,5 +12,8 @@ pub fn is_tty(stream: &T) -> bool { /// /// This allows for getting stdio representing _only_ the TTY, and not other streams. pub fn get_tty() -> io::Result { - fs::OpenOptions::new().read(true).write(true).open("/dev/tty") + fs::OpenOptions::new() + .read(true) + .write(true) + .open("/dev/tty") }