...
 
Commits (5)
......@@ -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"
......
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() {
{
......
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<W: Write>(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).",
......
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();
......
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();
......
......@@ -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;
......
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;
......
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,
......
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();
}
}
},
_ => {}
}
......
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<W: Write>(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;
......
extern crate termion;
use std::io::{stdin, stdout, Write};
use termion::input::TermRead;
use std::io::{Write, stdout, stdin};
fn main() {
let stdout = stdout();
......
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();
}
......
extern crate termion;
use termion::{color, cursor, clear};
use std::{thread, time};
use termion::{clear, color, cursor};
fn main() {
for r in 0..255 {
......
......@@ -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 }
}
......
......@@ -12,13 +12,12 @@
//! }
//! ```
use std::fmt;
use numtoa::NumToA;
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 std::fmt;
use std::io::{self, Read, Write};
use std::time::{Duration, SystemTime};
/// A terminal color.
pub trait Color {
......@@ -49,11 +48,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 +97,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 +121,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 +135,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 +167,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 +179,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 +211,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 {
......@@ -248,7 +263,8 @@ pub trait DetectColors {
impl<W: Write> DetectColors for W {
fn available_colors(&mut self) -> io::Result<u16> {
let mut stdin = async_stdin();
let stdin = io::stdin();
let mut stdin = stdin.lock();
if detect_color(self, &mut stdin, 0)? {
// OSC 4 is supported, detect how many colors there are.
......@@ -268,15 +284,15 @@ impl<W: Write> 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,
})
}
}
}
......
//! 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<Goto> 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<Left> 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<Right> 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<Up> 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<Down> 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<W: Write> 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<W: Write> DetectCursorPos for W {
let coords: String = read_str.chars().skip(beg + 1).collect();
let mut nums = coords.split(';');
let cy = nums.next()
.unwrap()
.parse::<u16>()
.unwrap();
let cx = nums.next()
.unwrap()
.parse::<u16>()
.unwrap();
let cy = nums.next().unwrap().parse::<u16>().unwrap();
let cx = nums.next().unwrap().parse::<u16>().unwrap();
Ok((cx, cy))
}
......
This diff is collapsed.
......@@ -27,15 +27,17 @@ impl<R: Read> Iterator for Keys<R> {
}
/// An iterator over input events.
pub struct Events<R> {
inner: EventsAndRaw<R>
pub struct Events<R> {
inner: EventsAndRaw<R>,
}
impl<R: Read> Iterator for Events<R> {
type Item = Result<Event, io::Error>;
fn next(&mut self) -> Option<Result<Event, io::Error>> {
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<R: Read> Iterator for EventsAndRaw<R> {
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<R: Read> Iterator for EventsAndRaw<R> {
}
fn parse_event<I>(item: u8, iter: &mut I) -> Result<(Event, Vec<u8>), io::Error>
where I: Iterator<Item = Result<u8, io::Error>>
where
I: Iterator<Item = Result<u8, io::Error>>,
{
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<Self> where Self: Sized;
fn events(self) -> Events<Self>
where
Self: Sized;
/// An iterator over key inputs.
fn keys(self) -> Keys<Self> where Self: Sized;
fn keys(self) -> Keys<Self>
where
Self: Sized;
/// Read a line.
///
......@@ -126,15 +134,16 @@ pub trait TermRead {
}
}
impl<R: Read + TermReadEventsAndRaw> TermRead for R {
fn events(self) -> Events<Self> {
Events {
inner: self.events_and_raw()
inner: self.events_and_raw(),
}
}
fn keys(self) -> Keys<Self> {
Keys { iter: self.events() }
Keys {
iter: self.events(),
}
}
fn read_line(&mut self) -> io::Result<Option<String>> {
......@@ -152,8 +161,8 @@ impl<R: Read + TermReadEventsAndRaw> 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<R: Read + TermReadEventsAndRaw> 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<Self> where Self: Sized;
fn events_and_raw(self) -> EventsAndRaw<Self>
where
Self: Sized;
}
impl<R: Read> TermReadEventsAndRaw for R {
......@@ -227,8 +238,8 @@ impl<W: Write> Write for MouseTerminal<W> {
#[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::<u8>::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]
......
......@@ -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;
......
......@@ -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<str> for $name {
fn as_ref(&self) -> &'static str { csi!($value) }
fn as_ref(&self) -> &'static str {
csi!($value)
}
}
};
}
......@@ -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<W: Write> RawTerminal<W> {
#[cfg(test)]
mod test {
use super::*;
use std::io::{Write, stdout};
use std::io::{stdout, Write};
#[test]
fn test_into_raw_mode() {
......
......@@ -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;
......
......@@ -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");
......@@ -12,7 +12,10 @@ pub fn get_terminal_attr() -> io::Result<Termios> {
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.",
))
}
}
......
......@@ -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.",
))
}
}
use std::{env, fs, io};
use std::os::unix::io::AsRawFd;
use std::{env, fs, io};
use super::syscall;
......
use std::{io, mem};
use super::{cvt, Termios};
use super::libc::c_int;
use super::{cvt, Termios};
pub fn get_terminal_attr() -> io::Result<Termios> {
extern "C" {
......
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<T: AsRawFd>(stream: &T) -> bool {
unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
......@@ -13,5 +12,8 @@ pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
///
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
pub fn get_tty() -> io::Result<fs::File> {
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/tty")
}