summaryrefslogtreecommitdiffstats
path: root/third_party/rust/termion
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/termion
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/termion')
-rw-r--r--third_party/rust/termion/.cargo-checksum.json1
-rw-r--r--third_party/rust/termion/Cargo.toml17
-rw-r--r--third_party/rust/termion/LICENSE21
-rw-r--r--third_party/rust/termion/README.md181
-rw-r--r--third_party/rust/termion/examples/alternate_screen.rs17
-rw-r--r--third_party/rust/termion/examples/alternate_screen_raw.rs40
-rw-r--r--third_party/rust/termion/examples/async.rs39
-rw-r--r--third_party/rust/termion/examples/click.rs35
-rw-r--r--third_party/rust/termion/examples/color.rs10
-rw-r--r--third_party/rust/termion/examples/commie.rs51
-rw-r--r--third_party/rust/termion/examples/detect_color.rs19
-rw-r--r--third_party/rust/termion/examples/is_tty.rs11
-rw-r--r--third_party/rust/termion/examples/keys.rs44
-rw-r--r--third_party/rust/termion/examples/mouse.rs46
-rw-r--r--third_party/rust/termion/examples/rainbow.rs60
-rw-r--r--third_party/rust/termion/examples/read.rs23
-rw-r--r--third_party/rust/termion/examples/rustc_fun.rs24
-rw-r--r--third_party/rust/termion/examples/simple.rs42
-rw-r--r--third_party/rust/termion/examples/size.rs7
-rw-r--r--third_party/rust/termion/examples/truecolor.rs12
-rw-r--r--third_party/rust/termion/logo.svg9
-rw-r--r--third_party/rust/termion/src/async.rs78
-rw-r--r--third_party/rust/termion/src/clear.rs9
-rw-r--r--third_party/rust/termion/src/color.rs242
-rw-r--r--third_party/rust/termion/src/cursor.rs140
-rw-r--r--third_party/rust/termion/src/event.rs351
-rw-r--r--third_party/rust/termion/src/input.rs388
-rw-r--r--third_party/rust/termion/src/lib.rs61
-rw-r--r--third_party/rust/termion/src/macros.rs19
-rw-r--r--third_party/rust/termion/src/raw.rs117
-rw-r--r--third_party/rust/termion/src/screen.rs91
-rw-r--r--third_party/rust/termion/src/scroll.rs23
-rw-r--r--third_party/rust/termion/src/style.rs22
-rw-r--r--third_party/rust/termion/src/sys/redox/attr.rs33
-rw-r--r--third_party/rust/termion/src/sys/redox/mod.rs15
-rw-r--r--third_party/rust/termion/src/sys/redox/size.rs18
-rw-r--r--third_party/rust/termion/src/sys/redox/tty.rs22
-rw-r--r--third_party/rust/termion/src/sys/unix/attr.rs29
-rw-r--r--third_party/rust/termion/src/sys/unix/mod.rs33
-rw-r--r--third_party/rust/termion/src/sys/unix/size.rs48
-rw-r--r--third_party/rust/termion/src/sys/unix/tty.rs17
41 files changed, 2465 insertions, 0 deletions
diff --git a/third_party/rust/termion/.cargo-checksum.json b/third_party/rust/termion/.cargo-checksum.json
new file mode 100644
index 0000000000..51aebde873
--- /dev/null
+++ b/third_party/rust/termion/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"b061b09fc4bbf280c932f4b7d3c4641a5a2f0eb8c57029de7d0e4441888a1765","LICENSE":"6252f0c8d4a0df9b2dc0c6464cb2489dbe8859b0eb727e19c14e6af1ee432394","README.md":"71cac837cd6f1326865add7dd565ea2738756648de87fc2d35d0cf22a2512630","examples/alternate_screen.rs":"37978473e77331ad613843049b4f355e32a51a7b8ece9ee52efe02997391e4ec","examples/alternate_screen_raw.rs":"bfd68f86de929952aaed7e1e7175694d771be2a0b0943092fec4f58bf7473dc3","examples/async.rs":"2fdf5fe69edd3b407de3c1c8caf23f9e19a7f25b55578c84838e4305a9857c42","examples/click.rs":"bb3a76f4817292a82b00d92281a86a400038a8282b6c9d41a34dbaca84fb0caf","examples/color.rs":"808219c739677b9f2645e0ae7975a4bd8981255ed8bfc9df2413f8f8059bbda5","examples/commie.rs":"7bb00a7f669c74ccb3a9e9c8ff39fd01aa984f7c38983e5c1cfe6940f90b0c73","examples/detect_color.rs":"764d6465c6879efc38aca8d980a433bac058c1f03d578ac764437af289931824","examples/is_tty.rs":"9a76bdfb11ea84e7b25f6efc06c8d3a8d13a03448bd826bd283b935852f409c7","examples/keys.rs":"cd89f31a21062486e7c014843b360cee35dcee07d8022fc0282eddc48e46bdc7","examples/mouse.rs":"eeccab8043cec987e40e175f03b63c9c6cdc7bcd5808f51a57d0e970cae1c04e","examples/rainbow.rs":"c015176eba7c7a81c6d302d6fc41abea8817935ee804af59d64e3098b127c333","examples/read.rs":"b95fb9b02d2cbf978096825b18ffa25772467df3417c3f3cf5edfe811181a67a","examples/rustc_fun.rs":"f39bd8dbe224cb592d58a0084d15d1eb80d86eade72423fe51acc455e0e1ea68","examples/simple.rs":"eac1aab0d251f884a7d7d7d0d017b6ac4854b38ffcfb244d6a80102ed7a981ab","examples/size.rs":"ae89e7b98a29040f1b641d75226f5738770e2e043cee3f49649accaaeaabbeac","examples/truecolor.rs":"67128ef4870e9e742b1090fa129097484633d82fb015009b1760295f7a24e6af","logo.svg":"09b7a6bca3185acddc217d3d3baab23627c5e03a192f815116e71cf00cb31ddc","src/async.rs":"f0126ff1ee18c8c355bb86deb76e852c5f6bb47fbbaa286cce3d38cb656dfa28","src/clear.rs":"a9cf9a9f92cef2430239dc33d8ecb4e291c46b650f1f71b63248d0a0215768ba","src/color.rs":"2f2c8f6f572b22fc40a62d0502fc192c843dbeed21bb7ecc1954e881d41c9a1c","src/cursor.rs":"9132638902c4e42728efaac247ca363c1bda3e80d6c3cc20bc6928b9fe9a1d10","src/event.rs":"321c0a73cd6208a8ef17083d1bd907e308d5197cfd198a3f1ed1498b7e2b8055","src/input.rs":"a3e226a53b6536aef89d66a1e4974453389c86116c175c3efc4fc4bb9b41d60c","src/lib.rs":"549beee5f5a6fb7954af37df7fa598ba6c716f2287123e09d300e048af2a70ac","src/macros.rs":"8611f257ffc046ed25870611f1353a96e17a5671efbe2bbf33aae525b53a4df3","src/raw.rs":"6949b164cf1bd0a4146c169b948100ed626b3e0747df15a54b7f41853d348ea1","src/screen.rs":"19a9a2c42abe2afdb75d4850dc6ee54f33808f6da7f59644668e683418cbce4a","src/scroll.rs":"d0ba08663127e09e111fafc9f84c1fe42662b74f9acbca6b5d26d0b335ee4136","src/style.rs":"b2c805d710cf553835a01482f014aff7395f79d5f71f1e5029ae679e002ac010","src/sys/redox/attr.rs":"834d21cc17090fb7989906ce885f4efed252d65086c05e32ecc05792eb69151a","src/sys/redox/mod.rs":"4066ba97f10c8b87a6ebf32257c89d36338366e1230781aeb5fca98bd5f5bae0","src/sys/redox/size.rs":"b892d0053f40c343eeb40ea06a10fb77abd2758eecda6a7cccb0b07c07fff6eb","src/sys/redox/tty.rs":"3369a6ee3a21400053a023c01912bdd92b709915b833e25e557214dfad425224","src/sys/unix/attr.rs":"7b17e4841eab69533d2561764506211e2967cd4d7464a309e6d2832473b75dd7","src/sys/unix/mod.rs":"8ddebce9f5b2dbbd419519beb7d41f6d6a7eaed40a70cd0d0676ee893549f7a7","src/sys/unix/size.rs":"19f3de9ced2b329a50a9752ba1be406b25648b298c1a2b3e83582a34e21d2998","src/sys/unix/tty.rs":"eeea0279f76838aa4badd807325403d32ab359334cf59f9574191afcbe86c811"},"package":"689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"} \ No newline at end of file
diff --git a/third_party/rust/termion/Cargo.toml b/third_party/rust/termion/Cargo.toml
new file mode 100644
index 0000000000..a4415719a8
--- /dev/null
+++ b/third_party/rust/termion/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "termion"
+version = "1.5.1"
+authors = ["ticki <Ticki@users.noreply.github.com>", "gycos <alexandre.bury@gmail.com>", "IGI-111 <igi-111@protonmail.com>"]
+description = "A bindless library for manipulating terminals."
+repository = "https://github.com/ticki/termion"
+documentation = "https://docs.rs/termion"
+license = "MIT"
+keywords = ["tty", "color", "terminal", "password", "tui"]
+exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"]
+
+[target.'cfg(not(target_os = "redox"))'.dependencies]
+libc = "0.2.8"
+
+[target.'cfg(target_os = "redox")'.dependencies]
+redox_syscall = "0.1"
+redox_termios = "0.1"
diff --git a/third_party/rust/termion/LICENSE b/third_party/rust/termion/LICENSE
new file mode 100644
index 0000000000..3903091a13
--- /dev/null
+++ b/third_party/rust/termion/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Ticki
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/third_party/rust/termion/README.md b/third_party/rust/termion/README.md
new file mode 100644
index 0000000000..7bda9d317c
--- /dev/null
+++ b/third_party/rust/termion/README.md
@@ -0,0 +1,181 @@
+<p align="center">
+<img alt="Termion logo" src="https://rawgit.com/ticki/termion/master/logo.svg" />
+</p>
+
+[![Build Status](https://travis-ci.org/ticki/termion.svg?branch=master)](https://travis-ci.org/ticki/termion) [![Latest Version](https://img.shields.io/crates/v/termion.svg)](https://crates.io/crates/termion) | [Documentation](https://docs.rs/termion) | [Examples](https://github.com/Ticki/termion/tree/master/examples) | [Changelog](https://github.com/Ticki/termion/tree/master/CHANGELOG.md) | [Tutorial](http://ticki.github.io/blog/making-terminal-applications-in-rust-with-termion/)
+|----|----|----|----|----
+
+
+**Termion** is a pure Rust, bindless library for low-level handling, manipulating
+and reading information about terminals. This provides a full-featured
+alternative to Termbox.
+
+Termion aims to be simple and yet expressive. It is bindless, meaning that it
+is not a front-end to some other library (e.g., ncurses or termbox), but a
+standalone library directly talking to the TTY.
+
+Termion is quite convenient, due to its complete coverage of essential TTY
+features, providing one consistent API. Termion is rather low-level containing
+only abstraction aligned with what actually happens behind the scenes. For
+something more high-level, refer to inquirer-rs, which uses Termion as backend.
+
+Termion generates escapes and API calls for the user. This makes it a whole lot
+cleaner to use escapes.
+
+Supports Redox, Mac OS X, BSD, and Linux (or, in general, ANSI terminals).
+
+## A note on stability
+
+This crate is stable.
+
+## Cargo.toml
+
+```toml
+[dependencies]
+termion = "*"
+```
+
+## 0.1.0 to 1.0.0 guide
+
+This sample table gives an idea of how to go about converting to the new major
+version of Termion.
+
+| 0.1.0 | 1.0.0
+|--------------------------------|---------------------------
+| `use termion::IntoRawMode` | `use termion::raw::IntoRawMode`
+| `use termion::TermRead` | `use termion::input::TermRead`
+| `stdout.color(color::Red);` | `write!(stdout, "{}", color::Fg(color::Red));`
+| `stdout.color_bg(color::Red);` | `write!(stdout, "{}", color::Bg(color::Red));`
+| `stdout.goto(x, y);` | `write!(stdout, "{}", cursor::Goto(x, y));`
+| `color::rgb(r, g, b);` | `color::Rgb(r, g, b)` (truecolor)
+| `x.with_mouse()` | `MouseTerminal::from(x)`
+
+## Features
+
+- Raw mode.
+- TrueColor.
+- 256-color mode.
+- Cursor movement.
+- Text formatting.
+- Console size.
+- TTY-only stream.
+- Control sequences.
+- Termios control.
+- Password input.
+- Redox support.
+- Safe `isatty` wrapper.
+- Panic-free error handling.
+- Special keys events (modifiers, special keys, etc.).
+- Allocation-free.
+- Asynchronous key events.
+- Mouse input.
+- Carefully tested.
+- Detailed documentation on every item.
+
+and much more.
+
+## Examples
+
+### Style and colors.
+
+```rust
+extern crate termion;
+
+use termion::{color, style};
+
+use std::io;
+
+fn main() {
+ println!("{}Red", color::Fg(color::Red));
+ println!("{}Blue", color::Fg(color::Blue));
+ println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
+ println!("{}Just plain italic", style::Italic);
+}
+```
+
+### Moving the cursor
+
+```rust
+extern crate termion;
+
+fn main() {
+ print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(1, 1));
+}
+
+```
+
+### Mouse
+
+```rust
+extern crate termion;
+
+use termion::event::{Key, Event, MouseEvent};
+use termion::input::{TermRead, MouseTerminal};
+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();
+ 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();
+ },
+ _ => (),
+ }
+ }
+ _ => {}
+ }
+ stdout.flush().unwrap();
+ }
+}
+```
+
+### Read a password
+
+```rust
+extern crate termion;
+
+use termion::input::TermRead;
+use std::io::{Write, stdout, stdin};
+
+fn main() {
+ let stdout = stdout();
+ let mut stdout = stdout.lock();
+ let stdin = stdin();
+ let mut stdin = stdin.lock();
+
+ stdout.write_all(b"password: ").unwrap();
+ stdout.flush().unwrap();
+
+ let pass = stdin.read_passwd(&mut stdout);
+
+ if let Ok(Some(pass)) = pass {
+ stdout.write_all(pass.as_bytes()).unwrap();
+ stdout.write_all(b"\n").unwrap();
+ } else {
+ stdout.write_all(b"Error\n").unwrap();
+ }
+}
+```
+
+## Usage
+
+See `examples/`, and the documentation, which can be rendered using `cargo doc`.
+
+For a more complete example, see [a minesweeper implementation](https://github.com/redox-os/games-for-redox/blob/master/src/minesweeper/main.rs), that I made for Redox using termion.
+
+<img src="image.png" width="200">
+
+## License
+
+MIT/X11.
diff --git a/third_party/rust/termion/examples/alternate_screen.rs b/third_party/rust/termion/examples/alternate_screen.rs
new file mode 100644
index 0000000000..91d28b1702
--- /dev/null
+++ b/third_party/rust/termion/examples/alternate_screen.rs
@@ -0,0 +1,17 @@
+extern crate termion;
+
+use termion::screen::*;
+use std::io::{Write, stdout};
+use std::{time, thread};
+
+fn main() {
+ {
+ let mut screen = AlternateScreen::from(stdout());
+ write!(screen, "Welcome to the alternate screen.\n\nPlease wait patiently until we arrive back at the main screen in a about three seconds.").unwrap();
+ screen.flush().unwrap();
+
+ thread::sleep(time::Duration::from_secs(3));
+ }
+
+ println!("Phew! We are back.");
+}
diff --git a/third_party/rust/termion/examples/alternate_screen_raw.rs b/third_party/rust/termion/examples/alternate_screen_raw.rs
new file mode 100644
index 0000000000..7ba7888804
--- /dev/null
+++ b/third_party/rust/termion/examples/alternate_screen_raw.rs
@@ -0,0 +1,40 @@
+extern crate termion;
+
+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).",
+ termion::clear::All,
+ termion::cursor::Goto(1, 1),
+ termion::cursor::Goto(1, 3),
+ termion::cursor::Goto(1, 4)).unwrap();
+}
+
+fn main() {
+ let stdin = stdin();
+ let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap());
+ write!(screen, "{}", termion::cursor::Hide).unwrap();
+ write_alt_screen_msg(&mut screen);
+
+ screen.flush().unwrap();
+
+ for c in stdin.keys() {
+ match c.unwrap() {
+ Key::Char('q') => break,
+ Key::Char('1') => {
+ write!(screen, "{}", ToMainScreen).unwrap();
+ }
+ Key::Char('2') => {
+ write!(screen, "{}", ToAlternateScreen).unwrap();
+ write_alt_screen_msg(&mut screen);
+ }
+ _ => {}
+ }
+ screen.flush().unwrap();
+ }
+ write!(screen, "{}", termion::cursor::Show).unwrap();
+}
diff --git a/third_party/rust/termion/examples/async.rs b/third_party/rust/termion/examples/async.rs
new file mode 100644
index 0000000000..b16bb78618
--- /dev/null
+++ b/third_party/rust/termion/examples/async.rs
@@ -0,0 +1,39 @@
+extern crate termion;
+
+use termion::raw::IntoRawMode;
+use termion::async_stdin;
+use std::io::{Read, Write, stdout};
+use std::thread;
+use std::time::Duration;
+
+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();
+
+ loop {
+ write!(stdout, "{}", termion::clear::CurrentLine).unwrap();
+
+ let b = stdin.next();
+ write!(stdout, "\r{:?} <- This demonstrates the async read input char. Between each update a 100 ms. is waited, simply to demonstrate the async fashion. \n\r", b).unwrap();
+ if let Some(Ok(b'q')) = b {
+ break;
+ }
+
+ stdout.flush().unwrap();
+
+ thread::sleep(Duration::from_millis(50));
+ stdout.write_all(b"# ").unwrap();
+ stdout.flush().unwrap();
+ thread::sleep(Duration::from_millis(50));
+ stdout.write_all(b"\r #").unwrap();
+ write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
+ stdout.flush().unwrap();
+ }
+}
diff --git a/third_party/rust/termion/examples/click.rs b/third_party/rust/termion/examples/click.rs
new file mode 100644
index 0000000000..fe903d837f
--- /dev/null
+++ b/third_party/rust/termion/examples/click.rs
@@ -0,0 +1,35 @@
+extern crate termion;
+
+use termion::event::{Key, Event, MouseEvent};
+use termion::input::{TermRead, MouseTerminal};
+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();
+ 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();
+ }
+ _ => (),
+ }
+ }
+ _ => {}
+ }
+ stdout.flush().unwrap();
+ }
+}
diff --git a/third_party/rust/termion/examples/color.rs b/third_party/rust/termion/examples/color.rs
new file mode 100644
index 0000000000..16071be993
--- /dev/null
+++ b/third_party/rust/termion/examples/color.rs
@@ -0,0 +1,10 @@
+extern crate termion;
+
+use termion::{color, style};
+
+fn main() {
+ println!("{}Red", color::Fg(color::Red));
+ println!("{}Blue", color::Fg(color::Blue));
+ println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
+ println!("{}Just plain italic", style::Italic);
+}
diff --git a/third_party/rust/termion/examples/commie.rs b/third_party/rust/termion/examples/commie.rs
new file mode 100644
index 0000000000..32f8820e50
--- /dev/null
+++ b/third_party/rust/termion/examples/commie.rs
@@ -0,0 +1,51 @@
+extern crate termion;
+
+use termion::{clear, color, cursor};
+
+use std::{time, thread};
+
+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);
+ 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)));
+
+ state += 1;
+ state %= 8;
+
+ thread::sleep(time::Duration::from_millis(90));
+ }
+}
diff --git a/third_party/rust/termion/examples/detect_color.rs b/third_party/rust/termion/examples/detect_color.rs
new file mode 100644
index 0000000000..ecfdd5b45a
--- /dev/null
+++ b/third_party/rust/termion/examples/detect_color.rs
@@ -0,0 +1,19 @@
+extern crate termion;
+
+use termion::color::{DetectColors, AnsiValue, Bg};
+use termion::raw::IntoRawMode;
+use std::io::stdout;
+
+fn main() {
+ let count;
+ {
+ let mut term = stdout().into_raw_mode().unwrap();
+ count = term.available_colors().unwrap();
+ }
+
+ println!("This terminal supports {} colors.", count);
+ for i in 0..count {
+ print!("{} {}", Bg(AnsiValue(i as u8)), Bg(AnsiValue(0)));
+ }
+ println!();
+}
diff --git a/third_party/rust/termion/examples/is_tty.rs b/third_party/rust/termion/examples/is_tty.rs
new file mode 100644
index 0000000000..52d1bc1d2d
--- /dev/null
+++ b/third_party/rust/termion/examples/is_tty.rs
@@ -0,0 +1,11 @@
+extern crate termion;
+
+use std::fs;
+
+fn main() {
+ if termion::is_tty(&fs::File::create("/dev/stdout").unwrap()) {
+ println!("This is a TTY!");
+ } else {
+ println!("This is not a TTY :(");
+ }
+}
diff --git a/third_party/rust/termion/examples/keys.rs b/third_party/rust/termion/examples/keys.rs
new file mode 100644
index 0000000000..272ffd1b9a
--- /dev/null
+++ b/third_party/rust/termion/examples/keys.rs
@@ -0,0 +1,44 @@
+extern crate termion;
+
+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();
+ stdout.flush().unwrap();
+
+ for c in stdin.keys() {
+ write!(stdout,
+ "{}{}",
+ termion::cursor::Goto(1, 1),
+ termion::clear::CurrentLine)
+ .unwrap();
+
+ match c.unwrap() {
+ Key::Char('q') => break,
+ 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!("↑"),
+ Key::Down => println!("↓"),
+ Key::Backspace => println!("×"),
+ _ => {}
+ }
+ stdout.flush().unwrap();
+ }
+
+ write!(stdout, "{}", termion::cursor::Show).unwrap();
+}
diff --git a/third_party/rust/termion/examples/mouse.rs b/third_party/rust/termion/examples/mouse.rs
new file mode 100644
index 0000000000..cbe8baf464
--- /dev/null
+++ b/third_party/rust/termion/examples/mouse.rs
@@ -0,0 +1,46 @@
+extern crate termion;
+
+use termion::event::*;
+use termion::cursor::{self, DetectCursorPos};
+use termion::input::{TermRead, MouseTerminal};
+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();
+
+ 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();
+ }
+ }
+ }
+ _ => {}
+ }
+
+ stdout.flush().unwrap();
+ }
+}
diff --git a/third_party/rust/termion/examples/rainbow.rs b/third_party/rust/termion/examples/rainbow.rs
new file mode 100644
index 0000000000..4ee4000d01
--- /dev/null
+++ b/third_party/rust/termion/examples/rainbow.rs
@@ -0,0 +1,60 @@
+extern crate termion;
+
+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();
+
+ 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, "\n\r").unwrap();
+ }
+
+ writeln!(stdout, "{}b = {}", termion::style::Reset, blue).unwrap();
+}
+
+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();
+
+ let mut blue = 172u8;
+
+ for c in stdin.keys() {
+ match c.unwrap() {
+ Key::Up => {
+ blue = blue.saturating_add(4);
+ rainbow(&mut stdout, blue);
+ }
+ Key::Down => {
+ blue = blue.saturating_sub(4);
+ rainbow(&mut stdout, blue);
+ }
+ Key::Char('q') => break,
+ _ => {}
+ }
+ stdout.flush().unwrap();
+ }
+
+ write!(stdout, "{}", termion::cursor::Show).unwrap();
+}
diff --git a/third_party/rust/termion/examples/read.rs b/third_party/rust/termion/examples/read.rs
new file mode 100644
index 0000000000..8d53e1bf72
--- /dev/null
+++ b/third_party/rust/termion/examples/read.rs
@@ -0,0 +1,23 @@
+extern crate termion;
+
+use termion::input::TermRead;
+use std::io::{Write, stdout, stdin};
+
+fn main() {
+ let stdout = stdout();
+ let mut stdout = stdout.lock();
+ let stdin = stdin();
+ let mut stdin = stdin.lock();
+
+ stdout.write_all(b"password: ").unwrap();
+ stdout.flush().unwrap();
+
+ let pass = stdin.read_passwd(&mut stdout);
+
+ if let Ok(Some(pass)) = pass {
+ stdout.write_all(pass.as_bytes()).unwrap();
+ stdout.write_all(b"\n").unwrap();
+ } else {
+ stdout.write_all(b"Error\n").unwrap();
+ }
+}
diff --git a/third_party/rust/termion/examples/rustc_fun.rs b/third_party/rust/termion/examples/rustc_fun.rs
new file mode 100644
index 0000000000..734e89f42b
--- /dev/null
+++ b/third_party/rust/termion/examples/rustc_fun.rs
@@ -0,0 +1,24 @@
+extern crate termion;
+
+use termion::{color, style};
+
+fn main() {
+ println!("{lighgreen}-- src/test/ui/borrow-errors.rs at 82:18 --\n\
+ {red}error: {reset}{bold}two closures require unique access to `vec` at the same time {reset}{bold}{magenta}[E0524]{reset}\n\
+ {line_num_fg}{line_num_bg}79 {reset} let append = |e| {{\n\
+ {line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}first closure is constructed here\n\
+ {line_num_fg}{line_num_bg}80 {reset} vec.push(e)\n\
+ {line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}previous borrow occurs due to use of `vec` in closure\n\
+ {line_num_fg}{line_num_bg}84 {reset} }};\n\
+ {line_num_fg}{line_num_bg}85 {reset} }}\n\
+ {line_num_fg}{line_num_bg}{info_line}{reset} {red}^{reset} {error_fg}borrow from first closure ends here",
+ lighgreen = color::Fg(color::LightGreen),
+ red = color::Fg(color::Red),
+ bold = style::Bold,
+ reset = style::Reset,
+ magenta = color::Fg(color::Magenta),
+ line_num_bg = color::Bg(color::AnsiValue::grayscale(3)),
+ line_num_fg = color::Fg(color::AnsiValue::grayscale(18)),
+ info_line = "| ",
+ error_fg = color::Fg(color::AnsiValue::grayscale(17)))
+}
diff --git a/third_party/rust/termion/examples/simple.rs b/third_party/rust/termion/examples/simple.rs
new file mode 100644
index 0000000000..93ef1df3ab
--- /dev/null
+++ b/third_party/rust/termion/examples/simple.rs
@@ -0,0 +1,42 @@
+extern crate termion;
+
+use termion::color;
+use termion::raw::IntoRawMode;
+use std::io::{Read, Write, stdout, stdin};
+
+fn main() {
+ // Initialize 'em all.
+ let stdout = stdout();
+ let mut stdout = stdout.lock().into_raw_mode().unwrap();
+ 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();
+ stdout.flush().unwrap();
+
+ let mut bytes = stdin.bytes();
+ loop {
+ 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();
+
+ stdout.flush().unwrap();
+ }
+}
diff --git a/third_party/rust/termion/examples/size.rs b/third_party/rust/termion/examples/size.rs
new file mode 100644
index 0000000000..e91fa54308
--- /dev/null
+++ b/third_party/rust/termion/examples/size.rs
@@ -0,0 +1,7 @@
+extern crate termion;
+
+use termion::terminal_size;
+
+fn main() {
+ println!("Size is {:?}", terminal_size().unwrap())
+}
diff --git a/third_party/rust/termion/examples/truecolor.rs b/third_party/rust/termion/examples/truecolor.rs
new file mode 100644
index 0000000000..ba5c8685b8
--- /dev/null
+++ b/third_party/rust/termion/examples/truecolor.rs
@@ -0,0 +1,12 @@
+extern crate termion;
+
+use termion::{color, cursor, clear};
+use std::{thread, time};
+
+fn main() {
+ for r in 0..255 {
+ let c = color::Rgb(r, !r, 2 * ((r % 128) as i8 - 64).abs() as u8);
+ println!("{}{}{}wow", cursor::Goto(1, 1), color::Bg(c), clear::All);
+ thread::sleep(time::Duration::from_millis(100));
+ }
+}
diff --git a/third_party/rust/termion/logo.svg b/third_party/rust/termion/logo.svg
new file mode 100644
index 0000000000..39bc574b69
--- /dev/null
+++ b/third_party/rust/termion/logo.svg
@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="60.099598mm" height="18.291185mm" viewBox="0 0 212.95 64.81">
+ <style>
+ .blink { animation: blinker 3s linear infinite; } @keyframes blinker { 50% { opacity: 0; } }
+ </style>
+
+ <path d="M0 0h212.95v64.82H0z" opacity=".71"/>
+ <path fill="#f9f9f9" d="M12.24 17.8H34.5v3.33h-9.13v25.84H21.4V21.13h-9.16V17.8zm27 0h17.3v3.33H43.2v8.63h12.77v3.32h-12.8v10.57H56.9v3.32H39.24V17.8zM74.3 33.2q1.5.4 2.6 1.48 1.06 1.08 2.66 4.32l3.97 7.97H79.3l-3.5-7.37q-1.5-3.14-2.7-4.04-1.2-.92-3.13-.92H66.2v12.33h-3.96V17.8h8.13q4.8 0 7.36 2.17 2.56 2.17 2.56 6.27 0 2.9-1.6 4.73-1.6 1.82-4.4 2.23zm-8.1-12.15V31.4h4.32q2.83 0 4.22-1.27 1.4-1.27 1.4-3.9 0-2.5-1.5-3.83-1.46-1.35-4.27-1.35H66.2zm19-3.25h5.26l5.04 14.85 5.08-14.84h5.3V47h-3.66V21.2l-5.2 15.38h-2.98L88.82 21.2v25.77H85.2V17.8zm26.3 0h16.2v3.33h-6.12v22.52h6.1v3.32H111.5v-3.32h6.1V21.13h-6.1V17.8zm37.8 14.62q0-6.43-1.32-9.18-1.3-2.76-4.3-2.76t-4.33 2.76q-1.3 2.75-1.3 9.18 0 6.4 1.3 9.16 1.33 2.75 4.32 2.75 3 0 4.3-2.73 1.34-2.76 1.34-9.18zm4.13 0q0 7.6-2.42 11.36-2.4 3.75-7.3 3.75t-7.3-3.73q-2.4-3.73-2.4-11.38 0-7.64 2.4-11.4 2.4-3.74 7.35-3.74t7.34 3.75q2.42 3.75 2.42 11.4zm4.97-14.62h5l9.86 24v-24h3.8v29.17h-5l-9.84-24v24h-3.8V17.8z"/>
+ <path fill="#f9f9f9" d="M192.7 8.66v47.5h-3.93V8.66h3.94z" class="blink"/>
+</svg>
diff --git a/third_party/rust/termion/src/async.rs b/third_party/rust/termion/src/async.rs
new file mode 100644
index 0000000000..f58b0444c6
--- /dev/null
+++ b/third_party/rust/termion/src/async.rs
@@ -0,0 +1,78 @@
+use std::io::{self, Read};
+use std::sync::mpsc;
+use std::thread;
+
+use sys::tty::get_tty;
+
+/// Construct an asynchronous handle to the TTY standard input.
+///
+/// This allows you to read from standard input _without blocking_ the current thread.
+/// Specifically, it works by firing up another thread to handle the event stream, which will then
+/// be buffered in a mpsc queue, which will eventually be read by the current thread.
+///
+/// This will not read the piped standard input, but rather read from the TTY device, since reading
+/// asyncronized from piped input would rarely make sense. In other words, if you pipe standard
+/// output from another process, it won't be reflected in the stream returned by this function, as
+/// this represents the TTY device, and not the piped standard input.
+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;
+ }
+ });
+
+ AsyncReader { recv: recv }
+}
+
+/// An asynchronous reader.
+///
+/// This acts as any other stream, with the exception that reading from it won't block. Instead,
+/// the buffer will only be partially updated based on how much the internal buffer holds.
+pub struct AsyncReader {
+ /// The underlying mpsc receiver.
+ recv: mpsc::Receiver<io::Result<u8>>,
+}
+
+// FIXME: Allow constructing an async reader from an arbitrary stream.
+
+impl Read for AsyncReader {
+ /// Read from the byte stream.
+ ///
+ /// This will never block, but try to drain the event queue until empty. If the total number of
+ /// bytes written is lower than the buffer's length, the event queue is empty or that the event
+ /// stream halted.
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ let mut total = 0;
+
+ loop {
+ if total >= buf.len() {
+ break;
+ }
+
+ match self.recv.try_recv() {
+ Ok(Ok(b)) => {
+ buf[total] = b;
+ total += 1;
+ }
+ Ok(Err(e)) => return Err(e),
+ Err(_) => break,
+ }
+ }
+
+ Ok(total)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::io::Read;
+
+ #[test]
+ fn test_async_stdin() {
+ let stdin = async_stdin();
+ stdin.bytes().next();
+ }
+}
diff --git a/third_party/rust/termion/src/clear.rs b/third_party/rust/termion/src/clear.rs
new file mode 100644
index 0000000000..d37f2c6b91
--- /dev/null
+++ b/third_party/rust/termion/src/clear.rs
@@ -0,0 +1,9 @@
+//! Clearing the screen.
+
+use std::fmt;
+
+derive_csi_sequence!("Clear the entire screen.", All, "2J");
+derive_csi_sequence!("Clear everything after the cursor.", AfterCursor, "J");
+derive_csi_sequence!("Clear everything before the cursor.", BeforeCursor, "1J");
+derive_csi_sequence!("Clear the current line.", CurrentLine, "2K");
+derive_csi_sequence!("Clear from cursor to newline.", UntilNewline, "K");
diff --git a/third_party/rust/termion/src/color.rs b/third_party/rust/termion/src/color.rs
new file mode 100644
index 0000000000..6f3fd88138
--- /dev/null
+++ b/third_party/rust/termion/src/color.rs
@@ -0,0 +1,242 @@
+//! Color managemement.
+//!
+//! # Example
+//!
+//! ```rust
+//! use termion::color;
+//!
+//! fn main() {
+//! println!("{}Red", color::Fg(color::Red));
+//! println!("{}Blue", color::Fg(color::Blue));
+//! println!("{}Back again", color::Fg(color::Reset));
+//! }
+//! ```
+
+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;
+
+/// A terminal color.
+pub trait Color {
+ /// Write the foreground version of this color.
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
+ /// Write the background version of this color.
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
+}
+
+macro_rules! derive_color {
+ ($doc:expr, $name:ident, $value:expr) => {
+ #[doc = $doc]
+ #[derive(Copy, Clone, Debug)]
+ pub struct $name;
+
+ impl Color for $name {
+ #[inline]
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("38;5;", $value, "m"))
+ }
+
+ #[inline]
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("48;5;", $value, "m"))
+ }
+ }
+ };
+}
+
+derive_color!("Black.", Black, "0");
+derive_color!("Red.", Red, "1");
+derive_color!("Green.", Green, "2");
+derive_color!("Yellow.", Yellow, "3");
+derive_color!("Blue.", Blue, "4");
+derive_color!("Magenta.", Magenta, "5");
+derive_color!("Cyan.", Cyan, "6");
+derive_color!("White.", White, "7");
+derive_color!("High-intensity light black.", LightBlack, "8");
+derive_color!("High-intensity light red.", LightRed, "9");
+derive_color!("High-intensity light green.", LightGreen, "10");
+derive_color!("High-intensity light yellow.", LightYellow, "11");
+derive_color!("High-intensity light blue.", LightBlue, "12");
+derive_color!("High-intensity light magenta.", LightMagenta, "13");
+derive_color!("High-intensity light cyan.", LightCyan, "14");
+derive_color!("High-intensity light white.", LightWhite, "15");
+
+impl<'a> Color for &'a Color {
+ #[inline]
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ (*self).write_fg(f)
+ }
+
+ #[inline]
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ (*self).write_bg(f)
+ }
+}
+
+/// An arbitrary ANSI color value.
+#[derive(Clone, Copy, Debug)]
+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);
+
+ AnsiValue(16 + 36 * r + 6 * g + b)
+ }
+
+ /// Grayscale color.
+ ///
+ /// 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);
+
+ AnsiValue(0xE8 + shade)
+ }
+}
+
+impl Color for AnsiValue {
+ #[inline]
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("38;5;{}m"), self.0)
+ }
+
+ #[inline]
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("48;5;{}m"), self.0)
+ }
+}
+
+/// A truecolor RGB.
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub struct Rgb(pub u8, pub u8, pub u8);
+
+impl Color for Rgb {
+ #[inline]
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("38;2;{};{};{}m"), self.0, self.1, self.2)
+ }
+
+ #[inline]
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("48;2;{};{};{}m"), self.0, self.1, self.2)
+ }
+}
+
+/// Reset colors to defaults.
+#[derive(Debug, Clone, Copy)]
+pub struct Reset;
+
+impl Color for Reset {
+ #[inline]
+ fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("39m"))
+ }
+
+ #[inline]
+ fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("49m"))
+ }
+}
+
+/// A foreground color.
+#[derive(Debug, Clone, Copy)]
+pub struct Fg<C: Color>(pub C);
+
+impl<C: Color> fmt::Display for Fg<C> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.write_fg(f)
+ }
+}
+
+/// A background color.
+#[derive(Debug, Clone, Copy)]
+pub struct Bg<C: Color>(pub C);
+
+impl<C: Color> fmt::Display for Bg<C> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.0.write_bg(f)
+ }
+}
+
+/// Types that allow detection of the colors they support.
+pub trait DetectColors {
+ /// How many ANSI colors are supported (from 8 to 256)?
+ ///
+ /// Beware: the information given isn't authoritative, it's infered through escape codes or the
+ /// value of `TERM`, more colors may be available.
+ fn available_colors(&mut self) -> io::Result<u16>;
+}
+
+impl<W: Write> DetectColors for W {
+ fn available_colors(&mut self) -> io::Result<u16> {
+ let mut stdin = async_stdin();
+
+ if detect_color(self, &mut stdin, 0)? {
+ // OSC 4 is supported, detect how many colors there are.
+ // Do a binary search of the last supported color.
+ let mut min = 8;
+ let mut max = 256;
+ let mut i;
+ while min + 1 < max {
+ i = (min + max) / 2;
+ if detect_color(self, &mut stdin, i)? {
+ min = i
+ } else {
+ max = i
+ }
+ }
+ Ok(max)
+ } 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,
+ })
+ }
+ }
+}
+
+/// Detect a color using OSC 4.
+fn detect_color(stdout: &mut Write, stdin: &mut Read, color: u16) -> io::Result<bool> {
+ // Is the color available?
+ // Use `ESC ] 4 ; color ; ? BEL`.
+ write!(stdout, "\x1B]4;{};?\x07", color)?;
+ stdout.flush()?;
+
+ let mut buf: [u8; 1] = [0];
+ let mut total_read = 0;
+
+ let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
+ let now = SystemTime::now();
+ let bell = 7u8;
+
+ // Either consume all data up to bell or wait for a timeout.
+ while buf[0] != bell && now.elapsed().unwrap() < timeout {
+ total_read += stdin.read(&mut buf)?;
+ }
+
+ // If there was a response, the color is supported.
+ Ok(total_read > 0)
+}
diff --git a/third_party/rust/termion/src/cursor.rs b/third_party/rust/termion/src/cursor.rs
new file mode 100644
index 0000000000..6296da9b10
--- /dev/null
+++ b/third_party/rust/termion/src/cursor.rs
@@ -0,0 +1,140 @@
+//! Cursor movement.
+
+use std::fmt;
+use std::io::{self, Write, Error, ErrorKind, Read};
+use async::async_stdin;
+use std::time::{SystemTime, Duration};
+use raw::CONTROL_SEQUENCE_TIMEOUT;
+
+derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
+derive_csi_sequence!("Show the cursor.", Show, "?25h");
+
+derive_csi_sequence!("Restore the cursor.", Restore, "u");
+derive_csi_sequence!("Save the cursor.", Save, "s");
+
+/// Goto some position ((1,1)-based).
+///
+/// # Why one-based?
+///
+/// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
+/// can be quite strange at first, but it is not that big of an obstruction once you get used to
+/// it.
+///
+/// # Example
+///
+/// ```rust
+/// extern crate termion;
+///
+/// fn main() {
+/// print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(5, 3));
+/// }
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Goto(pub u16, pub u16);
+
+impl Default for Goto {
+ fn default() -> Goto {
+ Goto(1, 1)
+ }
+}
+
+impl fmt::Display for Goto {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
+
+ write!(f, csi!("{};{}H"), self.1, self.0)
+ }
+}
+
+/// Move cursor left.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Left(pub u16);
+
+impl fmt::Display for Left {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}D"), self.0)
+ }
+}
+
+/// Move cursor right.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Right(pub u16);
+
+impl fmt::Display for Right {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}C"), self.0)
+ }
+}
+
+/// Move cursor up.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Up(pub u16);
+
+impl fmt::Display for Up {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}A"), self.0)
+ }
+}
+
+/// Move cursor down.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Down(pub u16);
+
+impl fmt::Display for Down {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}B"), self.0)
+ }
+}
+
+/// Types that allow detection of the cursor position.
+pub trait DetectCursorPos {
+ /// Get the (1,1)-based cursor position from the terminal.
+ fn cursor_pos(&mut self) -> io::Result<(u16, u16)>;
+}
+
+impl<W: Write> DetectCursorPos for W {
+ fn cursor_pos(&mut self) -> io::Result<(u16, u16)> {
+ let mut stdin = async_stdin();
+
+ // Where is the cursor?
+ // Use `ESC [ 6 n`.
+ write!(self, "\x1B[6n")?;
+ self.flush()?;
+
+ let mut buf: [u8; 1] = [0];
+ let mut read_chars = Vec::new();
+
+ let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
+ let now = SystemTime::now();
+
+ // Either consume all data up to R or wait for a timeout.
+ while buf[0] != b'R' && now.elapsed().unwrap() < timeout {
+ if stdin.read(&mut buf)? > 0 {
+ read_chars.push(buf[0]);
+ }
+ }
+
+ if read_chars.len() == 0 {
+ return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out."));
+ }
+
+ // The answer will look like `ESC [ Cy ; Cx R`.
+
+ read_chars.pop(); // remove trailing R.
+ let read_str = String::from_utf8(read_chars).unwrap();
+ let beg = read_str.rfind('[').unwrap();
+ 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();
+
+ Ok((cx, cy))
+ }
+}
diff --git a/third_party/rust/termion/src/event.rs b/third_party/rust/termion/src/event.rs
new file mode 100644
index 0000000000..f79cb4c11f
--- /dev/null
+++ b/third_party/rust/termion/src/event.rs
@@ -0,0 +1,351 @@
+//! Mouse and key events.
+
+use std::io::{Error, ErrorKind};
+use std::ascii::AsciiExt;
+use std::str;
+
+/// An event reported by the terminal.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Event {
+ /// A key press.
+ Key(Key),
+ /// A mouse button press, release or wheel use at specific coordinates.
+ Mouse(MouseEvent),
+ /// An event that cannot currently be evaluated.
+ Unsupported(Vec<u8>),
+}
+
+/// A mouse related event.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum MouseEvent {
+ /// A mouse button was pressed.
+ ///
+ /// The coordinates are one-based.
+ Press(MouseButton, u16, u16),
+ /// A mouse button was released.
+ ///
+ /// The coordinates are one-based.
+ Release(u16, u16),
+ /// A mouse button is held over the given coordinates.
+ ///
+ /// The coordinates are one-based.
+ Hold(u16, u16),
+}
+
+/// A mouse button.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum MouseButton {
+ /// The left mouse button.
+ Left,
+ /// The right mouse button.
+ Right,
+ /// The middle mouse button.
+ Middle,
+ /// Mouse wheel is going up.
+ ///
+ /// This event is typically only used with Mouse::Press.
+ WheelUp,
+ /// Mouse wheel is going down.
+ ///
+ /// This event is typically only used with Mouse::Press.
+ WheelDown,
+}
+
+/// A key.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Key {
+ /// Backspace.
+ Backspace,
+ /// Left arrow.
+ Left,
+ /// Right arrow.
+ Right,
+ /// Up arrow.
+ Up,
+ /// Down arrow.
+ Down,
+ /// Home key.
+ Home,
+ /// End key.
+ End,
+ /// Page Up key.
+ PageUp,
+ /// Page Down key.
+ PageDown,
+ /// Delete key.
+ Delete,
+ /// Insert key.
+ Insert,
+ /// Function keys.
+ ///
+ /// Only function keys 1 through 12 are supported.
+ F(u8),
+ /// Normal character.
+ Char(char),
+ /// Alt modified character.
+ Alt(char),
+ /// Ctrl modified character.
+ ///
+ /// Note that certain keys may not be modifiable with `ctrl`, due to limitations of terminals.
+ Ctrl(char),
+ /// Null byte.
+ Null,
+ /// Esc key.
+ Esc,
+
+ #[doc(hidden)]
+ __IsNotComplete,
+}
+
+/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
+pub fn parse_event<I>(item: u8, iter: &mut I) -> Result<Event, Error>
+ where I: Iterator<Item = Result<u8, Error>>
+{
+ 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),
+ }
+ }
+ 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(Err(_)) | None => return Err(error),
+ })
+ }
+ b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))),
+ b'\t' => Ok(Event::Key(Key::Char('\t'))),
+ b'\x7F' => Ok(Event::Key(Key::Backspace)),
+ 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)))
+ })
+ }
+ }
+}
+
+/// Parses a CSI sequence, just after reading ^[
+///
+/// Returns None if an unrecognized sequence is found.
+fn parse_csi<I>(iter: &mut I) -> Option<Event>
+ where I: Iterator<Item = Result<u8, Error>>
+{
+ 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')) => {
+ // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only).
+ let mut next = || iter.next().unwrap().unwrap();
+
+ 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'<')) => {
+ // 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::<u16>()
+ .unwrap();
+ let cx = nums.next()
+ .unwrap()
+ .parse::<u16>()
+ .unwrap();
+ let cy = nums.next()
+ .unwrap()
+ .parse::<u16>()
+ .unwrap();
+
+ 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,
+ }
+ }
+ 32 => MouseEvent::Hold(cx, cy),
+ 3 => MouseEvent::Release(cx, cy),
+ _ => return None,
+ };
+
+ 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();
+ }
+
+ match c {
+ // rxvt mouse encoding:
+ // ESC [ Cb ; Cx ; Cy ; M
+ b'M' => {
+ let str_buf = String::from_utf8(buf).unwrap();
+
+ let nums: Vec<u16> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
+
+ let cb = nums[0];
+ let cx = nums[1];
+ let cy = nums[2];
+
+ 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,
+ };
+
+ Event::Mouse(event)
+ }
+ // Special key code.
+ b'~' => {
+ let str_buf = String::from_utf8(buf).unwrap();
+
+ // This CSI sequence can be a list of semicolon-separated
+ // numbers.
+ let nums: Vec<u8> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
+
+ if nums.is_empty() {
+ return None;
+ }
+
+ // TODO: handle multiple values for key modififiers (ex: values
+ // [3, 2] means Shift+Delete)
+ if nums.len() > 1 {
+ return None;
+ }
+
+ 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,
+ }
+ }
+ _ => return None,
+ }
+ }
+ _ => return None,
+ })
+
+}
+
+/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char.
+fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
+ where I: Iterator<Item = Result<u8, Error>>
+{
+ 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 {
+ match iter.next() {
+ Some(Ok(next)) => {
+ bytes.push(next);
+ if let Ok(st) = str::from_utf8(bytes) {
+ return Ok(st.chars().next().unwrap());
+ }
+ if bytes.len() >= 4 {
+ return error;
+ }
+ }
+ _ => return error,
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+#[test]
+fn test_parse_utf8() {
+ let st = "abcéŷ¤£€ù%323";
+ let ref mut bytes = st.bytes().map(|x| Ok(x));
+ let chars = st.chars();
+ for c in chars {
+ let b = bytes.next().unwrap().unwrap();
+ assert!(c == parse_utf8_char(b, bytes).unwrap());
+ }
+}
diff --git a/third_party/rust/termion/src/input.rs b/third_party/rust/termion/src/input.rs
new file mode 100644
index 0000000000..6f6dd171f3
--- /dev/null
+++ b/third_party/rust/termion/src/input.rs
@@ -0,0 +1,388 @@
+//! User input.
+
+use std::io::{self, Read, Write};
+use std::ops;
+
+use event::{self, Event, Key};
+use raw::IntoRawMode;
+
+/// An iterator over input keys.
+pub struct Keys<R> {
+ iter: Events<R>,
+}
+
+impl<R: Read> Iterator for Keys<R> {
+ type Item = Result<Key, io::Error>;
+
+ fn next(&mut self) -> Option<Result<Key, io::Error>> {
+ loop {
+ match self.iter.next() {
+ Some(Ok(Event::Key(k))) => return Some(Ok(k)),
+ Some(Ok(_)) => continue,
+ e @ Some(Err(_)) => e,
+ None => return None,
+ };
+ }
+ }
+}
+
+/// An iterator over input events.
+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))
+ }
+}
+
+/// An iterator over input events and the bytes that define them.
+pub struct EventsAndRaw<R> {
+ source: R,
+ leftover: Option<u8>,
+}
+
+impl<R: Read> Iterator for EventsAndRaw<R> {
+ type Item = Result<(Event, Vec<u8>), io::Error>;
+
+ fn next(&mut self) -> Option<Result<(Event, Vec<u8>), io::Error>> {
+ 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(c, &mut source.bytes()));
+ }
+
+ // 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), vec![b'\x1B'])),
+ c => parse_event(c, &mut source.bytes()),
+ }
+ }
+ Ok(2) => {
+ let mut option_iter = &mut Some(buf[1]).into_iter();
+ let result = {
+ let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes());
+ parse_event(buf[0], &mut iter)
+ };
+ // If the option_iter wasn't consumed, keep the byte for later.
+ self.leftover = option_iter.next();
+ result
+ }
+ Ok(_) => unreachable!(),
+ Err(e) => Err(e),
+ };
+
+ Some(res)
+ }
+}
+
+fn parse_event<I>(item: u8, iter: &mut I) -> Result<(Event, Vec<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);
+ });
+ event::parse_event(item, &mut iter)
+ };
+ 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;
+
+ /// An iterator over key inputs.
+ fn keys(self) -> Keys<Self> where Self: Sized;
+
+ /// Read a line.
+ ///
+ /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
+ /// complete the input.
+ fn read_line(&mut self) -> io::Result<Option<String>>;
+
+ /// Read a password.
+ ///
+ /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
+ /// complete the input.
+ fn read_passwd<W: Write>(&mut self, writer: &mut W) -> io::Result<Option<String>> {
+ let _raw = try!(writer.into_raw_mode());
+ self.read_line()
+ }
+}
+
+
+impl<R: Read + TermReadEventsAndRaw> TermRead for R {
+ fn events(self) -> Events<Self> {
+ Events {
+ inner: self.events_and_raw()
+ }
+ }
+ fn keys(self) -> Keys<Self> {
+ Keys { iter: self.events() }
+ }
+
+ fn read_line(&mut self) -> io::Result<Option<String>> {
+ let mut buf = Vec::with_capacity(30);
+
+ for c in self.bytes() {
+ match c {
+ Err(e) => return Err(e),
+ Ok(0) | Ok(3) | Ok(4) => return Ok(None),
+ Ok(0x7f) => {
+ buf.pop();
+ }
+ Ok(b'\n') | Ok(b'\r') => break,
+ Ok(c) => buf.push(c),
+ }
+ }
+
+ let string = try!(String::from_utf8(buf)
+ .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)));
+ Ok(Some(string))
+ }
+}
+
+/// 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;
+}
+
+impl<R: Read> TermReadEventsAndRaw for R {
+ fn events_and_raw(self) -> EventsAndRaw<Self> {
+ EventsAndRaw {
+ source: self,
+ leftover: None,
+ }
+ }
+}
+
+/// A sequence of escape codes to enable terminal mouse support.
+const ENTER_MOUSE_SEQUENCE: &'static str = csi!("?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h");
+
+/// A sequence of escape codes to disable terminal mouse support.
+const EXIT_MOUSE_SEQUENCE: &'static str = csi!("?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l");
+
+/// A terminal with added mouse support.
+///
+/// This can be obtained through the `From` implementations.
+pub struct MouseTerminal<W: Write> {
+ term: W,
+}
+
+impl<W: Write> From<W> for MouseTerminal<W> {
+ fn from(mut from: W) -> MouseTerminal<W> {
+ from.write_all(ENTER_MOUSE_SEQUENCE.as_bytes()).unwrap();
+
+ MouseTerminal { term: from }
+ }
+}
+
+impl<W: Write> Drop for MouseTerminal<W> {
+ fn drop(&mut self) {
+ self.term.write_all(EXIT_MOUSE_SEQUENCE.as_bytes()).unwrap();
+ }
+}
+
+impl<W: Write> ops::Deref for MouseTerminal<W> {
+ type Target = W;
+
+ fn deref(&self) -> &W {
+ &self.term
+ }
+}
+
+impl<W: Write> ops::DerefMut for MouseTerminal<W> {
+ fn deref_mut(&mut self) -> &mut W {
+ &mut self.term
+ }
+}
+
+impl<W: Write> Write for MouseTerminal<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.term.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.term.flush()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::io;
+ use event::{Key, Event, MouseEvent, MouseButton};
+
+ #[test]
+ fn test_keys() {
+ let mut i = b"\x1Bayo\x7F\x1B[D".keys();
+
+ assert_eq!(i.next().unwrap().unwrap(), Key::Alt('a'));
+ assert_eq!(i.next().unwrap().unwrap(), Key::Char('y'));
+ assert_eq!(i.next().unwrap().unwrap(), Key::Char('o'));
+ assert_eq!(i.next().unwrap().unwrap(), Key::Backspace);
+ assert_eq!(i.next().unwrap().unwrap(), Key::Left);
+ assert!(i.next().is_none());
+ }
+
+ #[test]
+ fn test_events() {
+ 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();
+
+ 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::Key(Key::Char('b')));
+ assert!(i.next().is_none());
+ }
+
+ #[test]
+ fn test_events_and_raw() {
+ let input = 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";
+ 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]));
+ 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::Key(Key::Char('b')));
+ assert!(i.next().is_none());
+ }
+
+ assert_eq!(input.iter().map(|b| *b).collect::<Vec<u8>>(), output)
+ }
+
+ #[test]
+ fn test_function_keys() {
+ let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".keys();
+ for i in 1..5 {
+ assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
+ }
+
+ 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();
+ for i in 1..13 {
+ assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
+ }
+ }
+
+ #[test]
+ fn test_special_keys() {
+ let mut st = b"\x1B[2~\x1B[H\x1B[7~\x1B[5~\x1B[3~\x1B[F\x1B[8~\x1B[6~".keys();
+ assert_eq!(st.next().unwrap().unwrap(), Key::Insert);
+ assert_eq!(st.next().unwrap().unwrap(), Key::Home);
+ assert_eq!(st.next().unwrap().unwrap(), Key::Home);
+ assert_eq!(st.next().unwrap().unwrap(), Key::PageUp);
+ assert_eq!(st.next().unwrap().unwrap(), Key::Delete);
+ assert_eq!(st.next().unwrap().unwrap(), Key::End);
+ assert_eq!(st.next().unwrap().unwrap(), Key::End);
+ assert_eq!(st.next().unwrap().unwrap(), Key::PageDown);
+ 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();
+
+ let line = a.as_bytes().read_line().unwrap();
+ let pass = a.as_bytes().read_passwd(&mut sink).unwrap();
+
+ // godammit rustc
+
+ assert_eq!(line, pass);
+
+ if let Some(l) = line {
+ assert_eq!(Some(l.as_str()), b);
+ } else {
+ assert!(b.is_none());
+ }
+ }
+
+ #[test]
+ fn test_read() {
+ let test1 = "this is the first test";
+ let test2 = "this is the second test";
+
+ line_match(test1, Some(test1));
+ line_match(test2, Some(test2));
+ }
+
+ #[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"));
+ }
+
+ #[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"));
+ }
+
+ #[test]
+ fn test_abort() {
+ line_match("abc\x03https://www.youtube.com/watch?v=dQw4w9WgXcQ", None);
+ line_match("hello\x04https://www.youtube.com/watch?v=yPYZpwSpKmA", None);
+ }
+
+}
diff --git a/third_party/rust/termion/src/lib.rs b/third_party/rust/termion/src/lib.rs
new file mode 100644
index 0000000000..b284eddd16
--- /dev/null
+++ b/third_party/rust/termion/src/lib.rs
@@ -0,0 +1,61 @@
+//! Termion is a pure Rust, bindless library for low-level handling, manipulating
+//! and reading information about terminals. This provides a full-featured
+//! alternative to Termbox.
+//!
+//! Termion aims to be simple and yet expressive. It is bindless, meaning that it
+//! is not a front-end to some other library (e.g., ncurses or termbox), but a
+//! standalone library directly talking to the TTY.
+//!
+//! Supports Redox, Mac OS X, and Linux (or, in general, ANSI terminals).
+//!
+//! For more information refer to the [README](https://github.com/ticki/termion).
+#![warn(missing_docs)]
+
+#[cfg(target_os = "redox")]
+#[path="sys/redox/mod.rs"]
+mod sys;
+
+#[cfg(unix)]
+#[path="sys/unix/mod.rs"]
+mod sys;
+
+pub use sys::size::terminal_size;
+pub use sys::tty::{is_tty, get_tty};
+
+mod async;
+pub use async::{AsyncReader, async_stdin};
+
+#[macro_use]
+mod macros;
+pub mod clear;
+pub mod color;
+pub mod cursor;
+pub mod event;
+pub mod input;
+pub mod raw;
+pub mod screen;
+pub mod scroll;
+pub mod style;
+
+#[cfg(test)]
+mod test {
+ use super::sys;
+
+ #[test]
+ fn test_get_terminal_attr() {
+ sys::attr::get_terminal_attr().unwrap();
+ sys::attr::get_terminal_attr().unwrap();
+ sys::attr::get_terminal_attr().unwrap();
+ }
+
+ #[test]
+ fn test_set_terminal_attr() {
+ let ios = sys::attr::get_terminal_attr().unwrap();
+ sys::attr::set_terminal_attr(&ios).unwrap();
+ }
+
+ #[test]
+ fn test_size() {
+ sys::size::terminal_size().unwrap();
+ }
+}
diff --git a/third_party/rust/termion/src/macros.rs b/third_party/rust/termion/src/macros.rs
new file mode 100644
index 0000000000..28370e36b4
--- /dev/null
+++ b/third_party/rust/termion/src/macros.rs
@@ -0,0 +1,19 @@
+/// Create a CSI-introduced sequence.
+macro_rules! csi {
+ ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
+}
+
+/// Derive a CSI sequence struct.
+macro_rules! derive_csi_sequence {
+ ($doc:expr, $name:ident, $value:expr) => {
+ #[doc = $doc]
+ #[derive(Copy, Clone)]
+ pub struct $name;
+
+ impl fmt::Display for $name {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!($value))
+ }
+ }
+ };
+}
diff --git a/third_party/rust/termion/src/raw.rs b/third_party/rust/termion/src/raw.rs
new file mode 100644
index 0000000000..5421d56591
--- /dev/null
+++ b/third_party/rust/termion/src/raw.rs
@@ -0,0 +1,117 @@
+//! Managing raw mode.
+//!
+//! Raw mode is a particular state a TTY can have. It signifies that:
+//!
+//! 1. No line buffering (the input is given byte-by-byte).
+//! 2. The input is not written out, instead it has to be done manually by the programmer.
+//! 3. The output is not canonicalized (for example, `\n` means "go one line down", not "line
+//! break").
+//!
+//! It is essential to design terminal programs.
+//!
+//! # Example
+//!
+//! ```rust,no_run
+//! use termion::raw::IntoRawMode;
+//! use std::io::{Write, stdout};
+//!
+//! fn main() {
+//! let mut stdout = stdout().into_raw_mode().unwrap();
+//!
+//! write!(stdout, "Hey there.").unwrap();
+//! }
+//! ```
+
+use std::io::{self, Write};
+use std::ops;
+
+use sys::Termios;
+use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
+
+/// The timeout of an escape code control sequence, in milliseconds.
+pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
+
+/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
+/// dropped.
+///
+/// Restoring will entirely bring back the old TTY state.
+pub struct RawTerminal<W: Write> {
+ prev_ios: Termios,
+ output: W,
+}
+
+impl<W: Write> Drop for RawTerminal<W> {
+ fn drop(&mut self) {
+ set_terminal_attr(&self.prev_ios).unwrap();
+ }
+}
+
+impl<W: Write> ops::Deref for RawTerminal<W> {
+ type Target = W;
+
+ fn deref(&self) -> &W {
+ &self.output
+ }
+}
+
+impl<W: Write> ops::DerefMut for RawTerminal<W> {
+ fn deref_mut(&mut self) -> &mut W {
+ &mut self.output
+ }
+}
+
+impl<W: Write> Write for RawTerminal<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.output.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.output.flush()
+ }
+}
+
+/// Types which can be converted into "raw mode".
+///
+/// # Why is this type defined on writers and not readers?
+///
+/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the
+/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well.
+pub trait IntoRawMode: Write + Sized {
+ /// Switch to raw mode.
+ ///
+ /// Raw mode means that stdin won't be printed (it will instead have to be written manually by
+ /// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can
+ /// read from stdin one byte of a time). The output is neither modified in any way.
+ fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
+}
+
+impl<W: Write> IntoRawMode for W {
+ fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
+ let mut ios = get_terminal_attr()?;
+ let prev_ios = ios;
+
+ raw_terminal_attr(&mut ios);
+
+ set_terminal_attr(&ios)?;
+
+ Ok(RawTerminal {
+ prev_ios: prev_ios,
+ output: self,
+ })
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::io::{Write, stdout};
+
+ #[test]
+ fn test_into_raw_mode() {
+ let mut out = stdout().into_raw_mode().unwrap();
+
+ out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
+
+ drop(out);
+ }
+}
diff --git a/third_party/rust/termion/src/screen.rs b/third_party/rust/termion/src/screen.rs
new file mode 100644
index 0000000000..822399e27c
--- /dev/null
+++ b/third_party/rust/termion/src/screen.rs
@@ -0,0 +1,91 @@
+//! Managing switching between main and alternate screen buffers.
+//!
+//! Note that this implementation uses xterm's new escape sequences for screen switching and thus
+//! only works for xterm compatible terminals (which should be most terminals nowadays).
+//!
+//! # Example
+//!
+//! ```rust
+//! use termion::screen::AlternateScreen;
+//! use std::io::{Write, stdout};
+//!
+//! fn main() {
+//! {
+//! let mut screen = AlternateScreen::from(stdout());
+//! write!(screen, "Writing to alternate screen!").unwrap();
+//! screen.flush().unwrap();
+//! }
+//! println!("Writing to main screen.");
+//! }
+//! ```
+
+use std::io::{self, Write};
+use std::ops;
+use std::fmt;
+
+/// Switch to the main screen buffer of the terminal.
+pub struct ToMainScreen;
+
+impl fmt::Display for ToMainScreen {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("?1049l"))
+ }
+}
+
+/// Switch to the alternate screen buffer of the terminal.
+pub struct ToAlternateScreen;
+
+impl fmt::Display for ToAlternateScreen {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("?1049h"))
+ }
+}
+
+/// A terminal restorer, which wraps a type implementing Write, and causes all writes to be written
+/// to an alternate screen.
+///
+/// This is achieved by switching the terminal to the alternate screen on creation and
+/// automatically switching it back to the original screen on drop.
+pub struct AlternateScreen<W: Write> {
+ /// The output target.
+ output: W,
+}
+
+impl<W: Write> AlternateScreen<W> {
+ /// Create an alternate screen wrapper struct for the provided output and switch the terminal
+ /// to the alternate screen.
+ pub fn from(mut output: W) -> Self {
+ write!(output, "{}", ToAlternateScreen).expect("switch to alternate screen");
+ AlternateScreen { output: output }
+ }
+}
+
+impl<W: Write> Drop for AlternateScreen<W> {
+ fn drop(&mut self) {
+ write!(self, "{}", ToMainScreen).expect("switch to main screen");
+ }
+}
+
+impl<W: Write> ops::Deref for AlternateScreen<W> {
+ type Target = W;
+
+ fn deref(&self) -> &W {
+ &self.output
+ }
+}
+
+impl<W: Write> ops::DerefMut for AlternateScreen<W> {
+ fn deref_mut(&mut self) -> &mut W {
+ &mut self.output
+ }
+}
+
+impl<W: Write> Write for AlternateScreen<W> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.output.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.output.flush()
+ }
+}
diff --git a/third_party/rust/termion/src/scroll.rs b/third_party/rust/termion/src/scroll.rs
new file mode 100644
index 0000000000..2744507f3c
--- /dev/null
+++ b/third_party/rust/termion/src/scroll.rs
@@ -0,0 +1,23 @@
+//! Scrolling.
+
+use std::fmt;
+
+/// Scroll up.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Up(pub u16);
+
+impl fmt::Display for Up {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}S"), self.0)
+ }
+}
+
+/// Scroll down.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Down(pub u16);
+
+impl fmt::Display for Down {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, csi!("{}T"), self.0)
+ }
+}
diff --git a/third_party/rust/termion/src/style.rs b/third_party/rust/termion/src/style.rs
new file mode 100644
index 0000000000..58e9a78b87
--- /dev/null
+++ b/third_party/rust/termion/src/style.rs
@@ -0,0 +1,22 @@
+//! Text styling management.
+
+use std::fmt;
+
+derive_csi_sequence!("Reset SGR parameters.", Reset, "m");
+derive_csi_sequence!("Bold text.", Bold, "1m");
+derive_csi_sequence!("Fainted text (not widely supported).", Faint, "2m");
+derive_csi_sequence!("Italic text.", Italic, "3m");
+derive_csi_sequence!("Underlined text.", Underline, "4m");
+derive_csi_sequence!("Blinking text (not widely supported).", Blink, "5m");
+derive_csi_sequence!("Inverted colors (negative mode).", Invert, "7m");
+derive_csi_sequence!("Crossed out text (not widely supported).", CrossedOut, "9m");
+derive_csi_sequence!("Undo bold text.", NoBold, "21m");
+derive_csi_sequence!("Undo fainted text (not widely supported).", NoFaint, "22m");
+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!("Framed text (not widely supported).", Framed, "51m");
diff --git a/third_party/rust/termion/src/sys/redox/attr.rs b/third_party/rust/termion/src/sys/redox/attr.rs
new file mode 100644
index 0000000000..c6489a510c
--- /dev/null
+++ b/third_party/rust/termion/src/sys/redox/attr.rs
@@ -0,0 +1,33 @@
+use std::io;
+
+use super::{cvt, syscall, Termios};
+
+pub fn get_terminal_attr() -> io::Result<Termios> {
+ let mut termios = Termios::default();
+
+ let fd = cvt(syscall::dup(0, b"termios"))?;
+ let res = cvt(syscall::read(fd, &mut termios));
+ let _ = syscall::close(fd);
+
+ if res? == termios.len() {
+ Ok(termios)
+ } else {
+ Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes."))
+ }
+}
+
+pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
+ let fd = cvt(syscall::dup(0, b"termios"))?;
+ let res = cvt(syscall::write(fd, termios));
+ let _ = syscall::close(fd);
+
+ if res? == termios.len() {
+ Ok(())
+ } else {
+ Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes."))
+ }
+}
+
+pub fn raw_terminal_attr(ios: &mut Termios) {
+ ios.make_raw()
+}
diff --git a/third_party/rust/termion/src/sys/redox/mod.rs b/third_party/rust/termion/src/sys/redox/mod.rs
new file mode 100644
index 0000000000..2a9b875e32
--- /dev/null
+++ b/third_party/rust/termion/src/sys/redox/mod.rs
@@ -0,0 +1,15 @@
+extern crate redox_termios;
+extern crate syscall;
+
+use std::io;
+
+pub use self::redox_termios::Termios;
+
+pub mod attr;
+pub mod size;
+pub mod tty;
+
+// Support function for converting syscall error to io error
+fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
+ result.map_err(|err| io::Error::from_raw_os_error(err.errno))
+}
diff --git a/third_party/rust/termion/src/sys/redox/size.rs b/third_party/rust/termion/src/sys/redox/size.rs
new file mode 100644
index 0000000000..07f64a2437
--- /dev/null
+++ b/third_party/rust/termion/src/sys/redox/size.rs
@@ -0,0 +1,18 @@
+use std::io;
+
+use super::{cvt, redox_termios, syscall};
+
+/// Get the size of the terminal.
+pub fn terminal_size() -> io::Result<(u16, u16)> {
+ let mut winsize = redox_termios::Winsize::default();
+
+ let fd = cvt(syscall::dup(1, b"winsize"))?;
+ let res = cvt(syscall::read(fd, &mut winsize));
+ let _ = syscall::close(fd);
+
+ 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."))
+ }
+}
diff --git a/third_party/rust/termion/src/sys/redox/tty.rs b/third_party/rust/termion/src/sys/redox/tty.rs
new file mode 100644
index 0000000000..9179b39625
--- /dev/null
+++ b/third_party/rust/termion/src/sys/redox/tty.rs
@@ -0,0 +1,22 @@
+use std::{env, fs, io};
+use std::os::unix::io::AsRawFd;
+
+use super::syscall;
+
+/// Is this stream a TTY?
+pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
+ if let Ok(fd) = syscall::dup(stream.as_raw_fd(), b"termios") {
+ let _ = syscall::close(fd);
+ true
+ } else {
+ false
+ }
+}
+
+/// Get the TTY device.
+///
+/// This allows for getting stdio representing _only_ the TTY, and not other streams.
+pub fn get_tty() -> io::Result<fs::File> {
+ let tty = try!(env::var("TTY").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x)));
+ fs::OpenOptions::new().read(true).write(true).open(tty)
+}
diff --git a/third_party/rust/termion/src/sys/unix/attr.rs b/third_party/rust/termion/src/sys/unix/attr.rs
new file mode 100644
index 0000000000..5e21fbac8e
--- /dev/null
+++ b/third_party/rust/termion/src/sys/unix/attr.rs
@@ -0,0 +1,29 @@
+use std::{io, mem};
+
+use super::{cvt, Termios};
+use super::libc::c_int;
+
+pub fn get_terminal_attr() -> io::Result<Termios> {
+ extern "C" {
+ pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
+ }
+ unsafe {
+ let mut termios = mem::zeroed();
+ cvt(tcgetattr(0, &mut termios))?;
+ Ok(termios)
+ }
+}
+
+pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
+ extern "C" {
+ pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
+ }
+ cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
+}
+
+pub fn raw_terminal_attr(termios: &mut Termios) {
+ extern "C" {
+ pub fn cfmakeraw(termptr: *mut Termios);
+ }
+ unsafe { cfmakeraw(termios) }
+}
diff --git a/third_party/rust/termion/src/sys/unix/mod.rs b/third_party/rust/termion/src/sys/unix/mod.rs
new file mode 100644
index 0000000000..08d73feb12
--- /dev/null
+++ b/third_party/rust/termion/src/sys/unix/mod.rs
@@ -0,0 +1,33 @@
+extern crate libc;
+
+use std::io;
+
+pub use self::libc::termios as Termios;
+
+pub mod attr;
+pub mod size;
+pub mod tty;
+
+// Support functions for converting libc return values to io errors {
+trait IsMinusOne {
+ fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+ ($($t:ident)*) => ($(impl IsMinusOne for $t {
+ fn is_minus_one(&self) -> bool {
+ *self == -1
+ }
+ })*)
+ }
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+ if t.is_minus_one() {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(t)
+ }
+}
+// } End of support functions
diff --git a/third_party/rust/termion/src/sys/unix/size.rs b/third_party/rust/termion/src/sys/unix/size.rs
new file mode 100644
index 0000000000..9c2aaf1a8a
--- /dev/null
+++ b/third_party/rust/termion/src/sys/unix/size.rs
@@ -0,0 +1,48 @@
+use std::{io, mem};
+
+use super::cvt;
+use super::libc::{c_ushort, ioctl, STDOUT_FILENO};
+
+#[repr(C)]
+struct TermSize {
+ row: c_ushort,
+ col: c_ushort,
+ _x: c_ushort,
+ _y: c_ushort,
+}
+
+#[cfg(target_os = "linux")]
+pub const TIOCGWINSZ: usize = 0x00005413;
+
+#[cfg(not(target_os = "linux"))]
+pub const TIOCGWINSZ: usize = 0x40087468;
+
+// Since attributes on non-item statements is not stable yet, we use a function.
+#[cfg(not(target_os = "android"))]
+#[cfg(not(target_os = "redox"))]
+#[cfg(target_pointer_width = "64")]
+#[cfg(not(target_env = "musl"))]
+fn tiocgwinsz() -> u64 {
+ TIOCGWINSZ as u64
+}
+#[cfg(not(target_os = "android"))]
+#[cfg(not(target_os = "redox"))]
+#[cfg(target_pointer_width = "32")]
+#[cfg(not(target_env = "musl"))]
+fn tiocgwinsz() -> u32 {
+ TIOCGWINSZ as u32
+}
+
+#[cfg(any(target_env = "musl", target_os = "android"))]
+fn tiocgwinsz() -> i32 {
+ TIOCGWINSZ as i32
+}
+
+/// Get the size of the terminal.
+pub fn terminal_size() -> io::Result<(u16, u16)> {
+ unsafe {
+ let mut size: TermSize = mem::zeroed();
+ cvt(ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _))?;
+ Ok((size.col as u16, size.row as u16))
+ }
+}
diff --git a/third_party/rust/termion/src/sys/unix/tty.rs b/third_party/rust/termion/src/sys/unix/tty.rs
new file mode 100644
index 0000000000..2be9363470
--- /dev/null
+++ b/third_party/rust/termion/src/sys/unix/tty.rs
@@ -0,0 +1,17 @@
+use std::{fs, io};
+use std::os::unix::io::AsRawFd;
+
+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 }
+}
+
+/// Get the TTY device.
+///
+/// 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")
+}