Merge pull request #31 from ticki/termion-fmt-overhaul
Termion fmt overhaul
This commit is contained in:
commit
f21a5ceeed
|
@ -0,0 +1,84 @@
|
|||
# 1.0.0
|
||||
|
||||
Termion 1.0.0 is out! This release is breaking, which is also the reason for the semver bump.
|
||||
|
||||
## Highlights
|
||||
|
||||
Lot'ta goodies.
|
||||
|
||||
- **Mouse support:** If you enabled mouse mode through the `MouseTerminal` struct, you can get mouse events (thanks to IGI-111).
|
||||
- **TrueColor support:** You can now use true color, by the `Rgb` struct.
|
||||
- **A complete revision of the way escapes are handled:** Everything is now done through `Display` instead of custom traits.
|
||||
- **`isatty` wrapper:** `termion::is_tty` takes any `T: AsRawFd` and gives you a `bool`.
|
||||
- **Crates.io release:** Previously, it was distributed solely through git. This turns out to be very convinient, but quite critical whenever making breaking changes (that is, major semver bumps).
|
||||
|
||||
## 0.1.0 to 1.0.0 guide
|
||||
|
||||
This sample table gives an idea of how to go bu converting to the new major
|
||||
version of Termion.
|
||||
|
||||
+------------------------------------------------------------
|
||||
| 0.1.0 | 1.0.0
|
||||
|--------------------------------|---------------------------
|
||||
| `use termion::IntoRawMode` | `use termion::raw::IntoRawMode`
|
||||
| `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)`
|
||||
|
||||
## An example
|
||||
|
||||
```rust
|
||||
#![feature(step_by)]
|
||||
|
||||
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..255).step_by(8 as u8) {
|
||||
for green in (0..255).step_by(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 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();
|
||||
}
|
||||
```
|
|
@ -1,7 +1,12 @@
|
|||
[package]
|
||||
name = "termion"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
authors = ["Ticki <Ticki@users.noreply.github.com>"]
|
||||
description = "A bindless library for manipulating terminals."
|
||||
repository = "ticki/termion"
|
||||
license = "MIT"
|
||||
keywords = ["tty", "color", "terminal", "console", "tui", "size", "cursor", "clear", "ansi", "escape", "codes", "termios", "truecolor", "mouse", "isatty", "raw", "text", "password", "redox", "async"]
|
||||
exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"]
|
||||
|
||||
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||
libc = "0.2.8"
|
||||
|
|
128
README.md
128
README.md
|
@ -8,9 +8,17 @@ 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).
|
||||
Termion is quite convinient, 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.
|
||||
|
||||
[Documentation.](http://ticki.github.io/termion/) | [Examples.](https://github.com/Ticki/termion/tree/master/examples)
|
||||
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).
|
||||
|
||||
[Documentation.](http://ticki.github.io/termion/) | [Examples.](https://github.com/Ticki/termion/tree/master/examples) | [Changelog.](https://github.com/Ticki/termion/tree/master/CHANGELOG.md)
|
||||
|
||||
## A note on stability
|
||||
|
||||
|
@ -20,23 +28,38 @@ and this crate can generally be considered stable.
|
|||
## Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies.termion]
|
||||
git = "https://github.com/ticki/termion.git"
|
||||
[dependencies]
|
||||
termion = "1.0.0"
|
||||
```
|
||||
|
||||
## 0.1.0 to 1.0.0 guide
|
||||
|
||||
This sample table gives an idea of how to go bu converting to the new major
|
||||
version of Termion.
|
||||
|
||||
+------------------------------------------------------------
|
||||
| 0.1.0 | 1.0.0
|
||||
|--------------------------------|---------------------------
|
||||
| `use termion::IntoRawMode` | `use termion::raw::IntoRawMode`
|
||||
| `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.
|
||||
- Color output.
|
||||
- Calculating ANSI escapes.
|
||||
- Text formatting.
|
||||
- Console size.
|
||||
- Control sequences.
|
||||
- Termios control.
|
||||
- Password input.
|
||||
- Redox support.
|
||||
- Safe `isatty` wrapper.
|
||||
- Panic-free error handling.
|
||||
- Special keys events (modifiers, special keys, etc.).
|
||||
- Allocation-free.
|
||||
|
@ -46,31 +69,97 @@ git = "https://github.com/ticki/termion.git"
|
|||
|
||||
and much more.
|
||||
|
||||
## Example
|
||||
## Examples
|
||||
|
||||
### Style and colors.
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::{TermWrite, color, Style};
|
||||
use termion::{color, style};
|
||||
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
let stdout = io::stdout();
|
||||
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.color(color::Red).unwrap();
|
||||
println!("Red");
|
||||
stdout.write(b"password: ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
stdout.color(color::Blue).unwrap();
|
||||
println!("Blue");
|
||||
let pass = stdin.read_passwd(&mut stdout);
|
||||
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
println!("Blue'n'Bold");
|
||||
|
||||
stdout.reset().unwrap();
|
||||
stdout.style(Style::Italic).unwrap();
|
||||
println!("Just plain italic")
|
||||
if let Ok(Some(pass)) = pass {
|
||||
stdout.write(pass.as_bytes()).unwrap();
|
||||
stdout.write(b"\n").unwrap();
|
||||
} else {
|
||||
stdout.write(b"Error\n").unwrap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -82,7 +171,6 @@ For a more complete example, see [a minesweeper implementation](https://github.c
|
|||
|
||||
<img src="image.png" width="200">
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT/X11.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{TermWrite, IntoRawMode, async_stdin};
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::async_stdin;
|
||||
use std::io::{Read, Write, stdout, stdin};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
@ -10,11 +11,10 @@ fn main() {
|
|||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||
let mut stdin = async_stdin().bytes();
|
||||
|
||||
stdout.clear().unwrap();
|
||||
stdout.goto(0, 0).unwrap();
|
||||
write!(stdout, "{}{}", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
|
||||
|
||||
loop {
|
||||
stdout.clear_line().unwrap();
|
||||
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();
|
||||
|
@ -29,7 +29,7 @@ fn main() {
|
|||
stdout.flush().unwrap();
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
stdout.write(b"\r #").unwrap();
|
||||
stdout.goto(0, 0).unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -1,23 +1,10 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{TermWrite, color, Style};
|
||||
|
||||
use std::io;
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
|
||||
stdout.color(color::Red).unwrap();
|
||||
println!("Red");
|
||||
|
||||
stdout.color(color::Blue).unwrap();
|
||||
println!("Blue");
|
||||
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
println!("Blue'n'Bold");
|
||||
|
||||
stdout.reset().unwrap();
|
||||
stdout.style(Style::Italic).unwrap();
|
||||
println!("Just plain italic")
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 :(");
|
||||
}
|
||||
}
|
|
@ -1,21 +1,22 @@
|
|||
extern crate termion;
|
||||
|
||||
fn main() {
|
||||
use termion::{TermRead, TermWrite, IntoRawMode, Key};
|
||||
use std::io::{Write, stdout, stdin};
|
||||
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();
|
||||
|
||||
stdout.clear().unwrap();
|
||||
stdout.goto(0, 0).unwrap();
|
||||
stdout.write(b"q to exit. Type stuff, use alt, and so on.").unwrap();
|
||||
stdout.hide_cursor().unwrap();
|
||||
stdout.flush().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();
|
||||
|
||||
for c in stdin.keys() {
|
||||
stdout.goto(5, 5).unwrap();
|
||||
stdout.clear_line().unwrap();
|
||||
write!(stdout, "{}{}", termion::cursor::Goto(1, 1), termion::clear::CurrentLine).unwrap();
|
||||
|
||||
match c.unwrap() {
|
||||
Key::Char('q') => break,
|
||||
Key::Char(c) => println!("{}", c),
|
||||
|
@ -31,5 +32,5 @@ fn main() {
|
|||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
stdout.show_cursor().unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
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() {
|
||||
use termion::{TermRead, TermWrite, IntoRawMode, Key, Event, MouseEvent};
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap().with_mouse().unwrap();
|
||||
let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
|
||||
stdout.clear().unwrap();
|
||||
stdout.goto(0, 0).unwrap();
|
||||
stdout.write(b"q to exit. Type stuff, use alt, click around...").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
write!(stdout, "{}{}q to exit. Type stuff, use alt, click around...", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
|
||||
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
let mut x = 1;
|
||||
let mut y = 1;
|
||||
|
||||
for c in stdin.events() {
|
||||
stdout.goto(5, 5).unwrap();
|
||||
stdout.clear_line().unwrap();
|
||||
let evt = c.unwrap();
|
||||
writeln!(stdout, "{:?}{}{}", evt, termion::cursor::Goto(5, 5), termion::clear::CurrentLine).unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
|
@ -32,10 +30,9 @@ fn main() {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
println!("{:?}", evt);
|
||||
stdout.goto(x, y).unwrap();
|
||||
writeln!(stdout, "{:?}{}", evt, termion::cursor::Goto(x, y)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
stdout.show_cursor().unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#![feature(step_by)]
|
||||
|
||||
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..255).step_by(8 as u8) {
|
||||
for green in (0..255).step_by(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 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();
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::TermRead;
|
||||
use termion::input::TermRead;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,137 +1,24 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{TermWrite, color, Style};
|
||||
use std::io::{self, Write};
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
let line_num_bg: color::AnsiValue = color::grayscale(3);
|
||||
let line_num_fg: color::AnsiValue = color::grayscale(18);
|
||||
let error_fg: color::AnsiValue = color::grayscale(17);
|
||||
let info_line: &'static str = "| ";
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
|
||||
stdout.color(color::LightGreen).unwrap();
|
||||
stdout.write("-- src/test/ui/borrow-errors.rs at 82:18 --\n".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
stdout.write(b"error: ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
stdout.write(b"two closures require unique access to `vec` at the same time").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
stdout.color(color::Magenta).unwrap();
|
||||
stdout.write(b" [E0524]\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"79 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.write(b" let append = |e| {\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(info_line.as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.write(" ^^^ ".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(error_fg).unwrap();
|
||||
stdout.write(b"first closure is constructed here\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"80 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.write(b" vec.push(e)\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(info_line.as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.write(" ^^^ ".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(error_fg).unwrap();
|
||||
stdout.write(b"previous borrow occurs due to use of `vec` in closure\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"81 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.write(b" };\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"82 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.write(b" let append = |e| {\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(info_line.as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.write(" ^^^ ".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(error_fg).unwrap();
|
||||
stdout.write(b"second closure is constructed here\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"83 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.write(b" vec.push(e)\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(info_line.as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.write(" ^^^ ".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(error_fg).unwrap();
|
||||
stdout.write(b"borrow occurs due to use of `vec` in closure\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"84 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.write(b" };\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(b"85 ").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.write(b" }\n").unwrap();
|
||||
|
||||
stdout.color(line_num_fg).unwrap();
|
||||
stdout.bg_color(line_num_bg).unwrap();
|
||||
stdout.write(info_line.as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
stdout.color(color::Red).unwrap();
|
||||
stdout.write(" ^ ".as_bytes()).unwrap();
|
||||
stdout.reset().unwrap();
|
||||
|
||||
stdout.color(error_fg).unwrap();
|
||||
stdout.write(b"borrow from first closure ends here\n").unwrap();
|
||||
stdout.reset().unwrap();
|
||||
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)))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{TermWrite, IntoRawMode, Color, Style};
|
||||
use termion::color;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Read, Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
|
@ -10,19 +11,9 @@ fn main() {
|
|||
let stdin = stdin();
|
||||
let stdin = stdin.lock();
|
||||
|
||||
// Move the cursor to (5, 5)
|
||||
stdout.goto(5, 5).unwrap();
|
||||
// Clear the screen.
|
||||
stdout.clear().unwrap();
|
||||
// Set style to bold.
|
||||
stdout.style(Style::Bold).unwrap();
|
||||
// Write some guiding stuff
|
||||
stdout.write(b"yo, 'q' will exit.").unwrap();
|
||||
// Reset the style.
|
||||
stdout.reset().unwrap();
|
||||
// Flush and goto (20, 10)
|
||||
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();
|
||||
stdout.goto(20, 10).unwrap();
|
||||
|
||||
let mut bytes = stdin.bytes();
|
||||
loop {
|
||||
|
@ -32,11 +23,11 @@ fn main() {
|
|||
// Quit
|
||||
b'q' => return,
|
||||
// Clear the screen
|
||||
b'c' => stdout.clear(),
|
||||
b'c' => write!(stdout, "{}", termion::clear::All),
|
||||
// Set red color
|
||||
b'r' => stdout.color(Color::Rgb(5, 0, 0)),
|
||||
b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))),
|
||||
// Write it to stdout.
|
||||
a => stdout.write(&[a]),
|
||||
a => write!(stdout, "{}", a),
|
||||
}.unwrap();
|
||||
|
||||
stdout.flush().unwrap();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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");
|
272
src/color.rs
272
src/color.rs
|
@ -1,219 +1,119 @@
|
|||
//! Colors.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A terminal color.
|
||||
pub trait Color {
|
||||
/// Convert this to its ANSI value.
|
||||
fn to_ansi_val(self) -> u8;
|
||||
/// 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)]
|
||||
pub struct $name;
|
||||
|
||||
impl Color for $name {
|
||||
#[inline]
|
||||
fn to_ansi_val(self) -> u8 {
|
||||
$value
|
||||
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, 0x0);
|
||||
derive_color!("Red.", Red, 0x1);
|
||||
derive_color!("Green.", Green, 0x2);
|
||||
derive_color!("Yellow.", Yellow, 0x3);
|
||||
derive_color!("Blue.", Blue, 0x4);
|
||||
derive_color!("Magenta.", Magenta, 0x5);
|
||||
derive_color!("Cyan.", Cyan, 0x6);
|
||||
derive_color!("White.", White, 0x7);
|
||||
derive_color!("High-intensity light black.", LightBlack, 0x8);
|
||||
derive_color!("High-intensity light red.", LightRed, 0x9);
|
||||
derive_color!("High-intensity light green.", LightGreen, 0xA);
|
||||
derive_color!("High-intensity light yellow.", LightYellow, 0xB);
|
||||
derive_color!("High-intensity light blue.", LightBlue, 0xC);
|
||||
derive_color!("High-intensity light magenta.", LightMagenta, 0xD);
|
||||
derive_color!("High-intensity light cyan.", LightCyan, 0xE);
|
||||
derive_color!("High-intensity light white.", LightWhite, 0xF);
|
||||
|
||||
/// 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)
|
||||
}
|
||||
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");
|
||||
|
||||
/// An arbitrary ANSI color value.
|
||||
#[derive(Clone, Copy)]
|
||||
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 to_ansi_val(self) -> u8 {
|
||||
self.0
|
||||
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 color palette.
|
||||
///
|
||||
/// This should generally only be used when the color is runtime determined. Otherwise, use the
|
||||
/// color types, which resolves the value at compile time.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Palette {
|
||||
/// Black.
|
||||
Black,
|
||||
/// Red.
|
||||
Red,
|
||||
/// Green.
|
||||
Green,
|
||||
/// Yellow.
|
||||
Yellow,
|
||||
/// Blue.
|
||||
Blue,
|
||||
/// Megenta.
|
||||
Magenta,
|
||||
/// Cyan.
|
||||
Cyan,
|
||||
/// White.
|
||||
White,
|
||||
/// High-intensity black.
|
||||
LightBlack,
|
||||
/// High-intensity red.
|
||||
LightRed,
|
||||
/// High-intensity green.
|
||||
LightGreen,
|
||||
/// High-intensity yellow.
|
||||
LightYellow,
|
||||
/// High-intensity blue.
|
||||
LightBlue,
|
||||
/// High-intensity magenta.
|
||||
LightMagenta,
|
||||
/// High-intensity cyan.
|
||||
LightCyan,
|
||||
/// High-intensity white.
|
||||
LightWhite,
|
||||
/// 216-color (r, g, b ≤ 5) RGB.
|
||||
Rgb(u8, u8, u8),
|
||||
/// Grayscale (max value: 24).
|
||||
Grayscale(u8),
|
||||
}
|
||||
/// A truecolor RGB.
|
||||
pub struct Rgb(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Color for Palette {
|
||||
fn to_ansi_val(self) -> u8 {
|
||||
match self {
|
||||
Palette::Black => Black.to_ansi_val(),
|
||||
Palette::Red => Red.to_ansi_val(),
|
||||
Palette::Green => Green.to_ansi_val(),
|
||||
Palette::Yellow => Yellow.to_ansi_val(),
|
||||
Palette::Blue => Blue.to_ansi_val(),
|
||||
Palette::Magenta => Magenta.to_ansi_val(),
|
||||
Palette::Cyan => Cyan.to_ansi_val(),
|
||||
Palette::White => White.to_ansi_val(),
|
||||
Palette::LightBlack => LightBlack.to_ansi_val(),
|
||||
Palette::LightRed => LightRed.to_ansi_val(),
|
||||
Palette::LightGreen => LightGreen.to_ansi_val(),
|
||||
Palette::LightYellow => LightYellow.to_ansi_val(),
|
||||
Palette::LightBlue => LightBlue.to_ansi_val(),
|
||||
Palette::LightMagenta => LightMagenta.to_ansi_val(),
|
||||
Palette::LightCyan => LightCyan.to_ansi_val(),
|
||||
Palette::LightWhite => LightWhite.to_ansi_val(),
|
||||
Palette::Rgb(r, g, b) => rgb(r, g, b).to_ansi_val(),
|
||||
Palette::Grayscale(shade) => grayscale(shade).to_ansi_val(),
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
/// A foreground color.
|
||||
pub struct Fg<C: Color>(pub C);
|
||||
|
||||
#[test]
|
||||
fn test_rgb() {
|
||||
assert_eq!(rgb(2, 3, 4).to_ansi_val(), 110);
|
||||
assert_eq!(rgb(2, 1, 4).to_ansi_val(), 98);
|
||||
assert_eq!(rgb(5, 1, 4).to_ansi_val(), 206);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grayscale() {
|
||||
assert_eq!(grayscale(2).to_ansi_val(), 234);
|
||||
assert_eq!(grayscale(5).to_ansi_val(), 237);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normal() {
|
||||
assert_eq!(Black.to_ansi_val(), 0);
|
||||
assert_eq!(Green.to_ansi_val(), 2);
|
||||
assert_eq!(White.to_ansi_val(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hi() {
|
||||
assert_eq!(LightRed.to_ansi_val(), 9);
|
||||
assert_eq!(LightCyan.to_ansi_val(), 0xE);
|
||||
assert_eq!(LightWhite.to_ansi_val(), 0xF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_palette() {
|
||||
assert_eq!(Palette::Black.to_ansi_val(), Black.to_ansi_val());
|
||||
assert_eq!(Palette::Red.to_ansi_val(), Red.to_ansi_val());
|
||||
assert_eq!(Palette::LightBlue.to_ansi_val(), LightBlue.to_ansi_val());
|
||||
assert_eq!(Palette::Rgb(2, 2, 2).to_ansi_val(), rgb(2, 2, 2).to_ansi_val());
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_bound_check_rgb() {
|
||||
rgb(3, 9, 1);
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_bound_check_rgb_2() {
|
||||
rgb(3, 6, 1);
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_bound_check_grayscale() {
|
||||
grayscale(25);
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_palette_rgb_bound_check_1() {
|
||||
Palette::Rgb(3, 6, 1).to_ansi_val();
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_palette_rgb_bound_check_2() {
|
||||
Palette::Rgb(3, 9, 1).to_ansi_val();
|
||||
}
|
||||
|
||||
#[cfg(debug)]
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_palette_grayscale_bound_check_2() {
|
||||
Palette::Grayscale(25).to_ansi_val();
|
||||
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.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
357
src/control.rs
357
src/control.rs
|
@ -1,357 +0,0 @@
|
|||
use std::io::{self, Write};
|
||||
|
||||
use Style;
|
||||
use color::Color;
|
||||
|
||||
/// Extension to the `Write` trait.
|
||||
///
|
||||
/// This extension to the `Write` trait is capable of producing the correct ANSI escape sequences
|
||||
/// for given commands, effectively controlling the terminal.
|
||||
pub trait TermWrite {
|
||||
|
||||
/// Print the CSI (control sequence introducer) followed by a byte string.
|
||||
#[inline]
|
||||
fn csi(&mut self, b: &[u8]) -> io::Result<usize>;
|
||||
/// Print OSC (operating system command) followed by a byte string.
|
||||
#[inline]
|
||||
fn osc(&mut self, b: &[u8]) -> io::Result<usize>;
|
||||
/// Print DSC (device control string) followed by a byte string.
|
||||
#[inline]
|
||||
fn dsc(&mut self, b: &[u8]) -> io::Result<usize>;
|
||||
|
||||
|
||||
/// Clear the entire screen.
|
||||
#[inline]
|
||||
fn clear(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"2J")
|
||||
}
|
||||
|
||||
/// Clear everything _after_ the cursor.
|
||||
#[inline]
|
||||
fn clear_after(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"J")
|
||||
}
|
||||
|
||||
/// Clear everything _before_ the cursor.
|
||||
#[inline]
|
||||
fn clear_before(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"1J")
|
||||
}
|
||||
|
||||
/// Clear the current line.
|
||||
#[inline]
|
||||
fn clear_line(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"2K")
|
||||
}
|
||||
|
||||
/// Clear from the cursor until newline.
|
||||
#[inline]
|
||||
fn clear_until_newline(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"K")
|
||||
}
|
||||
|
||||
/// Show the cursor.
|
||||
#[inline]
|
||||
fn show_cursor(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"?25h")
|
||||
}
|
||||
|
||||
/// Hide the cursor.
|
||||
#[inline]
|
||||
fn hide_cursor(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"?25l")
|
||||
}
|
||||
|
||||
/// Move the cursor `num` spaces to the left.
|
||||
#[inline]
|
||||
fn move_cursor_left(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'D'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
/// Move the cursor `num` spaces to the right.
|
||||
#[inline]
|
||||
fn move_cursor_right(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'C'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the cursor `num` spaces up.
|
||||
#[inline]
|
||||
fn move_cursor_up(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'A'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the cursor `num` spaces down.
|
||||
#[inline]
|
||||
fn move_cursor_down(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'B'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Scroll the window `num` spaces up.
|
||||
#[inline]
|
||||
fn scroll_up(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'S'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Scroll the window `num` spaces down.
|
||||
#[inline]
|
||||
fn scroll_down(&mut self, num: u32) -> io::Result<usize> {
|
||||
if num > 0 {
|
||||
self.csi(&[b'0' + (num / 10000) as u8,
|
||||
b'0' + (num / 1000) as u8 % 10,
|
||||
b'0' + (num / 100) as u8 % 10,
|
||||
b'0' + (num / 10) as u8 % 10,
|
||||
b'0' + num as u8 % 10,
|
||||
b'T'])
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the rendition mode.
|
||||
///
|
||||
/// This will reset both the current style and color.
|
||||
#[inline]
|
||||
fn reset(&mut self) -> io::Result<usize> {
|
||||
self.csi(b"m")
|
||||
}
|
||||
|
||||
/// Restore the defaults.
|
||||
///
|
||||
/// This will reset color, position, cursor state, and so on. It is recommended that you use
|
||||
/// this before you exit your program, to avoid messing up the user's terminal.
|
||||
#[inline]
|
||||
fn restore(&mut self) -> io::Result<usize> {
|
||||
Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
|
||||
}
|
||||
|
||||
/// Go to a given position.
|
||||
///
|
||||
/// The position is 0-based.
|
||||
#[inline]
|
||||
fn goto(&mut self, mut x: u16, mut y: u16) -> io::Result<usize> {
|
||||
x += 1;
|
||||
y += 1;
|
||||
|
||||
self.csi(&[
|
||||
b'0' + (y / 10000) as u8,
|
||||
b'0' + (y / 1000) as u8 % 10,
|
||||
b'0' + (y / 100) as u8 % 10,
|
||||
b'0' + (y / 10) as u8 % 10,
|
||||
b'0' + y as u8 % 10,
|
||||
b';',
|
||||
b'0' + (x / 10000) as u8,
|
||||
b'0' + (x / 1000) as u8 % 10,
|
||||
b'0' + (x / 100) as u8 % 10,
|
||||
b'0' + (x / 10) as u8 % 10,
|
||||
b'0' + x as u8 % 10,
|
||||
b'H',
|
||||
])
|
||||
}
|
||||
|
||||
/// Set graphic rendition.
|
||||
#[inline]
|
||||
fn rendition(&mut self, r: u8) -> io::Result<usize> {
|
||||
self.csi(&[
|
||||
b'0' + r / 100,
|
||||
b'0' + r / 10 % 10,
|
||||
b'0' + r % 10,
|
||||
b'm',
|
||||
])
|
||||
}
|
||||
|
||||
/// Set foreground color.
|
||||
#[inline]
|
||||
fn color<C: Color>(&mut self, color: C) -> io::Result<usize> {
|
||||
let ansi = color.to_ansi_val();
|
||||
self.csi(&[
|
||||
b'3',
|
||||
b'8',
|
||||
b';',
|
||||
b'5',
|
||||
b';',
|
||||
b'0' + ansi / 100,
|
||||
b'0' + ansi / 10 % 10,
|
||||
b'0' + ansi % 10,
|
||||
b'm',
|
||||
])
|
||||
}
|
||||
|
||||
/// Set background color.
|
||||
#[inline]
|
||||
fn bg_color<C: Color>(&mut self, color: C) -> io::Result<usize> {
|
||||
let ansi = color.to_ansi_val();
|
||||
self.csi(&[
|
||||
b'4',
|
||||
b'8',
|
||||
b';',
|
||||
b'5',
|
||||
b';',
|
||||
b'0' + ansi / 100,
|
||||
b'0' + ansi / 10 % 10,
|
||||
b'0' + ansi % 10,
|
||||
b'm',
|
||||
])
|
||||
}
|
||||
|
||||
/// Set rendition mode (SGR).
|
||||
#[inline]
|
||||
fn style(&mut self, mode: Style) -> io::Result<usize> {
|
||||
self.rendition(mode as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> TermWrite for W {
|
||||
#[inline]
|
||||
fn csi(&mut self, b: &[u8]) -> io::Result<usize> {
|
||||
Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn osc(&mut self, b: &[u8]) -> io::Result<usize> {
|
||||
Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn dsc(&mut self, b: &[u8]) -> io::Result<usize> {
|
||||
Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn test_csi() {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.csi(b"bluh").unwrap();
|
||||
|
||||
assert_eq!(buf.get_ref(), b"\x1B[bluh");
|
||||
|
||||
buf.csi(b"blah").unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[bluh\x1B[blah");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_csi_partial() {
|
||||
let mut buf = [0; 3];
|
||||
let mut buf = &mut buf[..];
|
||||
assert_eq!(buf.csi(b"blu").unwrap(), 3);
|
||||
assert_eq!(buf.csi(b"").unwrap(), 0);
|
||||
assert_eq!(buf.csi(b"nooooo").unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_osc() {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.osc(b"bluh").unwrap();
|
||||
|
||||
assert_eq!(buf.get_ref(), b"\x1B]bluh");
|
||||
|
||||
buf.osc(b"blah").unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B]bluh\x1B]blah");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_osc_partial() {
|
||||
let mut buf = [0; 3];
|
||||
let mut buf = &mut buf[..];
|
||||
assert_eq!(buf.osc(b"blu").unwrap(), 3);
|
||||
assert_eq!(buf.osc(b"").unwrap(), 0);
|
||||
assert_eq!(buf.osc(b"nooooo").unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dsc() {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.dsc(b"bluh").unwrap();
|
||||
|
||||
assert_eq!(buf.get_ref(), b"\x1BPbluh");
|
||||
|
||||
buf.dsc(b"blah").unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1BPbluh\x1BPblah");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dsc_partial() {
|
||||
let mut buf = [0; 3];
|
||||
let mut buf = &mut buf[..];
|
||||
assert_eq!(buf.dsc(b"blu").unwrap(), 3);
|
||||
assert_eq!(buf.dsc(b"").unwrap(), 0);
|
||||
assert_eq!(buf.dsc(b"nooooo").unwrap(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.clear().unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[2J");
|
||||
buf.clear().unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[2J\x1B[2J");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_goto() {
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.goto(34, 43).unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[00044;00035H");
|
||||
buf.goto(24, 45).unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[00044;00035H\x1B[00046;00025H");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style() {
|
||||
use Style;
|
||||
|
||||
let mut buf = Cursor::new(Vec::new());
|
||||
buf.style(Style::Bold).unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[001m");
|
||||
buf.style(Style::Italic).unwrap();
|
||||
assert_eq!(buf.get_ref(), b"\x1B[001m\x1B[003m");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
//! Cursor.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
|
||||
derive_csi_sequence!("Show the cursor.", Show, "?25h");
|
||||
|
||||
/// 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.
|
||||
#[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.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
17
src/event.rs
17
src/event.rs
|
@ -1,3 +1,5 @@
|
|||
//! Mouse and key events.
|
||||
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::str;
|
||||
|
@ -17,8 +19,12 @@ pub enum 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),
|
||||
}
|
||||
|
||||
|
@ -112,8 +118,8 @@ where I: Iterator<Item = Result<u8, Error>>
|
|||
// X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only).
|
||||
let cb = iter.next().unwrap().unwrap() as i8 - 32;
|
||||
// (1, 1) are the coords for upper left.
|
||||
let cx = (iter.next().unwrap().unwrap() as u8 - 1).saturating_sub(32) as u16;
|
||||
let cy = (iter.next().unwrap().unwrap() as u8 - 1).saturating_sub(32) as u16;
|
||||
let cy = (iter.next().unwrap().unwrap() as u8).saturating_sub(32) as u16;
|
||||
let cx = (iter.next().unwrap().unwrap() as u8).saturating_sub(32) as u16;
|
||||
Event::Mouse(match cb & 0b11 {
|
||||
0 => {
|
||||
if cb & 0x40 != 0 {
|
||||
|
@ -150,8 +156,8 @@ where I: Iterator<Item = Result<u8, Error>>
|
|||
let ref mut nums = str_buf.split(';');
|
||||
|
||||
let cb = nums.next().unwrap().parse::<u16>().unwrap();
|
||||
let cx = nums.next().unwrap().parse::<u16>().unwrap() - 1;
|
||||
let cy = nums.next().unwrap().parse::<u16>().unwrap() - 1;
|
||||
let cy = nums.next().unwrap().parse::<u16>().unwrap();
|
||||
let cx = nums.next().unwrap().parse::<u16>().unwrap();
|
||||
|
||||
let button = match cb {
|
||||
0 => MouseButton::Left,
|
||||
|
@ -189,8 +195,8 @@ where I: Iterator<Item = Result<u8, Error>>
|
|||
let ref mut nums = str_buf.split(';');
|
||||
|
||||
let cb = nums.next().unwrap().parse::<u16>().unwrap();
|
||||
let cx = nums.next().unwrap().parse::<u16>().unwrap() - 1;
|
||||
let cy = nums.next().unwrap().parse::<u16>().unwrap() - 1;
|
||||
let cx = nums.next().unwrap().parse::<u16>().unwrap() - 1;
|
||||
|
||||
let event = match cb {
|
||||
32 => MouseEvent::Press(MouseButton::Left, cx, cy),
|
||||
|
@ -273,6 +279,7 @@ where I: Iterator<Item = Result<u8, Error>>
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_parse_utf8() {
|
||||
let st = "abcéŷ¤£€ù%323";
|
||||
|
|
70
src/input.rs
70
src/input.rs
|
@ -1,8 +1,10 @@
|
|||
//! Input.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops;
|
||||
|
||||
use event::{parse_event, Event, Key};
|
||||
|
||||
use IntoRawMode;
|
||||
|
||||
use raw::IntoRawMode;
|
||||
|
||||
/// An iterator over input keys.
|
||||
pub struct Keys<I> {
|
||||
|
@ -65,7 +67,6 @@ pub trait TermRead {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl<R: Read> TermRead for R {
|
||||
fn events(self) -> Events<io::Bytes<R>> {
|
||||
Events {
|
||||
|
@ -96,6 +97,57 @@ impl<R: Read> TermRead for R {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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(ENTER_MOUSE_SEQUENCE.as_bytes()).unwrap();
|
||||
|
||||
MouseTerminal { term: from }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for MouseTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
self.term.write(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::*;
|
||||
|
@ -124,11 +176,11 @@ mod test {
|
|||
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, 1, 3)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 1, 3)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 1, 3)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(1, 3)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(1, 3)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 4, 2)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 4, 2)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Press(MouseButton::Left, 3, 1)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(4, 2)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Mouse(MouseEvent::Release(3, 1)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -17,30 +17,22 @@ extern crate libc;
|
|||
#[cfg(not(target_os = "redox"))]
|
||||
mod termios;
|
||||
|
||||
mod control;
|
||||
pub use control::TermWrite;
|
||||
|
||||
mod async;
|
||||
pub use async::{AsyncReader, async_stdin};
|
||||
|
||||
mod input;
|
||||
pub use input::{TermRead, Events, Keys};
|
||||
|
||||
mod event;
|
||||
pub use event::{Key, MouseEvent, MouseButton, Event};
|
||||
|
||||
mod raw;
|
||||
pub use raw::{IntoRawMode, RawTerminal, MouseTerminal};
|
||||
|
||||
mod size;
|
||||
pub use size::terminal_size;
|
||||
|
||||
/// ANSI colors.
|
||||
mod tty;
|
||||
pub use tty::is_tty;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod clear;
|
||||
pub mod color;
|
||||
|
||||
/// Deprecated reexport.
|
||||
#[deprecated]
|
||||
pub use color::Palette as Color;
|
||||
|
||||
mod style;
|
||||
pub use style::Style;
|
||||
pub mod cursor;
|
||||
pub mod event;
|
||||
pub mod input;
|
||||
pub mod raw;
|
||||
pub mod scroll;
|
||||
pub mod style;
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
85
src/raw.rs
85
src/raw.rs
|
@ -1,5 +1,7 @@
|
|||
//! Raw mode.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::ops;
|
||||
|
||||
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
||||
/// dropped.
|
||||
|
@ -11,8 +13,7 @@ pub struct RawTerminal<W: Write> {
|
|||
#[cfg(target_os = "redox")]
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
use control::TermWrite;
|
||||
self.csi(b"R").unwrap();
|
||||
write!(self, csi!("?82h")).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,17 +27,6 @@ pub struct RawTerminal<W: Write> {
|
|||
output: W,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W> RawTerminal<W>
|
||||
where W: Write
|
||||
{
|
||||
/// Enable mouse support.
|
||||
pub fn with_mouse(mut self) -> io::Result<MouseTerminal<W>> {
|
||||
try!(self.write(ENTER_MOUSE_SEQUENCE));
|
||||
Ok(MouseTerminal { term: self })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
|
@ -45,7 +35,7 @@ impl<W: Write> Drop for RawTerminal<W> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Deref for RawTerminal<W> {
|
||||
impl<W: Write> ops::Deref for RawTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
|
@ -53,7 +43,7 @@ impl<W: Write> Deref for RawTerminal<W> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<W: Write> DerefMut for RawTerminal<W> {
|
||||
impl<W: Write> ops::DerefMut for RawTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.output
|
||||
}
|
||||
|
@ -107,65 +97,12 @@ impl<W: Write> IntoRawMode for W {
|
|||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn into_raw_mode(mut self) -> io::Result<RawTerminal<W>> {
|
||||
use control::TermWrite;
|
||||
|
||||
self.csi(b"r").map(|_| {
|
||||
let mut res = RawTerminal { output: self };
|
||||
res
|
||||
write!(self, csi!("?82h")).map(|_| {
|
||||
RawTerminal { output: self }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence of escape codes to enable terminal mouse support.
|
||||
const ENTER_MOUSE_SEQUENCE: &'static [u8] = b"\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h";
|
||||
|
||||
/// A sequence of escape codes to disable terminal mouse support.
|
||||
const EXIT_MOUSE_SEQUENCE: &'static [u8] = b"\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l";
|
||||
|
||||
/// A `RawTerminal` with added mouse support.
|
||||
///
|
||||
/// To get such a terminal handle use `RawTerminal`'s
|
||||
/// [`with_mouse()`](../termion/struct.RawTerminal.html#method.with_mouse) method.
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub struct MouseTerminal<W: Write> {
|
||||
term: RawTerminal<W>,
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W: Write> Drop for MouseTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
self.term.write(EXIT_MOUSE_SEQUENCE).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W: Write> Deref for MouseTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
self.term.deref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
impl<W: Write> DerefMut for MouseTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
self.term.deref_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
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::*;
|
||||
|
@ -177,10 +114,4 @@ mod test {
|
|||
|
||||
out.write(b"this is a test, muahhahahah").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enable_mouse() {
|
||||
let mut out = stdout().into_raw_mode().unwrap().with_mouse().unwrap();
|
||||
out.write(b"abcde\x1B[<1;1;0;Mfgh").unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
34
src/style.rs
34
src/style.rs
|
@ -1,21 +1,13 @@
|
|||
/// A SGR parameter (rendition mode).
|
||||
pub enum Style {
|
||||
/// Reset SGR parameters.
|
||||
Reset = 0,
|
||||
/// Bold text.
|
||||
Bold = 1,
|
||||
/// Fainted text (not widely supported).
|
||||
Faint = 2,
|
||||
/// Italic text.
|
||||
Italic = 3,
|
||||
/// Underlined text.
|
||||
Underline = 4,
|
||||
/// Blinking text (not widely supported).
|
||||
Blink = 5,
|
||||
/// Inverted colors (negative mode).
|
||||
Invert = 7,
|
||||
/// Crossed out text (not widely supported).
|
||||
CrossedOut = 9,
|
||||
/// Framed text (not widely supported).
|
||||
Framed = 51,
|
||||
}
|
||||
//! Style.
|
||||
|
||||
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!("Framed text (not widely supported).", Framed, "51m");
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// Is this stream an TTY?
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn is_tty<T: AsRawFd>(stream: T) -> bool {
|
||||
use libc;
|
||||
|
||||
unsafe { libc::isatty(stream.as_raw_fd()) == 1}
|
||||
}
|
||||
|
||||
/// This will panic.
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is_tty<T: AsRawFd>(_stream: T) -> bool {
|
||||
unimplemented!();
|
||||
}
|
Loading…
Reference in New Issue