termion-1.5.6/.cargo_vcs_info.json0000644000000001120000000000000125130ustar { "git": { "sha1": "dce5e7500fd709987f9bf8f3911e4daa61d0ad14" } } termion-1.5.6/.gitignore000064400000000000000000000000220000000000000132510ustar 00000000000000target Cargo.lock termion-1.5.6/.gitlab-ci.yml000064400000000000000000000013700000000000000137240ustar 00000000000000image: "redoxos/redoxer" stages: - build - test cache: paths: - target/ build:linux:stable: stage: build script: - rustup update stable - cargo +stable build --verbose build:linux: stage: build script: cargo +nightly build --verbose build:redox: stage: build script: redoxer build --verbose test:linux:stable: stage: test dependencies: - build:linux:stable script: - rustup update stable - script -c "cargo +stable test --verbose" test:linux: stage: test dependencies: - build:linux script: script -c "cargo +nightly test --verbose" test:redox: stage: test dependencies: - build:redox script: redoxer test --verbose termion-1.5.6/.travis.yml000064400000000000000000000004630000000000000134030ustar 00000000000000language: rust cache: cargo rust: - stable - beta - nightly os: - linux - osx script: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then FAKETTY="script -q /dev/null"; fi - $FAKETTY cargo build --verbose - $FAKETTY cargo test --verbose - $FAKETTY cargo test --release --verbose termion-1.5.6/Cargo.lock0000644000000023370000000000000105010ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "libc" version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929" [[package]] name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" [[package]] name = "redox_syscall" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ec8ca9416c5ea37062b502703cd7fcb207736bc294f6e0cf367ac6fc234570" dependencies = [ "bitflags", ] [[package]] name = "redox_termios" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" dependencies = [ "redox_syscall", ] [[package]] name = "termion" version = "1.5.6" dependencies = [ "libc", "numtoa", "redox_syscall", "redox_termios", ] termion-1.5.6/Cargo.toml0000644000000024200000000000000105150ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "termion" version = "1.5.6" authors = ["ticki ", "gycos ", "IGI-111 "] exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"] description = "A bindless library for manipulating terminals." documentation = "https://docs.rs/termion" keywords = ["tty", "color", "terminal", "password", "tui"] license = "MIT" repository = "https://gitlab.redox-os.org/redox-os/termion" [dependencies.numtoa] version = "0.1" features = ["std"] [target."cfg(not(target_os = \"redox\"))".dependencies.libc] version = "0.2" [target."cfg(target_os = \"redox\")".dependencies.redox_syscall] version = "0.2" [target."cfg(target_os = \"redox\")".dependencies.redox_termios] version = "0.1" termion-1.5.6/Cargo.toml.orig000064400000000000000000000012740000000000000141620ustar 00000000000000[package] name = "termion" version = "1.5.6" authors = ["ticki ", "gycos ", "IGI-111 "] description = "A bindless library for manipulating terminals." repository = "https://gitlab.redox-os.org/redox-os/termion" documentation = "https://docs.rs/termion" license = "MIT" keywords = ["tty", "color", "terminal", "password", "tui"] exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"] [dependencies] numtoa = { version = "0.1", features = ["std"]} [target.'cfg(not(target_os = "redox"))'.dependencies] libc = "0.2" [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.2" redox_termios = "0.1" termion-1.5.6/LICENSE000064400000000000000000000020600000000000000122720ustar 00000000000000The 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. termion-1.5.6/README.md000064400000000000000000000120340000000000000125460ustar 00000000000000

Termion logo

[![Build Status](https://travis-ci.org/redox-os/termion.svg?branch=master)](https://travis-ci.org/redox-os/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/redox-os/termion/tree/master/examples) | [Changelog](https://github.com/redox-os/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. ## License MIT/X11. termion-1.5.6/examples/alternate_screen.rs000064400000000000000000000007270000000000000167770ustar 00000000000000extern 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."); } termion-1.5.6/examples/alternate_screen_raw.rs000064400000000000000000000024510000000000000176440ustar 00000000000000extern 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(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(); } termion-1.5.6/examples/async.rs000064400000000000000000000022070000000000000145710ustar 00000000000000extern 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(); } } termion-1.5.6/examples/click.rs000064400000000000000000000017170000000000000145460ustar 00000000000000extern 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(); } } termion-1.5.6/examples/color.rs000064400000000000000000000004410000000000000145700ustar 00000000000000extern 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, style::Reset); } termion-1.5.6/examples/commie.rs000064400000000000000000000033070000000000000147270ustar 00000000000000extern 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)); } } termion-1.5.6/examples/detect_color.rs000064400000000000000000000007000000000000000161160ustar 00000000000000extern 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!(); } termion-1.5.6/examples/is_tty.rs000064400000000000000000000003300000000000000147620ustar 00000000000000extern 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 :("); } } termion-1.5.6/examples/keys.rs000064400000000000000000000023560000000000000144340ustar 00000000000000extern 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(); } termion-1.5.6/examples/mouse.rs000064400000000000000000000027210000000000000146050ustar 00000000000000extern 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(); } } termion-1.5.6/examples/rainbow.rs000064400000000000000000000030610000000000000151140ustar 00000000000000extern crate termion; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; use std::io::{Write, stdout, stdin}; fn rainbow(stdout: &mut W, blue: u8) { write!(stdout, "{}{}", termion::cursor::Goto(1, 1), termion::clear::All) .unwrap(); 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(); } termion-1.5.6/examples/read.rs000064400000000000000000000010430000000000000143640ustar 00000000000000extern 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(); } } termion-1.5.6/examples/rustc_fun.rs000064400000000000000000000026200000000000000154630ustar 00000000000000extern 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))) } termion-1.5.6/examples/simple.rs000064400000000000000000000021700000000000000147440ustar 00000000000000extern 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(); } } termion-1.5.6/examples/size.rs000064400000000000000000000001710000000000000144240ustar 00000000000000extern crate termion; use termion::terminal_size; fn main() { println!("Size is {:?}", terminal_size().unwrap()) } termion-1.5.6/examples/truecolor.rs000064400000000000000000000005210000000000000154670ustar 00000000000000extern 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)); } } termion-1.5.6/logo.svg000064400000000000000000000024610000000000000127530ustar 00000000000000 termion-1.5.6/src/async.rs000064400000000000000000000061670000000000000135530ustar 00000000000000use 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, with a delimiter byte. /// /// This has the same advantages as async_stdin(), but also allows specifying a delimiter byte. The /// reader will stop reading after consuming the delimiter byte. pub fn async_stdin_until(delimiter: u8) -> AsyncReader { let (send, recv) = mpsc::channel(); thread::spawn(move || for i in get_tty().unwrap().bytes() { match i { Ok(byte) => { let end_of_stream = &byte == &delimiter; let send_error = send.send(Ok(byte)).is_err(); if end_of_stream || send_error { return; } }, Err(_) => { return; } } }); AsyncReader { recv: recv } } /// 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>, } // 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 { 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(); } } termion-1.5.6/src/clear.rs000064400000000000000000000006230000000000000135130ustar 00000000000000//! 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"); termion-1.5.6/src/color.rs000064400000000000000000000213320000000000000135430ustar 00000000000000//! 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; use std::fmt::Debug; use numtoa::NumToA; /// A terminal color. pub trait Color: Debug { /// 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 { f.write_str(self.fg_str()) } #[inline] fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.bg_str()) } } impl $name { #[inline] /// Returns the ANSI escape sequence as a string. pub fn fg_str(&self) -> &'static str { csi!("38;5;", $value, "m") } #[inline] /// Returns the ANSI escape sequences as a string. pub fn bg_str(&self) -> &'static str { csi!("48;5;", $value, "m") } } }; } 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 dyn 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 AnsiValue { /// Returns the ANSI sequence as a string. pub fn fg_string(self) -> String { let mut x = [0u8; 20]; let x = self.0.numtoa_str(10, &mut x); [csi!("38;5;"), x, "m"].concat() } /// Returns the ANSI sequence as a string. pub fn bg_string(self) -> String { let mut x = [0u8; 20]; let x = self.0.numtoa_str(10, &mut x); [csi!("48;5;"), x, "m"].concat() } } impl Color for AnsiValue { #[inline] fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.fg_string()) } #[inline] fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.bg_string()) } } /// A truecolor RGB. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Rgb(pub u8, pub u8, pub u8); impl Rgb { /// Returns the ANSI sequence as a string. pub fn fg_string(self) -> String { let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]); let (x, y, z) = ( self.0.numtoa_str(10, &mut x), self.1.numtoa_str(10, &mut y), self.2.numtoa_str(10, &mut z), ); [csi!("38;2;"), x, ";", y, ";", z, "m"].concat() } /// Returns the ANSI sequence as a string. pub fn bg_string(self) -> String { let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]); let (x, y, z) = ( self.0.numtoa_str(10, &mut x), self.1.numtoa_str(10, &mut y), self.2.numtoa_str(10, &mut z), ); [csi!("48;2;"), x, ";", y, ";", z, "m"].concat() } } impl Color for Rgb { #[inline] fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.fg_string()) } #[inline] fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(&self.bg_string()) } } /// Reset colors to defaults. #[derive(Debug, Clone, Copy)] pub struct Reset; const RESET_FG: &str = csi!("39m"); const RESET_BG: &str = csi!("49m"); impl Reset { /// Returns the ANSI sequence as a string. pub fn fg_str(self) -> &'static str { RESET_FG } /// Returns the ANSI sequence as a string. pub fn bg_str(self) -> &'static str { RESET_BG } } impl Color for Reset { #[inline] fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(RESET_FG) } #[inline] fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(RESET_BG) } } /// A foreground color. #[derive(Debug, Clone, Copy)] pub struct Fg(pub C); impl fmt::Display for Fg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.write_fg(f) } } /// A background color. #[derive(Debug, Clone, Copy)] pub struct Bg(pub C); impl fmt::Display for Bg { 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; } impl DetectColors for W { fn available_colors(&mut self) -> io::Result { 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 dyn Write, stdin: &mut dyn Read, color: u16) -> io::Result { // 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) } termion-1.5.6/src/cursor.rs000064400000000000000000000143000000000000000137370ustar 00000000000000//! Cursor movement. use std::fmt; use std::ops; use std::io::{self, Write, Error, ErrorKind, Read}; use async::async_stdin_until; use std::time::{SystemTime, Duration}; use raw::CONTROL_SEQUENCE_TIMEOUT; use numtoa::NumToA; 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"); derive_csi_sequence!("Change the cursor style to blinking block", BlinkingBlock, "\x31 q"); derive_csi_sequence!("Change the cursor style to steady block", SteadyBlock, "\x32 q"); derive_csi_sequence!("Change the cursor style to blinking underline", BlinkingUnderline, "\x33 q"); derive_csi_sequence!("Change the cursor style to steady underline", SteadyUnderline, "\x34 q"); derive_csi_sequence!("Change the cursor style to blinking bar", BlinkingBar, "\x35 q"); derive_csi_sequence!("Change the cursor style to steady bar", SteadyBar, "\x36 q"); /// 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 From for String { fn from(this: Goto) -> String { let (mut x, mut y) = ([0u8; 20], [0u8; 20]); ["\x1B[", this.1.numtoa_str(10, &mut x), ";", this.0.numtoa_str(10, &mut y), "H"].concat() } } 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, "\x1B[{};{}H", self.1, self.0) } } /// Move cursor left. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Left(pub u16); impl From for String { fn from(this: Left) -> String { let mut buf = [0u8; 20]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "D"].concat() } } impl fmt::Display for Left { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{}D", self.0) } } /// Move cursor right. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Right(pub u16); impl From for String { fn from(this: Right) -> String { let mut buf = [0u8; 20]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "C"].concat() } } impl fmt::Display for Right { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{}C", self.0) } } /// Move cursor up. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Up(pub u16); impl From for String { fn from(this: Up) -> String { let mut buf = [0u8; 20]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "A"].concat() } } impl fmt::Display for Up { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{}A", self.0) } } /// Move cursor down. #[derive(Copy, Clone, PartialEq, Eq)] pub struct Down(pub u16); impl From for String { fn from(this: Down) -> String { let mut buf = [0u8; 20]; ["\x1B[", this.0.numtoa_str(10, &mut buf), "B"].concat() } } impl fmt::Display for Down { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "\x1B[{}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 DetectCursorPos for W { fn cursor_pos(&mut self) -> io::Result<(u16, u16)> { let delimiter = b'R'; let mut stdin = async_stdin_until(delimiter); // 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] != delimiter && now.elapsed().unwrap() < timeout { if stdin.read(&mut buf)? > 0 { read_chars.push(buf[0]); } } if read_chars.is_empty() { 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::() .unwrap(); let cx = nums.next() .unwrap() .parse::() .unwrap(); Ok((cx, cy)) } } /// Hide the cursor for the lifetime of this struct. /// It will hide the cursor on creation with from() and show it back on drop(). pub struct HideCursor { /// The output target. output: W, } impl HideCursor { /// Create a hide cursor wrapper struct for the provided output and hides the cursor. pub fn from(mut output: W) -> Self { write!(output, "{}", Hide).expect("hide the cursor"); HideCursor { output: output } } } impl Drop for HideCursor { fn drop(&mut self) { write!(self, "{}", Show).expect("show the cursor"); } } impl ops::Deref for HideCursor { type Target = W; fn deref(&self) -> &W { &self.output } } impl ops::DerefMut for HideCursor { fn deref_mut(&mut self) -> &mut W { &mut self.output } } impl Write for HideCursor { fn write(&mut self, buf: &[u8]) -> io::Result { self.output.write(buf) } fn flush(&mut self) -> io::Result<()> { self.output.flush() } } termion-1.5.6/src/event.rs000064400000000000000000000260170000000000000135530ustar 00000000000000//! Mouse and key events. use std::io::{Error, ErrorKind}; 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), } /// 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, /// Backward Tab key. BackTab, /// 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(item: u8, iter: &mut I) -> Result where I: Iterator> { let error = Error::new(ErrorKind::Other, "Could not parse an event"); match item { b'\x1B' => { // This is an escape character, leading a control sequence. Ok(match iter.next() { Some(Ok(b'O')) => { match iter.next() { // F1-F4 Some(Ok(val @ b'P'..=b'S')) => Event::Key(Key::F(1 + val - b'P')), _ => return Err(error), } } 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(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(ch)) }) } } } /// Parses a CSI sequence, just after reading ^[ /// /// Returns None if an unrecognized sequence is found. fn parse_csi(iter: &mut I) -> Option where I: Iterator> { 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'Z')) => Event::Key(Key::BackTab), 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::() .unwrap(); let cx = nums.next() .unwrap() .parse::() .unwrap(); let cy = nums.next() .unwrap() .parse::() .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 = 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 = 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(c: u8, iter: &mut I) -> Result where I: Iterator> { let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8")); if c.is_ascii() { Ok(c as char) } else { let bytes = &mut Vec::new(); bytes.push(c); loop { 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()); } } termion-1.5.6/src/input.rs000064400000000000000000000305000000000000000135610ustar 00000000000000//! 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 { iter: Events, } impl Iterator for Keys { type Item = Result; fn next(&mut self) -> Option> { loop { match self.iter.next() { Some(Ok(Event::Key(k))) => return Some(Ok(k)), Some(Ok(_)) => continue, Some(Err(e)) => return Some(Err(e)), None => return None, }; } } } /// An iterator over input events. pub struct Events { inner: EventsAndRaw } impl Iterator for Events { type Item = Result; fn next(&mut self) -> Option> { self.inner.next().map(|tuple| tuple.map(|(event, _raw)| event)) } } /// An iterator over input events and the bytes that define them. pub struct EventsAndRaw { source: R, leftover: Option, } impl Iterator for EventsAndRaw { type Item = Result<(Event, Vec), io::Error>; fn next(&mut self) -> Option), io::Error>> { let 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 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(item: u8, iter: &mut I) -> Result<(Event, Vec), io::Error> where I: Iterator> { 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 where Self: Sized; /// An iterator over key inputs. fn keys(self) -> Keys 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>; /// Read a password. /// /// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will /// complete the input. fn read_passwd(&mut self, writer: &mut W) -> io::Result> { let _raw = writer.into_raw_mode()?; self.read_line() } } impl TermRead for R { fn events(self) -> Events { Events { inner: self.events_and_raw() } } fn keys(self) -> Keys { Keys { iter: self.events() } } fn read_line(&mut self) -> io::Result> { 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 = 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 where Self: Sized; } impl TermReadEventsAndRaw for R { fn events_and_raw(self) -> EventsAndRaw { 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 { term: W, } impl From for MouseTerminal { fn from(mut from: W) -> MouseTerminal { from.write_all(ENTER_MOUSE_SEQUENCE.as_bytes()).unwrap(); MouseTerminal { term: from } } } impl Drop for MouseTerminal { fn drop(&mut self) { self.term.write_all(EXIT_MOUSE_SEQUENCE.as_bytes()).unwrap(); } } impl ops::Deref for MouseTerminal { type Target = W; fn deref(&self) -> &W { &self.term } } impl ops::DerefMut for MouseTerminal { fn deref_mut(&mut self) -> &mut W { &mut self.term } } impl Write for MouseTerminal { fn write(&mut self, buf: &[u8]) -> io::Result { 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::::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::>(), 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); } } termion-1.5.6/src/lib.rs000064400000000000000000000031360000000000000131750ustar 00000000000000//! 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/redox-os/termion). #![warn(missing_docs)] extern crate numtoa; #[cfg(target_os = "redox")] #[path="sys/redox/mod.rs"] mod sys; #[cfg(all(unix, not(target_os = "redox")))] #[path="sys/unix/mod.rs"] mod sys; pub use sys::size::terminal_size; #[cfg(all(unix, not(target_os = "redox")))] pub use sys::size::terminal_size_pixels; 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(); } } termion-1.5.6/src/macros.rs000064400000000000000000000013220000000000000137060ustar 00000000000000/// 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)) } } impl AsRef<[u8]> for $name { fn as_ref(&self) -> &'static [u8] { csi!($value).as_bytes() } } impl AsRef for $name { fn as_ref(&self) -> &'static str { csi!($value) } } }; } termion-1.5.6/src/raw.rs000064400000000000000000000073240000000000000132230ustar 00000000000000//! 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::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr}; use sys::Termios; /// The timeout of an escape code control sequence, in milliseconds. pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100; /// 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 { prev_ios: Termios, output: W, } impl Drop for RawTerminal { fn drop(&mut self) { set_terminal_attr(&self.prev_ios).unwrap(); } } impl ops::Deref for RawTerminal { type Target = W; fn deref(&self) -> &W { &self.output } } impl ops::DerefMut for RawTerminal { fn deref_mut(&mut self) -> &mut W { &mut self.output } } impl Write for RawTerminal { fn write(&mut self, buf: &[u8]) -> io::Result { self.output.write(buf) } fn flush(&mut self) -> io::Result<()> { self.output.flush() } } #[cfg(unix)] mod unix_impl { use super::*; use std::os::unix::io::{AsRawFd, RawFd}; impl AsRawFd for RawTerminal { fn as_raw_fd(&self) -> RawFd { self.output.as_raw_fd() } } } /// 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>; } impl IntoRawMode for W { fn into_raw_mode(self) -> io::Result> { 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, }) } } impl RawTerminal { /// Temporarily switch to original mode pub fn suspend_raw_mode(&self) -> io::Result<()> { set_terminal_attr(&self.prev_ios)?; Ok(()) } /// Temporarily switch to raw mode pub fn activate_raw_mode(&self) -> io::Result<()> { let mut ios = get_terminal_attr()?; raw_terminal_attr(&mut ios); set_terminal_attr(&ios)?; Ok(()) } } #[cfg(test)] mod test { use super::*; use std::io::{stdout, Write}; #[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); } } termion-1.5.6/src/screen.rs000064400000000000000000000047010000000000000137050ustar 00000000000000//! 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 { /// The output target. output: W, } impl AlternateScreen { /// 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 Drop for AlternateScreen { fn drop(&mut self) { write!(self, "{}", ToMainScreen).expect("switch to main screen"); } } impl ops::Deref for AlternateScreen { type Target = W; fn deref(&self) -> &W { &self.output } } impl ops::DerefMut for AlternateScreen { fn deref_mut(&mut self) -> &mut W { &mut self.output } } impl Write for AlternateScreen { fn write(&mut self, buf: &[u8]) -> io::Result { self.output.write(buf) } fn flush(&mut self) -> io::Result<()> { self.output.flush() } } termion-1.5.6/src/scroll.rs000064400000000000000000000007140000000000000137240ustar 00000000000000//! 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) } } termion-1.5.6/src/style.rs000064400000000000000000000022720000000000000135670ustar 00000000000000//! 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"); termion-1.5.6/src/sys/redox/attr.rs000064400000000000000000000015400000000000000153350ustar 00000000000000use std::io; use super::{cvt, syscall, Termios}; pub fn get_terminal_attr() -> io::Result { let mut termios = Termios::default(); let fd = cvt(syscall::dup(1, 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(1, 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() } termion-1.5.6/src/sys/redox/mod.rs000064400000000000000000000005300000000000000151400ustar 00000000000000extern 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) -> io::Result { result.map_err(|err| io::Error::from_raw_os_error(err.errno)) } termion-1.5.6/src/sys/redox/size.rs000064400000000000000000000010040000000000000153300ustar 00000000000000use 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.")) } } termion-1.5.6/src/sys/redox/tty.rs000064400000000000000000000011550000000000000152050ustar 00000000000000use std::{env, fs, io}; use std::os::unix::io::AsRawFd; use super::syscall; /// Is this stream a TTY? pub fn is_tty(stream: &T) -> bool { if let Ok(fd) = syscall::dup(stream.as_raw_fd() as _, 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 { 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) } termion-1.5.6/src/sys/unix/attr.rs000064400000000000000000000013320000000000000151760ustar 00000000000000use std::{io, mem}; use super::{cvt, Termios}; use super::libc::c_int; pub fn get_terminal_attr() -> io::Result { extern "C" { pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int; } unsafe { let mut termios = mem::zeroed(); cvt(tcgetattr(1, &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(1, 0, termios) }).and(Ok(())) } pub fn raw_terminal_attr(termios: &mut Termios) { extern "C" { pub fn cfmakeraw(termptr: *mut Termios); } unsafe { cfmakeraw(termios) } } termion-1.5.6/src/sys/unix/mod.rs000064400000000000000000000012260000000000000150050ustar 00000000000000extern 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: T) -> io::Result { if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } // } End of support functions termion-1.5.6/src/sys/unix/size.rs000064400000000000000000000014040000000000000151760ustar 00000000000000use std::{io, mem}; use super::cvt; use super::libc::{c_ushort, ioctl, STDOUT_FILENO, TIOCGWINSZ}; #[repr(C)] struct TermSize { row: c_ushort, col: c_ushort, x: c_ushort, y: c_ushort, } /// 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.into(), &mut size as *mut _))?; Ok((size.col as u16, size.row as u16)) } } /// Get the size of the terminal, in pixels pub fn terminal_size_pixels() -> io::Result<(u16, u16)> { unsafe { let mut size: TermSize = mem::zeroed(); cvt(ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &mut size as *mut _))?; Ok((size.x as u16, size.y as u16)) } } termion-1.5.6/src/sys/unix/tty.rs000064400000000000000000000006530000000000000150510ustar 00000000000000use std::{fs, io}; use std::os::unix::io::AsRawFd; use super::libc; /// Is this stream a TTY? pub fn is_tty(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::OpenOptions::new().read(true).write(true).open("/dev/tty") }