зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1716518 - Upgrade atty to v0.2.14. r=emilio
This also removes termion v1.5.1 and redox_termios v0.1.1 Differential Revision: https://phabricator.services.mozilla.com/D117761
This commit is contained in:
Родитель
0487bbb991
Коммит
2a1e8a5766
|
@ -109,12 +109,12 @@ checksum = "681b971236e0f76b20fcafca0236b8718c9186ee778d67cd78bd5f28fd85427f"
|
|||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"termion",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
|
@ -4174,15 +4174,6 @@ version = "0.1.56"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
dependencies = [
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.3.2"
|
||||
|
@ -5076,17 +5067,6 @@ dependencies = [
|
|||
"wincolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.9.0"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"7bd35273865921a87f96ea8d69bd98bde11a96001b74c79cdcdbc83bfbbee53c","Cargo.toml":"77b51a9746012bf939c0fd0a72daa9db816c26798c557c234213a2d38b8aae81","LICENSE":"f3f8d32084848316048c5a1e125a3c5003eb32145a5f5f2a0d5586377324f9ba","README.md":"a62d294c45c9d8b2e54fcf35d9ee1ba8b8e2ab6960fb3d3f4cc9d59e8aed0835","appveyor.yml":"dea9c8da309cbb02bce31c613b697256f4cfada20b2f7b0c8911b73d569daf58","examples/atty.rs":"1551387a71474d9ac1b5153231f884e9e05213badcfaa3494ad2cb7ea958374a","rustfmt.toml":"bd196700242d17913cf8adead6912f55e9347e52ab5a001729d6c18d169f05c4","src/lib.rs":"ec3428266e83b35a8714ab99d9962c9e29f78becb39e313846f042f5b176c723"},"package":"9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"}
|
||||
{"files":{"CHANGELOG.md":"70db121262d72acc472ad1a90b78c42de570820e65b566c6b9339b62e636d572","Cargo.lock":"6868f02a96413bcba37a06f01c6bf87e6331dea9461681a47a561cec6acd2546","Cargo.toml":"3af88a07af6a4adb84373fc3cd4920884b0b12b338cdb55ef598fd512ee1a790","LICENSE":"99fa95ba4e4cdaf71c27d73260ea069fc4515b3d02fde3020c5b562280006cbc","README.md":"e559a69c0b2bd20bffcede64fd548df6c671b0d1504613c5e3e5d884d759caea","examples/atty.rs":"1551387a71474d9ac1b5153231f884e9e05213badcfaa3494ad2cb7ea958374a","rustfmt.toml":"8e6ea1bcb79c505490034020c98e9b472f4ac4113f245bae90f5e1217b1ec65a","src/lib.rs":"d5abf6a54e8c496c486572bdc91eef10480f6ad126c4287f039df5feff7a9bbb"},"package":"d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"}
|
|
@ -1,3 +1,15 @@
|
|||
# 0.2.14
|
||||
|
||||
* add support for [RustyHermit](https://github.com/hermitcore/libhermit-rs), a Rust-based unikernel [#41](https://github.com/softprops/atty/pull/41)
|
||||
|
||||
# 0.2.13
|
||||
|
||||
* support older versions of rust that do now support 2018 edition
|
||||
|
||||
# 0.2.12
|
||||
|
||||
* Redox is now in the unix family so redox cfg is no longer needed [#35](https://github.com/softprops/atty/pull/35)
|
||||
|
||||
# 0.2.11
|
||||
|
||||
* fix msys detection with `winapi@0.3.5` [#28](https://github.com/softprops/atty/pull/28)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum hermit-abi 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eff2656d88f158ce120947499e971d743c05dbcbed62e5bd2f38f1698bbc3772"
|
||||
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
|
||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@ -3,7 +3,7 @@
|
|||
# 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
|
||||
# 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
|
||||
|
@ -12,17 +12,18 @@
|
|||
|
||||
[package]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
version = "0.2.14"
|
||||
authors = ["softprops <d.tangren@gmail.com>"]
|
||||
exclude = ["/.travis.yml", "/appveyor.yml"]
|
||||
description = "A simple interface for querying atty"
|
||||
homepage = "https://github.com/softprops/atty"
|
||||
documentation = "http://softprops.github.io/atty"
|
||||
readme = "README.md"
|
||||
keywords = ["terminal", "tty"]
|
||||
keywords = ["terminal", "tty", "isatty"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/softprops/atty"
|
||||
[target."cfg(target_os = \"redox\")".dependencies.termion]
|
||||
version = "1.5"
|
||||
[target."cfg(target_os = \"hermit\")".dependencies.hermit-abi]
|
||||
version = "0.1.6"
|
||||
[target."cfg(unix)".dependencies.libc]
|
||||
version = "0.2"
|
||||
default-features = false
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2015-2017 Doug Tangren
|
||||
Copyright (c) 2015-2019 Doug Tangren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
|
@ -17,8 +17,6 @@ atty = "0.2"
|
|||
## usage
|
||||
|
||||
```rust
|
||||
extern crate atty;
|
||||
|
||||
use atty::Stream;
|
||||
|
||||
fn main() {
|
||||
|
@ -73,4 +71,4 @@ stderr? false
|
|||
stdin? true
|
||||
```
|
||||
|
||||
Doug Tangren (softprops) 2015-2017
|
||||
Doug Tangren (softprops) 2015-2019
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
environment:
|
||||
matrix:
|
||||
- TARGET: nightly-x86_64-pc-windows-msvc
|
||||
- TARGET: nightly-i686-pc-windows-msvc
|
||||
- TARGET: nightly-x86_64-pc-windows-gnu
|
||||
- TARGET: nightly-i686-pc-windows-gnu
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" -FileName "rust-install.exe"
|
||||
- ps: .\rust-install.exe /VERYSILENT /NORESTART /DIR="C:\rust" | Out-Null
|
||||
- ps: $env:PATH="$env:PATH;C:\rust\bin"
|
||||
- call "%VCVARS%" || ver>nul
|
||||
- rustc -vV
|
||||
- cargo -vV
|
||||
build: false
|
||||
test_script:
|
||||
- cargo build
|
|
@ -1,10 +1,4 @@
|
|||
# keep imports tidy
|
||||
reorder_imported_names = true
|
||||
reorder_imports = true
|
||||
reorder_imports_in_group = true
|
||||
# there is no try!
|
||||
use_try_shorthand = true
|
||||
# don't create rustfmt artifacts
|
||||
write_mode = "Replace"
|
||||
# reduce wide load
|
||||
max_width = 80
|
||||
# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#fn_args_layout
|
||||
fn_args_layout = "Vertical"
|
||||
# https://github.com/rust-lang/rustfmt/blob/master/Configurations.md#merge_imports
|
||||
merge_imports = true
|
|
@ -21,8 +21,6 @@
|
|||
extern crate libc;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
#[cfg(target_os = "redox")]
|
||||
extern crate termion;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
|
@ -50,11 +48,26 @@ pub fn is(stream: Stream) -> bool {
|
|||
unsafe { libc::isatty(fd) != 0 }
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(target_os = "hermit")]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
extern crate hermit_abi;
|
||||
|
||||
let fd = match stream {
|
||||
Stream::Stdout => hermit_abi::STDOUT_FILENO,
|
||||
Stream::Stderr => hermit_abi::STDERR_FILENO,
|
||||
Stream::Stdin => hermit_abi::STDIN_FILENO,
|
||||
};
|
||||
hermit_abi::isatty(fd)
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(windows)]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
use winapi::um::winbase::{STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
|
||||
STD_OUTPUT_HANDLE as STD_OUTPUT};
|
||||
use winapi::um::winbase::{
|
||||
STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
|
||||
STD_OUTPUT_HANDLE as STD_OUTPUT,
|
||||
};
|
||||
|
||||
let (fd, others) = match stream {
|
||||
Stream::Stdin => (STD_INPUT, [STD_ERROR, STD_OUTPUT]),
|
||||
|
@ -88,8 +101,7 @@ pub fn isnt(stream: Stream) -> bool {
|
|||
/// Returns true if any of the given fds are on a console.
|
||||
#[cfg(windows)]
|
||||
unsafe fn console_on_any(fds: &[DWORD]) -> bool {
|
||||
use winapi::um::consoleapi::GetConsoleMode;
|
||||
use winapi::um::processenv::GetStdHandle;
|
||||
use winapi::um::{consoleapi::GetConsoleMode, processenv::GetStdHandle};
|
||||
|
||||
for &fd in fds {
|
||||
let mut out = 0;
|
||||
|
@ -104,15 +116,16 @@ unsafe fn console_on_any(fds: &[DWORD]) -> bool {
|
|||
/// Returns true if there is an MSYS tty on the given handle.
|
||||
#[cfg(windows)]
|
||||
unsafe fn msys_tty_on(fd: DWORD) -> bool {
|
||||
use std::mem;
|
||||
use std::slice;
|
||||
use std::{mem, slice};
|
||||
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::um::winbase::GetFileInformationByHandleEx;
|
||||
use winapi::um::fileapi::FILE_NAME_INFO;
|
||||
use winapi::um::minwinbase::FileNameInfo;
|
||||
use winapi::um::processenv::GetStdHandle;
|
||||
use winapi::shared::minwindef::MAX_PATH;
|
||||
use winapi::{
|
||||
ctypes::c_void,
|
||||
shared::minwindef::MAX_PATH,
|
||||
um::{
|
||||
fileapi::FILE_NAME_INFO, minwinbase::FileNameInfo, processenv::GetStdHandle,
|
||||
winbase::GetFileInformationByHandleEx,
|
||||
},
|
||||
};
|
||||
|
||||
let size = mem::size_of::<FILE_NAME_INFO>();
|
||||
let mut name_info_bytes = vec![0u8; size + MAX_PATH * mem::size_of::<WCHAR>()];
|
||||
|
@ -140,19 +153,6 @@ unsafe fn msys_tty_on(fd: DWORD) -> bool {
|
|||
is_msys && is_pty
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn is(stream: Stream) -> bool {
|
||||
use std::io;
|
||||
use termion::is_tty;
|
||||
|
||||
match stream {
|
||||
Stream::Stdin => is_tty(&io::stdin()),
|
||||
Stream::Stdout => is_tty(&io::stdout()),
|
||||
Stream::Stderr => is_tty(&io::stderr()),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if this is a tty
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn is(_stream: Stream) -> bool {
|
||||
|
@ -161,7 +161,7 @@ pub fn is(_stream: Stream) -> bool {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Stream, is};
|
||||
use super::{is, Stream};
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"e7d20b4132bf22e2c98a5f846f8c16e7628a9181d485fd131b97127ea8719130","LICENSE":"cb46b697c3fd9d27d7bfe1b1ad48f8a58a284984504c6eb215ae2164538df7cb","README.md":"c62ac3d33174db7977cfc7fc77605885f5445c0c9c7d1310dcbe3564c2854edb","src/lib.rs":"af5bc7c59c695880fef0352f9e0f9e9f8682c262a9b1c0fce725b10553647b4e"},"package":"7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"}
|
|
@ -1,26 +0,0 @@
|
|||
# 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 = "redox_termios"
|
||||
version = "0.1.1"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||
description = "A Rust library to access Redox termios functions"
|
||||
documentation = "https://docs.rs/redox_termios"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/redox-os/termios"
|
||||
|
||||
[lib]
|
||||
name = "redox_termios"
|
||||
path = "src/lib.rs"
|
||||
[dependencies.redox_syscall]
|
||||
version = "0.1"
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Redox OS
|
||||
|
||||
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.
|
|
@ -1,2 +0,0 @@
|
|||
# termios
|
||||
Redox Rust termios library
|
|
@ -1,218 +0,0 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
#![no_std]
|
||||
|
||||
extern crate syscall;
|
||||
|
||||
use core::{mem, slice};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
pub type tcflag_t = u32;
|
||||
pub type cc_t = u8;
|
||||
|
||||
/* c_cc { */
|
||||
pub const VEOF: usize = 0;
|
||||
pub const VEOL: usize = 1;
|
||||
pub const VEOL2: usize = 2;
|
||||
pub const VERASE: usize = 3;
|
||||
pub const VWERASE: usize = 4;
|
||||
pub const VKILL: usize = 5;
|
||||
pub const VREPRINT: usize = 6;
|
||||
pub const VSWTC: usize = 7;
|
||||
pub const VINTR: usize = 8;
|
||||
pub const VQUIT: usize = 9;
|
||||
pub const VSUSP: usize = 10;
|
||||
pub const VSTART: usize = 12;
|
||||
pub const VSTOP: usize = 13;
|
||||
pub const VLNEXT: usize = 14;
|
||||
pub const VDISCARD: usize = 15;
|
||||
pub const VMIN: usize = 16;
|
||||
pub const VTIME: usize = 17;
|
||||
pub const NCCS: usize = 32;
|
||||
/* } c_cc */
|
||||
|
||||
/* c_iflag { */
|
||||
pub const IGNBRK: tcflag_t = 0o000001;
|
||||
pub const BRKINT: tcflag_t = 0o000002;
|
||||
pub const IGNPAR: tcflag_t = 0o000004;
|
||||
pub const PARMRK: tcflag_t = 0o000010;
|
||||
pub const INPCK: tcflag_t = 0o000020;
|
||||
pub const ISTRIP: tcflag_t = 0o000040;
|
||||
pub const INLCR: tcflag_t = 0o000100;
|
||||
pub const IGNCR: tcflag_t = 0o000200;
|
||||
pub const ICRNL: tcflag_t = 0o000400;
|
||||
pub const IXON: tcflag_t = 0o001000;
|
||||
pub const IXOFF: tcflag_t = 0o002000;
|
||||
/* } c_iflag */
|
||||
|
||||
/* c_oflag { */
|
||||
pub const OPOST: tcflag_t = 0o000001;
|
||||
pub const ONLCR: tcflag_t = 0o000002;
|
||||
pub const OLCUC: tcflag_t = 0o000004;
|
||||
|
||||
pub const OCRNL: tcflag_t = 0o000010;
|
||||
pub const ONOCR: tcflag_t = 0o000020;
|
||||
pub const ONLRET: tcflag_t = 0o000040;
|
||||
|
||||
pub const OFILL: tcflag_t = 0o0000100;
|
||||
pub const OFDEL: tcflag_t = 0o0000200;
|
||||
/* } c_oflag */
|
||||
|
||||
/* c_cflag { */
|
||||
pub const B0: tcflag_t = 0o000000;
|
||||
pub const B50: tcflag_t = 0o000001;
|
||||
pub const B75: tcflag_t = 0o000002;
|
||||
pub const B110: tcflag_t = 0o000003;
|
||||
pub const B134: tcflag_t = 0o000004;
|
||||
pub const B150: tcflag_t = 0o000005;
|
||||
pub const B200: tcflag_t = 0o000006;
|
||||
pub const B300: tcflag_t = 0o000007;
|
||||
pub const B600: tcflag_t = 0o000010;
|
||||
pub const B1200: tcflag_t = 0o000011;
|
||||
pub const B1800: tcflag_t = 0o000012;
|
||||
pub const B2400: tcflag_t = 0o000013;
|
||||
pub const B4800: tcflag_t = 0o000014;
|
||||
pub const B9600: tcflag_t = 0o000015;
|
||||
pub const B19200: tcflag_t = 0o000016;
|
||||
pub const B38400: tcflag_t = 0o000017;
|
||||
pub const B57600: tcflag_t = 0o0020;
|
||||
pub const B115200: tcflag_t = 0o0021;
|
||||
pub const B230400: tcflag_t = 0o0022;
|
||||
pub const B460800: tcflag_t = 0o0023;
|
||||
pub const B500000: tcflag_t = 0o0024;
|
||||
pub const B576000: tcflag_t = 0o0025;
|
||||
pub const B921600: tcflag_t = 0o0026;
|
||||
pub const B1000000: tcflag_t = 0o0027;
|
||||
pub const B1152000: tcflag_t = 0o0030;
|
||||
pub const B1500000: tcflag_t = 0o0031;
|
||||
pub const B2000000: tcflag_t = 0o0032;
|
||||
pub const B2500000: tcflag_t = 0o0033;
|
||||
pub const B3000000: tcflag_t = 0o0034;
|
||||
pub const B3500000: tcflag_t = 0o0035;
|
||||
pub const B4000000: tcflag_t = 0o0036;
|
||||
|
||||
pub const __MAX_BAUD: tcflag_t = B4000000;
|
||||
|
||||
pub const CSIZE: tcflag_t = 0o0001400;
|
||||
pub const CS5: tcflag_t = 0o0000000;
|
||||
pub const CS6: tcflag_t = 0o0000400;
|
||||
pub const CS7: tcflag_t = 0o0001000;
|
||||
pub const CS8: tcflag_t = 0o0001400;
|
||||
|
||||
pub const CSTOPB: tcflag_t = 0o0002000;
|
||||
pub const CREAD: tcflag_t = 0o0004000;
|
||||
pub const PARENB: tcflag_t = 0o0010000;
|
||||
pub const PARODD: tcflag_t = 0o0020000;
|
||||
pub const HUPCL: tcflag_t = 0o0040000;
|
||||
|
||||
pub const CLOCAL: tcflag_t = 0o0100000;
|
||||
/* } c_clfag */
|
||||
|
||||
/* c_lflag { */
|
||||
pub const ISIG: tcflag_t = 0x00000080;
|
||||
pub const ICANON: tcflag_t = 0x00000100;
|
||||
pub const ECHO: tcflag_t = 0x00000008;
|
||||
pub const ECHOE: tcflag_t = 0x00000002;
|
||||
pub const ECHOK: tcflag_t = 0x00000004;
|
||||
pub const ECHONL: tcflag_t = 0x00000010;
|
||||
pub const NOFLSH: tcflag_t = 0x80000000;
|
||||
pub const TOSTOP: tcflag_t = 0x00400000;
|
||||
pub const IEXTEN: tcflag_t = 0x00000400;
|
||||
/* } c_lflag */
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Termios {
|
||||
pub c_iflag: tcflag_t,
|
||||
pub c_oflag: tcflag_t,
|
||||
pub c_cflag: tcflag_t,
|
||||
pub c_lflag: tcflag_t,
|
||||
pub c_cc: [cc_t; 32]
|
||||
}
|
||||
|
||||
impl Default for Termios {
|
||||
fn default() -> Termios {
|
||||
let mut termios = Termios {
|
||||
c_iflag: ICRNL | IXON,
|
||||
c_oflag: OPOST | ONLCR,
|
||||
c_cflag: B38400 | CS8 | CREAD | HUPCL,
|
||||
c_lflag: ISIG | ICANON | ECHO | ECHOE | ECHOK | IEXTEN,
|
||||
c_cc: [0; 32]
|
||||
};
|
||||
|
||||
{
|
||||
let mut cc = |i: usize, b: cc_t| {
|
||||
termios.c_cc[i] = b;
|
||||
};
|
||||
|
||||
cc(VEOF, 0o004); // CTRL-D
|
||||
cc(VEOL, 0o000); // NUL
|
||||
cc(VEOL2, 0o000); // NUL
|
||||
cc(VERASE, 0o177); // DEL
|
||||
cc(VWERASE, 0o027); // CTRL-W
|
||||
cc(VKILL, 0o025); // CTRL-U
|
||||
cc(VREPRINT, 0o022);// CTRL-R
|
||||
cc(VINTR, 0o003); // CTRL-C
|
||||
cc(VQUIT, 0o034); // CTRL-\
|
||||
cc(VSUSP, 0o032); // CTRL-Z
|
||||
cc(VSTART, 0o021); // CTRL-Q
|
||||
cc(VSTOP, 0o023); // CTRL-S
|
||||
cc(VLNEXT, 0o026); // CTRL-V
|
||||
cc(VDISCARD, 0o017);// CTRL-U
|
||||
cc(VMIN, 1);
|
||||
cc(VTIME, 0);
|
||||
}
|
||||
|
||||
termios
|
||||
}
|
||||
}
|
||||
|
||||
impl Termios {
|
||||
pub fn make_raw(&mut self) {
|
||||
self.c_iflag &= !(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
|
||||
self.c_oflag &= !OPOST;
|
||||
self.c_cflag &= !(CSIZE | PARENB);
|
||||
self.c_cflag |= CS8;
|
||||
self.c_lflag &= !(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Termios {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Termios as *const u8, mem::size_of::<Termios>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Termios {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Termios as *mut u8, mem::size_of::<Termios>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Winsize {
|
||||
pub ws_row: u16,
|
||||
pub ws_col: u16
|
||||
}
|
||||
|
||||
impl Deref for Winsize {
|
||||
type Target = [u8];
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts(self as *const Winsize as *const u8, mem::size_of::<Winsize>()) as &[u8]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Winsize {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(self as *mut Winsize as *mut u8, mem::size_of::<Winsize>()) as &mut [u8]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"b061b09fc4bbf280c932f4b7d3c4641a5a2f0eb8c57029de7d0e4441888a1765","LICENSE":"6252f0c8d4a0df9b2dc0c6464cb2489dbe8859b0eb727e19c14e6af1ee432394","README.md":"71cac837cd6f1326865add7dd565ea2738756648de87fc2d35d0cf22a2512630","examples/alternate_screen.rs":"37978473e77331ad613843049b4f355e32a51a7b8ece9ee52efe02997391e4ec","examples/alternate_screen_raw.rs":"bfd68f86de929952aaed7e1e7175694d771be2a0b0943092fec4f58bf7473dc3","examples/async.rs":"2fdf5fe69edd3b407de3c1c8caf23f9e19a7f25b55578c84838e4305a9857c42","examples/click.rs":"bb3a76f4817292a82b00d92281a86a400038a8282b6c9d41a34dbaca84fb0caf","examples/color.rs":"808219c739677b9f2645e0ae7975a4bd8981255ed8bfc9df2413f8f8059bbda5","examples/commie.rs":"7bb00a7f669c74ccb3a9e9c8ff39fd01aa984f7c38983e5c1cfe6940f90b0c73","examples/detect_color.rs":"764d6465c6879efc38aca8d980a433bac058c1f03d578ac764437af289931824","examples/is_tty.rs":"9a76bdfb11ea84e7b25f6efc06c8d3a8d13a03448bd826bd283b935852f409c7","examples/keys.rs":"cd89f31a21062486e7c014843b360cee35dcee07d8022fc0282eddc48e46bdc7","examples/mouse.rs":"eeccab8043cec987e40e175f03b63c9c6cdc7bcd5808f51a57d0e970cae1c04e","examples/rainbow.rs":"c015176eba7c7a81c6d302d6fc41abea8817935ee804af59d64e3098b127c333","examples/read.rs":"b95fb9b02d2cbf978096825b18ffa25772467df3417c3f3cf5edfe811181a67a","examples/rustc_fun.rs":"f39bd8dbe224cb592d58a0084d15d1eb80d86eade72423fe51acc455e0e1ea68","examples/simple.rs":"eac1aab0d251f884a7d7d7d0d017b6ac4854b38ffcfb244d6a80102ed7a981ab","examples/size.rs":"ae89e7b98a29040f1b641d75226f5738770e2e043cee3f49649accaaeaabbeac","examples/truecolor.rs":"67128ef4870e9e742b1090fa129097484633d82fb015009b1760295f7a24e6af","logo.svg":"09b7a6bca3185acddc217d3d3baab23627c5e03a192f815116e71cf00cb31ddc","src/async.rs":"f0126ff1ee18c8c355bb86deb76e852c5f6bb47fbbaa286cce3d38cb656dfa28","src/clear.rs":"a9cf9a9f92cef2430239dc33d8ecb4e291c46b650f1f71b63248d0a0215768ba","src/color.rs":"2f2c8f6f572b22fc40a62d0502fc192c843dbeed21bb7ecc1954e881d41c9a1c","src/cursor.rs":"9132638902c4e42728efaac247ca363c1bda3e80d6c3cc20bc6928b9fe9a1d10","src/event.rs":"321c0a73cd6208a8ef17083d1bd907e308d5197cfd198a3f1ed1498b7e2b8055","src/input.rs":"a3e226a53b6536aef89d66a1e4974453389c86116c175c3efc4fc4bb9b41d60c","src/lib.rs":"549beee5f5a6fb7954af37df7fa598ba6c716f2287123e09d300e048af2a70ac","src/macros.rs":"8611f257ffc046ed25870611f1353a96e17a5671efbe2bbf33aae525b53a4df3","src/raw.rs":"6949b164cf1bd0a4146c169b948100ed626b3e0747df15a54b7f41853d348ea1","src/screen.rs":"19a9a2c42abe2afdb75d4850dc6ee54f33808f6da7f59644668e683418cbce4a","src/scroll.rs":"d0ba08663127e09e111fafc9f84c1fe42662b74f9acbca6b5d26d0b335ee4136","src/style.rs":"b2c805d710cf553835a01482f014aff7395f79d5f71f1e5029ae679e002ac010","src/sys/redox/attr.rs":"834d21cc17090fb7989906ce885f4efed252d65086c05e32ecc05792eb69151a","src/sys/redox/mod.rs":"4066ba97f10c8b87a6ebf32257c89d36338366e1230781aeb5fca98bd5f5bae0","src/sys/redox/size.rs":"b892d0053f40c343eeb40ea06a10fb77abd2758eecda6a7cccb0b07c07fff6eb","src/sys/redox/tty.rs":"3369a6ee3a21400053a023c01912bdd92b709915b833e25e557214dfad425224","src/sys/unix/attr.rs":"7b17e4841eab69533d2561764506211e2967cd4d7464a309e6d2832473b75dd7","src/sys/unix/mod.rs":"8ddebce9f5b2dbbd419519beb7d41f6d6a7eaed40a70cd0d0676ee893549f7a7","src/sys/unix/size.rs":"19f3de9ced2b329a50a9752ba1be406b25648b298c1a2b3e83582a34e21d2998","src/sys/unix/tty.rs":"eeea0279f76838aa4badd807325403d32ab359334cf59f9574191afcbe86c811"},"package":"689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"}
|
|
@ -1,17 +0,0 @@
|
|||
[package]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
authors = ["ticki <Ticki@users.noreply.github.com>", "gycos <alexandre.bury@gmail.com>", "IGI-111 <igi-111@protonmail.com>"]
|
||||
description = "A bindless library for manipulating terminals."
|
||||
repository = "https://github.com/ticki/termion"
|
||||
documentation = "https://docs.rs/termion"
|
||||
license = "MIT"
|
||||
keywords = ["tty", "color", "terminal", "password", "tui"]
|
||||
exclude = ["target", "CHANGELOG.md", "image.png", "Cargo.lock"]
|
||||
|
||||
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||
libc = "0.2.8"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox_syscall = "0.1"
|
||||
redox_termios = "0.1"
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Ticki
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,181 +0,0 @@
|
|||
<p align="center">
|
||||
<img alt="Termion logo" src="https://rawgit.com/ticki/termion/master/logo.svg" />
|
||||
</p>
|
||||
|
||||
[![Build Status](https://travis-ci.org/ticki/termion.svg?branch=master)](https://travis-ci.org/ticki/termion) [![Latest Version](https://img.shields.io/crates/v/termion.svg)](https://crates.io/crates/termion) | [Documentation](https://docs.rs/termion) | [Examples](https://github.com/Ticki/termion/tree/master/examples) | [Changelog](https://github.com/Ticki/termion/tree/master/CHANGELOG.md) | [Tutorial](http://ticki.github.io/blog/making-terminal-applications-in-rust-with-termion/)
|
||||
|----|----|----|----|----
|
||||
|
||||
|
||||
**Termion** is a pure Rust, bindless library for low-level handling, manipulating
|
||||
and reading information about terminals. This provides a full-featured
|
||||
alternative to Termbox.
|
||||
|
||||
Termion aims to be simple and yet expressive. It is bindless, meaning that it
|
||||
is not a front-end to some other library (e.g., ncurses or termbox), but a
|
||||
standalone library directly talking to the TTY.
|
||||
|
||||
Termion is quite convenient, due to its complete coverage of essential TTY
|
||||
features, providing one consistent API. Termion is rather low-level containing
|
||||
only abstraction aligned with what actually happens behind the scenes. For
|
||||
something more high-level, refer to inquirer-rs, which uses Termion as backend.
|
||||
|
||||
Termion generates escapes and API calls for the user. This makes it a whole lot
|
||||
cleaner to use escapes.
|
||||
|
||||
Supports Redox, Mac OS X, BSD, and Linux (or, in general, ANSI terminals).
|
||||
|
||||
## A note on stability
|
||||
|
||||
This crate is stable.
|
||||
|
||||
## Cargo.toml
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
termion = "*"
|
||||
```
|
||||
|
||||
## 0.1.0 to 1.0.0 guide
|
||||
|
||||
This sample table gives an idea of how to go about converting to the new major
|
||||
version of Termion.
|
||||
|
||||
| 0.1.0 | 1.0.0
|
||||
|--------------------------------|---------------------------
|
||||
| `use termion::IntoRawMode` | `use termion::raw::IntoRawMode`
|
||||
| `use termion::TermRead` | `use termion::input::TermRead`
|
||||
| `stdout.color(color::Red);` | `write!(stdout, "{}", color::Fg(color::Red));`
|
||||
| `stdout.color_bg(color::Red);` | `write!(stdout, "{}", color::Bg(color::Red));`
|
||||
| `stdout.goto(x, y);` | `write!(stdout, "{}", cursor::Goto(x, y));`
|
||||
| `color::rgb(r, g, b);` | `color::Rgb(r, g, b)` (truecolor)
|
||||
| `x.with_mouse()` | `MouseTerminal::from(x)`
|
||||
|
||||
## Features
|
||||
|
||||
- Raw mode.
|
||||
- TrueColor.
|
||||
- 256-color mode.
|
||||
- Cursor movement.
|
||||
- Text formatting.
|
||||
- Console size.
|
||||
- TTY-only stream.
|
||||
- Control sequences.
|
||||
- Termios control.
|
||||
- Password input.
|
||||
- Redox support.
|
||||
- Safe `isatty` wrapper.
|
||||
- Panic-free error handling.
|
||||
- Special keys events (modifiers, special keys, etc.).
|
||||
- Allocation-free.
|
||||
- Asynchronous key events.
|
||||
- Mouse input.
|
||||
- Carefully tested.
|
||||
- Detailed documentation on every item.
|
||||
|
||||
and much more.
|
||||
|
||||
## Examples
|
||||
|
||||
### Style and colors.
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
use std::io;
|
||||
|
||||
fn main() {
|
||||
println!("{}Red", color::Fg(color::Red));
|
||||
println!("{}Blue", color::Fg(color::Blue));
|
||||
println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
|
||||
println!("{}Just plain italic", style::Italic);
|
||||
}
|
||||
```
|
||||
|
||||
### Moving the cursor
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
fn main() {
|
||||
print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(1, 1));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Mouse
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::event::{Key, Event, MouseEvent};
|
||||
use termion::input::{TermRead, MouseTerminal};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());
|
||||
|
||||
write!(stdout, "{}{}q to exit. Click, click, click!", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c.unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
match me {
|
||||
MouseEvent::Press(_, x, y) => {
|
||||
write!(stdout, "{}x", termion::cursor::Goto(x, y)).unwrap();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Read a password
|
||||
|
||||
```rust
|
||||
extern crate termion;
|
||||
|
||||
use termion::input::TermRead;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
let stdin = stdin();
|
||||
let mut stdin = stdin.lock();
|
||||
|
||||
stdout.write_all(b"password: ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
let pass = stdin.read_passwd(&mut stdout);
|
||||
|
||||
if let Ok(Some(pass)) = pass {
|
||||
stdout.write_all(pass.as_bytes()).unwrap();
|
||||
stdout.write_all(b"\n").unwrap();
|
||||
} else {
|
||||
stdout.write_all(b"Error\n").unwrap();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
See `examples/`, and the documentation, which can be rendered using `cargo doc`.
|
||||
|
||||
For a more complete example, see [a minesweeper implementation](https://github.com/redox-os/games-for-redox/blob/master/src/minesweeper/main.rs), that I made for Redox using termion.
|
||||
|
||||
<img src="image.png" width="200">
|
||||
|
||||
## License
|
||||
|
||||
MIT/X11.
|
|
@ -1,17 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::screen::*;
|
||||
use std::io::{Write, stdout};
|
||||
use std::{time, thread};
|
||||
|
||||
fn main() {
|
||||
{
|
||||
let mut screen = AlternateScreen::from(stdout());
|
||||
write!(screen, "Welcome to the alternate screen.\n\nPlease wait patiently until we arrive back at the main screen in a about three seconds.").unwrap();
|
||||
screen.flush().unwrap();
|
||||
|
||||
thread::sleep(time::Duration::from_secs(3));
|
||||
}
|
||||
|
||||
println!("Phew! We are back.");
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::*;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn write_alt_screen_msg<W: Write>(screen: &mut W) {
|
||||
write!(screen, "{}{}Welcome to the alternate screen.{}Press '1' to switch to the main screen or '2' to switch to the alternate screen.{}Press 'q' to exit (and switch back to the main screen).",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Goto(1, 3),
|
||||
termion::cursor::Goto(1, 4)).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut screen = AlternateScreen::from(stdout().into_raw_mode().unwrap());
|
||||
write!(screen, "{}", termion::cursor::Hide).unwrap();
|
||||
write_alt_screen_msg(&mut screen);
|
||||
|
||||
screen.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Char('q') => break,
|
||||
Key::Char('1') => {
|
||||
write!(screen, "{}", ToMainScreen).unwrap();
|
||||
}
|
||||
Key::Char('2') => {
|
||||
write!(screen, "{}", ToAlternateScreen).unwrap();
|
||||
write_alt_screen_msg(&mut screen);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
screen.flush().unwrap();
|
||||
}
|
||||
write!(screen, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::async_stdin;
|
||||
use std::io::{Read, Write, stdout};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||
let mut stdin = async_stdin().bytes();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1))
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
write!(stdout, "{}", termion::clear::CurrentLine).unwrap();
|
||||
|
||||
let b = stdin.next();
|
||||
write!(stdout, "\r{:?} <- This demonstrates the async read input char. Between each update a 100 ms. is waited, simply to demonstrate the async fashion. \n\r", b).unwrap();
|
||||
if let Some(Ok(b'q')) = b {
|
||||
break;
|
||||
}
|
||||
|
||||
stdout.flush().unwrap();
|
||||
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
stdout.write_all(b"# ").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
stdout.write_all(b"\r #").unwrap();
|
||||
write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
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,10 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
println!("{}Red", color::Fg(color::Red));
|
||||
println!("{}Blue", color::Fg(color::Blue));
|
||||
println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
|
||||
println!("{}Just plain italic", style::Italic);
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{clear, color, cursor};
|
||||
|
||||
use std::{time, thread};
|
||||
|
||||
const COMMUNISM: &'static str = r#"
|
||||
!######### #
|
||||
!########! ##!
|
||||
!########! ###
|
||||
!########## ####
|
||||
######### ##### ######
|
||||
!###! !####! ######
|
||||
! ##### ######!
|
||||
!####! #######
|
||||
##### #######
|
||||
!####! #######!
|
||||
####!########
|
||||
## ##########
|
||||
,######! !#############
|
||||
,#### ########################!####!
|
||||
,####' ##################!' #####
|
||||
,####' ####### !####!
|
||||
####' #####
|
||||
~## ##~
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let mut state = 0;
|
||||
|
||||
println!("\n{}{}{}{}{}{}",
|
||||
cursor::Hide,
|
||||
clear::All,
|
||||
cursor::Goto(1, 1),
|
||||
color::Fg(color::Black),
|
||||
color::Bg(color::Red),
|
||||
COMMUNISM);
|
||||
loop {
|
||||
println!("{}{} ☭ GAY ☭ SPACE ☭ COMMUNISM ☭ ",
|
||||
cursor::Goto(1, 1),
|
||||
color::Bg(color::AnsiValue(state)));
|
||||
println!("{}{} WILL PREVAIL, COMRADES! ",
|
||||
cursor::Goto(1, 20),
|
||||
color::Bg(color::AnsiValue(state)));
|
||||
|
||||
state += 1;
|
||||
state %= 8;
|
||||
|
||||
thread::sleep(time::Duration::from_millis(90));
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::color::{DetectColors, AnsiValue, Bg};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::stdout;
|
||||
|
||||
fn main() {
|
||||
let count;
|
||||
{
|
||||
let mut term = stdout().into_raw_mode().unwrap();
|
||||
count = term.available_colors().unwrap();
|
||||
}
|
||||
|
||||
println!("This terminal supports {} colors.", count);
|
||||
for i in 0..count {
|
||||
print!("{} {}", Bg(AnsiValue(i as u8)), Bg(AnsiValue(0)));
|
||||
}
|
||||
println!();
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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,44 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}q to exit. Type stuff, use alt, and so on.{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Hide)
|
||||
.unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
for c in stdin.keys() {
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::clear::CurrentLine)
|
||||
.unwrap();
|
||||
|
||||
match c.unwrap() {
|
||||
Key::Char('q') => break,
|
||||
Key::Char(c) => println!("{}", c),
|
||||
Key::Alt(c) => println!("^{}", c),
|
||||
Key::Ctrl(c) => println!("*{}", c),
|
||||
Key::Esc => println!("ESC"),
|
||||
Key::Left => println!("←"),
|
||||
Key::Right => println!("→"),
|
||||
Key::Up => println!("↑"),
|
||||
Key::Down => println!("↓"),
|
||||
Key::Backspace => println!("×"),
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::*;
|
||||
use termion::cursor::{self, DetectCursorPos};
|
||||
use termion::input::{TermRead, MouseTerminal};
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
let stdin = io::stdin();
|
||||
let mut stdout = MouseTerminal::from(io::stdout().into_raw_mode().unwrap());
|
||||
|
||||
writeln!(stdout,
|
||||
"{}{}q to exit. Type stuff, use alt, click around...",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1))
|
||||
.unwrap();
|
||||
|
||||
for c in stdin.events() {
|
||||
let evt = c.unwrap();
|
||||
match evt {
|
||||
Event::Key(Key::Char('q')) => break,
|
||||
Event::Mouse(me) => {
|
||||
match me {
|
||||
MouseEvent::Press(_, a, b) |
|
||||
MouseEvent::Release(a, b) |
|
||||
MouseEvent::Hold(a, b) => {
|
||||
write!(stdout, "{}", cursor::Goto(a, b)).unwrap();
|
||||
let (x, y) = stdout.cursor_pos().unwrap();
|
||||
write!(stdout,
|
||||
"{}{}Cursor is at: ({},{}){}",
|
||||
cursor::Goto(5, 5),
|
||||
termion::clear::UntilNewline,
|
||||
x,
|
||||
y,
|
||||
cursor::Goto(a, b))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::event::Key;
|
||||
use termion::input::TermRead;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Write, stdout, stdin};
|
||||
|
||||
fn rainbow<W: Write>(stdout: &mut W, blue: u8) {
|
||||
write!(stdout,
|
||||
"{}{}",
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::clear::All)
|
||||
.unwrap();
|
||||
|
||||
for red in 0..32 {
|
||||
let red = red * 8;
|
||||
for green in 0..64 {
|
||||
let green = green * 4;
|
||||
write!(stdout,
|
||||
"{} ",
|
||||
termion::color::Bg(termion::color::Rgb(red, green, blue)))
|
||||
.unwrap();
|
||||
}
|
||||
write!(stdout, "\n\r").unwrap();
|
||||
}
|
||||
|
||||
writeln!(stdout, "{}b = {}", termion::style::Reset, blue).unwrap();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let stdin = stdin();
|
||||
let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
|
||||
writeln!(stdout,
|
||||
"{}{}{}Use the up/down arrow keys to change the blue in the rainbow.",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(1, 1),
|
||||
termion::cursor::Hide)
|
||||
.unwrap();
|
||||
|
||||
let mut blue = 172u8;
|
||||
|
||||
for c in stdin.keys() {
|
||||
match c.unwrap() {
|
||||
Key::Up => {
|
||||
blue = blue.saturating_add(4);
|
||||
rainbow(&mut stdout, blue);
|
||||
}
|
||||
Key::Down => {
|
||||
blue = blue.saturating_sub(4);
|
||||
rainbow(&mut stdout, blue);
|
||||
}
|
||||
Key::Char('q') => break,
|
||||
_ => {}
|
||||
}
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
|
||||
write!(stdout, "{}", termion::cursor::Show).unwrap();
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::{color, style};
|
||||
|
||||
fn main() {
|
||||
println!("{lighgreen}-- src/test/ui/borrow-errors.rs at 82:18 --\n\
|
||||
{red}error: {reset}{bold}two closures require unique access to `vec` at the same time {reset}{bold}{magenta}[E0524]{reset}\n\
|
||||
{line_num_fg}{line_num_bg}79 {reset} let append = |e| {{\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}first closure is constructed here\n\
|
||||
{line_num_fg}{line_num_bg}80 {reset} vec.push(e)\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^^^{reset} {error_fg}previous borrow occurs due to use of `vec` in closure\n\
|
||||
{line_num_fg}{line_num_bg}84 {reset} }};\n\
|
||||
{line_num_fg}{line_num_bg}85 {reset} }}\n\
|
||||
{line_num_fg}{line_num_bg}{info_line}{reset} {red}^{reset} {error_fg}borrow from first closure ends here",
|
||||
lighgreen = color::Fg(color::LightGreen),
|
||||
red = color::Fg(color::Red),
|
||||
bold = style::Bold,
|
||||
reset = style::Reset,
|
||||
magenta = color::Fg(color::Magenta),
|
||||
line_num_bg = color::Bg(color::AnsiValue::grayscale(3)),
|
||||
line_num_fg = color::Fg(color::AnsiValue::grayscale(18)),
|
||||
info_line = "| ",
|
||||
error_fg = color::Fg(color::AnsiValue::grayscale(17)))
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::color;
|
||||
use termion::raw::IntoRawMode;
|
||||
use std::io::{Read, Write, stdout, stdin};
|
||||
|
||||
fn main() {
|
||||
// Initialize 'em all.
|
||||
let stdout = stdout();
|
||||
let mut stdout = stdout.lock().into_raw_mode().unwrap();
|
||||
let stdin = stdin();
|
||||
let stdin = stdin.lock();
|
||||
|
||||
write!(stdout,
|
||||
"{}{}{}yo, 'q' will exit.{}{}",
|
||||
termion::clear::All,
|
||||
termion::cursor::Goto(5, 5),
|
||||
termion::style::Bold,
|
||||
termion::style::Reset,
|
||||
termion::cursor::Goto(20, 10))
|
||||
.unwrap();
|
||||
stdout.flush().unwrap();
|
||||
|
||||
let mut bytes = stdin.bytes();
|
||||
loop {
|
||||
let b = bytes.next().unwrap().unwrap();
|
||||
|
||||
match b {
|
||||
// Quit
|
||||
b'q' => return,
|
||||
// Clear the screen
|
||||
b'c' => write!(stdout, "{}", termion::clear::All),
|
||||
// Set red color
|
||||
b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))),
|
||||
// Write it to stdout.
|
||||
a => write!(stdout, "{}", a),
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
extern crate termion;
|
||||
|
||||
use termion::terminal_size;
|
||||
|
||||
fn main() {
|
||||
println!("Size is {:?}", terminal_size().unwrap())
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="60.099598mm" height="18.291185mm" viewBox="0 0 212.95 64.81">
|
||||
<style>
|
||||
.blink { animation: blinker 3s linear infinite; } @keyframes blinker { 50% { opacity: 0; } }
|
||||
</style>
|
||||
|
||||
<path d="M0 0h212.95v64.82H0z" opacity=".71"/>
|
||||
<path fill="#f9f9f9" d="M12.24 17.8H34.5v3.33h-9.13v25.84H21.4V21.13h-9.16V17.8zm27 0h17.3v3.33H43.2v8.63h12.77v3.32h-12.8v10.57H56.9v3.32H39.24V17.8zM74.3 33.2q1.5.4 2.6 1.48 1.06 1.08 2.66 4.32l3.97 7.97H79.3l-3.5-7.37q-1.5-3.14-2.7-4.04-1.2-.92-3.13-.92H66.2v12.33h-3.96V17.8h8.13q4.8 0 7.36 2.17 2.56 2.17 2.56 6.27 0 2.9-1.6 4.73-1.6 1.82-4.4 2.23zm-8.1-12.15V31.4h4.32q2.83 0 4.22-1.27 1.4-1.27 1.4-3.9 0-2.5-1.5-3.83-1.46-1.35-4.27-1.35H66.2zm19-3.25h5.26l5.04 14.85 5.08-14.84h5.3V47h-3.66V21.2l-5.2 15.38h-2.98L88.82 21.2v25.77H85.2V17.8zm26.3 0h16.2v3.33h-6.12v22.52h6.1v3.32H111.5v-3.32h6.1V21.13h-6.1V17.8zm37.8 14.62q0-6.43-1.32-9.18-1.3-2.76-4.3-2.76t-4.33 2.76q-1.3 2.75-1.3 9.18 0 6.4 1.3 9.16 1.33 2.75 4.32 2.75 3 0 4.3-2.73 1.34-2.76 1.34-9.18zm4.13 0q0 7.6-2.42 11.36-2.4 3.75-7.3 3.75t-7.3-3.73q-2.4-3.73-2.4-11.38 0-7.64 2.4-11.4 2.4-3.74 7.35-3.74t7.34 3.75q2.42 3.75 2.42 11.4zm4.97-14.62h5l9.86 24v-24h3.8v29.17h-5l-9.84-24v24h-3.8V17.8z"/>
|
||||
<path fill="#f9f9f9" d="M192.7 8.66v47.5h-3.93V8.66h3.94z" class="blink"/>
|
||||
</svg>
|
До Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -1,78 +0,0 @@
|
|||
use std::io::{self, Read};
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use sys::tty::get_tty;
|
||||
|
||||
/// Construct an asynchronous handle to the TTY standard input.
|
||||
///
|
||||
/// This allows you to read from standard input _without blocking_ the current thread.
|
||||
/// Specifically, it works by firing up another thread to handle the event stream, which will then
|
||||
/// be buffered in a mpsc queue, which will eventually be read by the current thread.
|
||||
///
|
||||
/// This will not read the piped standard input, but rather read from the TTY device, since reading
|
||||
/// asyncronized from piped input would rarely make sense. In other words, if you pipe standard
|
||||
/// output from another process, it won't be reflected in the stream returned by this function, as
|
||||
/// this represents the TTY device, and not the piped standard input.
|
||||
pub fn async_stdin() -> AsyncReader {
|
||||
let (send, recv) = mpsc::channel();
|
||||
|
||||
thread::spawn(move || for i in get_tty().unwrap().bytes() {
|
||||
if send.send(i).is_err() {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
AsyncReader { recv: recv }
|
||||
}
|
||||
|
||||
/// An asynchronous reader.
|
||||
///
|
||||
/// This acts as any other stream, with the exception that reading from it won't block. Instead,
|
||||
/// the buffer will only be partially updated based on how much the internal buffer holds.
|
||||
pub struct AsyncReader {
|
||||
/// The underlying mpsc receiver.
|
||||
recv: mpsc::Receiver<io::Result<u8>>,
|
||||
}
|
||||
|
||||
// FIXME: Allow constructing an async reader from an arbitrary stream.
|
||||
|
||||
impl Read for AsyncReader {
|
||||
/// Read from the byte stream.
|
||||
///
|
||||
/// This will never block, but try to drain the event queue until empty. If the total number of
|
||||
/// bytes written is lower than the buffer's length, the event queue is empty or that the event
|
||||
/// stream halted.
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let mut total = 0;
|
||||
|
||||
loop {
|
||||
if total >= buf.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
match self.recv.try_recv() {
|
||||
Ok(Ok(b)) => {
|
||||
buf[total] = b;
|
||||
total += 1;
|
||||
}
|
||||
Ok(Err(e)) => return Err(e),
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(total)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::Read;
|
||||
|
||||
#[test]
|
||||
fn test_async_stdin() {
|
||||
let stdin = async_stdin();
|
||||
stdin.bytes().next();
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
//! 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");
|
|
@ -1,242 +0,0 @@
|
|||
//! Color managemement.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use termion::color;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! println!("{}Red", color::Fg(color::Red));
|
||||
//! println!("{}Blue", color::Fg(color::Blue));
|
||||
//! println!("{}Back again", color::Fg(color::Reset));
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
use raw::CONTROL_SEQUENCE_TIMEOUT;
|
||||
use std::io::{self, Write, Read};
|
||||
use std::time::{SystemTime, Duration};
|
||||
use async::async_stdin;
|
||||
use std::env;
|
||||
|
||||
/// A terminal color.
|
||||
pub trait Color {
|
||||
/// Write the foreground version of this color.
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
/// Write the background version of this color.
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
|
||||
}
|
||||
|
||||
macro_rules! derive_color {
|
||||
($doc:expr, $name:ident, $value:expr) => {
|
||||
#[doc = $doc]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct $name;
|
||||
|
||||
impl Color for $name {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;5;", $value, "m"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;5;", $value, "m"))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
derive_color!("Black.", Black, "0");
|
||||
derive_color!("Red.", Red, "1");
|
||||
derive_color!("Green.", Green, "2");
|
||||
derive_color!("Yellow.", Yellow, "3");
|
||||
derive_color!("Blue.", Blue, "4");
|
||||
derive_color!("Magenta.", Magenta, "5");
|
||||
derive_color!("Cyan.", Cyan, "6");
|
||||
derive_color!("White.", White, "7");
|
||||
derive_color!("High-intensity light black.", LightBlack, "8");
|
||||
derive_color!("High-intensity light red.", LightRed, "9");
|
||||
derive_color!("High-intensity light green.", LightGreen, "10");
|
||||
derive_color!("High-intensity light yellow.", LightYellow, "11");
|
||||
derive_color!("High-intensity light blue.", LightBlue, "12");
|
||||
derive_color!("High-intensity light magenta.", LightMagenta, "13");
|
||||
derive_color!("High-intensity light cyan.", LightCyan, "14");
|
||||
derive_color!("High-intensity light white.", LightWhite, "15");
|
||||
|
||||
impl<'a> Color for &'a Color {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(*self).write_fg(f)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
(*self).write_bg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary ANSI color value.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct AnsiValue(pub u8);
|
||||
|
||||
impl AnsiValue {
|
||||
/// 216-color (r, g, b ≤ 5) RGB.
|
||||
pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
|
||||
debug_assert!(r <= 5,
|
||||
"Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
|
||||
r);
|
||||
debug_assert!(g <= 5,
|
||||
"Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
|
||||
g);
|
||||
debug_assert!(b <= 5,
|
||||
"Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
|
||||
b);
|
||||
|
||||
AnsiValue(16 + 36 * r + 6 * g + b)
|
||||
}
|
||||
|
||||
/// Grayscale color.
|
||||
///
|
||||
/// There are 24 shades of gray.
|
||||
pub fn grayscale(shade: u8) -> AnsiValue {
|
||||
// Unfortunately, there are a little less than fifty shades.
|
||||
debug_assert!(shade < 24,
|
||||
"Grayscale out of bound (shade = {}). There are only 24 shades of \
|
||||
gray.",
|
||||
shade);
|
||||
|
||||
AnsiValue(0xE8 + shade)
|
||||
}
|
||||
}
|
||||
|
||||
impl Color for AnsiValue {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;5;{}m"), self.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;5;{}m"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A truecolor RGB.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Rgb(pub u8, pub u8, pub u8);
|
||||
|
||||
impl Color for Rgb {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("38;2;{};{};{}m"), self.0, self.1, self.2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("48;2;{};{};{}m"), self.0, self.1, self.2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset colors to defaults.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Reset;
|
||||
|
||||
impl Color for Reset {
|
||||
#[inline]
|
||||
fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("39m"))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("49m"))
|
||||
}
|
||||
}
|
||||
|
||||
/// A foreground color.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Fg<C: Color>(pub C);
|
||||
|
||||
impl<C: Color> fmt::Display for Fg<C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.write_fg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A background color.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Bg<C: Color>(pub C);
|
||||
|
||||
impl<C: Color> fmt::Display for Bg<C> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.write_bg(f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that allow detection of the colors they support.
|
||||
pub trait DetectColors {
|
||||
/// How many ANSI colors are supported (from 8 to 256)?
|
||||
///
|
||||
/// Beware: the information given isn't authoritative, it's infered through escape codes or the
|
||||
/// value of `TERM`, more colors may be available.
|
||||
fn available_colors(&mut self) -> io::Result<u16>;
|
||||
}
|
||||
|
||||
impl<W: Write> DetectColors for W {
|
||||
fn available_colors(&mut self) -> io::Result<u16> {
|
||||
let mut stdin = async_stdin();
|
||||
|
||||
if detect_color(self, &mut stdin, 0)? {
|
||||
// OSC 4 is supported, detect how many colors there are.
|
||||
// Do a binary search of the last supported color.
|
||||
let mut min = 8;
|
||||
let mut max = 256;
|
||||
let mut i;
|
||||
while min + 1 < max {
|
||||
i = (min + max) / 2;
|
||||
if detect_color(self, &mut stdin, i)? {
|
||||
min = i
|
||||
} else {
|
||||
max = i
|
||||
}
|
||||
}
|
||||
Ok(max)
|
||||
} else {
|
||||
// OSC 4 is not supported, trust TERM contents.
|
||||
Ok(match env::var_os("TERM") {
|
||||
Some(val) => {
|
||||
if val.to_str().unwrap_or("").contains("256color") {
|
||||
256
|
||||
} else {
|
||||
8
|
||||
}
|
||||
}
|
||||
None => 8,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Detect a color using OSC 4.
|
||||
fn detect_color(stdout: &mut Write, stdin: &mut Read, color: u16) -> io::Result<bool> {
|
||||
// Is the color available?
|
||||
// Use `ESC ] 4 ; color ; ? BEL`.
|
||||
write!(stdout, "\x1B]4;{};?\x07", color)?;
|
||||
stdout.flush()?;
|
||||
|
||||
let mut buf: [u8; 1] = [0];
|
||||
let mut total_read = 0;
|
||||
|
||||
let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
|
||||
let now = SystemTime::now();
|
||||
let bell = 7u8;
|
||||
|
||||
// Either consume all data up to bell or wait for a timeout.
|
||||
while buf[0] != bell && now.elapsed().unwrap() < timeout {
|
||||
total_read += stdin.read(&mut buf)?;
|
||||
}
|
||||
|
||||
// If there was a response, the color is supported.
|
||||
Ok(total_read > 0)
|
||||
}
|
|
@ -1,140 +0,0 @@
|
|||
//! Cursor movement.
|
||||
|
||||
use std::fmt;
|
||||
use std::io::{self, Write, Error, ErrorKind, Read};
|
||||
use async::async_stdin;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use raw::CONTROL_SEQUENCE_TIMEOUT;
|
||||
|
||||
derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
|
||||
derive_csi_sequence!("Show the cursor.", Show, "?25h");
|
||||
|
||||
derive_csi_sequence!("Restore the cursor.", Restore, "u");
|
||||
derive_csi_sequence!("Save the cursor.", Save, "s");
|
||||
|
||||
/// Goto some position ((1,1)-based).
|
||||
///
|
||||
/// # Why one-based?
|
||||
///
|
||||
/// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
|
||||
/// can be quite strange at first, but it is not that big of an obstruction once you get used to
|
||||
/// it.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate termion;
|
||||
///
|
||||
/// fn main() {
|
||||
/// print!("{}{}Stuff", termion::clear::All, termion::cursor::Goto(5, 3));
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Goto(pub u16, pub u16);
|
||||
|
||||
impl Default for Goto {
|
||||
fn default() -> Goto {
|
||||
Goto(1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Goto {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
|
||||
|
||||
write!(f, csi!("{};{}H"), self.1, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor left.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Left(pub u16);
|
||||
|
||||
impl fmt::Display for Left {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}D"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor right.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Right(pub u16);
|
||||
|
||||
impl fmt::Display for Right {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}C"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor up.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Up(pub u16);
|
||||
|
||||
impl fmt::Display for Up {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}A"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Move cursor down.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Down(pub u16);
|
||||
|
||||
impl fmt::Display for Down {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("{}B"), self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Types that allow detection of the cursor position.
|
||||
pub trait DetectCursorPos {
|
||||
/// Get the (1,1)-based cursor position from the terminal.
|
||||
fn cursor_pos(&mut self) -> io::Result<(u16, u16)>;
|
||||
}
|
||||
|
||||
impl<W: Write> DetectCursorPos for W {
|
||||
fn cursor_pos(&mut self) -> io::Result<(u16, u16)> {
|
||||
let mut stdin = async_stdin();
|
||||
|
||||
// Where is the cursor?
|
||||
// Use `ESC [ 6 n`.
|
||||
write!(self, "\x1B[6n")?;
|
||||
self.flush()?;
|
||||
|
||||
let mut buf: [u8; 1] = [0];
|
||||
let mut read_chars = Vec::new();
|
||||
|
||||
let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
|
||||
let now = SystemTime::now();
|
||||
|
||||
// Either consume all data up to R or wait for a timeout.
|
||||
while buf[0] != b'R' && now.elapsed().unwrap() < timeout {
|
||||
if stdin.read(&mut buf)? > 0 {
|
||||
read_chars.push(buf[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if read_chars.len() == 0 {
|
||||
return Err(Error::new(ErrorKind::Other, "Cursor position detection timed out."));
|
||||
}
|
||||
|
||||
// The answer will look like `ESC [ Cy ; Cx R`.
|
||||
|
||||
read_chars.pop(); // remove trailing R.
|
||||
let read_str = String::from_utf8(read_chars).unwrap();
|
||||
let beg = read_str.rfind('[').unwrap();
|
||||
let coords: String = read_str.chars().skip(beg + 1).collect();
|
||||
let mut nums = coords.split(';');
|
||||
|
||||
let cy = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cx = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
|
||||
Ok((cx, cy))
|
||||
}
|
||||
}
|
|
@ -1,351 +0,0 @@
|
|||
//! Mouse and key events.
|
||||
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::ascii::AsciiExt;
|
||||
use std::str;
|
||||
|
||||
/// An event reported by the terminal.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Event {
|
||||
/// A key press.
|
||||
Key(Key),
|
||||
/// A mouse button press, release or wheel use at specific coordinates.
|
||||
Mouse(MouseEvent),
|
||||
/// An event that cannot currently be evaluated.
|
||||
Unsupported(Vec<u8>),
|
||||
}
|
||||
|
||||
/// A mouse related event.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MouseEvent {
|
||||
/// A mouse button was pressed.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Press(MouseButton, u16, u16),
|
||||
/// A mouse button was released.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Release(u16, u16),
|
||||
/// A mouse button is held over the given coordinates.
|
||||
///
|
||||
/// The coordinates are one-based.
|
||||
Hold(u16, u16),
|
||||
}
|
||||
|
||||
/// A mouse button.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MouseButton {
|
||||
/// The left mouse button.
|
||||
Left,
|
||||
/// The right mouse button.
|
||||
Right,
|
||||
/// The middle mouse button.
|
||||
Middle,
|
||||
/// Mouse wheel is going up.
|
||||
///
|
||||
/// This event is typically only used with Mouse::Press.
|
||||
WheelUp,
|
||||
/// Mouse wheel is going down.
|
||||
///
|
||||
/// This event is typically only used with Mouse::Press.
|
||||
WheelDown,
|
||||
}
|
||||
|
||||
/// A key.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Key {
|
||||
/// Backspace.
|
||||
Backspace,
|
||||
/// Left arrow.
|
||||
Left,
|
||||
/// Right arrow.
|
||||
Right,
|
||||
/// Up arrow.
|
||||
Up,
|
||||
/// Down arrow.
|
||||
Down,
|
||||
/// Home key.
|
||||
Home,
|
||||
/// End key.
|
||||
End,
|
||||
/// Page Up key.
|
||||
PageUp,
|
||||
/// Page Down key.
|
||||
PageDown,
|
||||
/// Delete key.
|
||||
Delete,
|
||||
/// Insert key.
|
||||
Insert,
|
||||
/// Function keys.
|
||||
///
|
||||
/// Only function keys 1 through 12 are supported.
|
||||
F(u8),
|
||||
/// Normal character.
|
||||
Char(char),
|
||||
/// Alt modified character.
|
||||
Alt(char),
|
||||
/// Ctrl modified character.
|
||||
///
|
||||
/// Note that certain keys may not be modifiable with `ctrl`, due to limitations of terminals.
|
||||
Ctrl(char),
|
||||
/// Null byte.
|
||||
Null,
|
||||
/// Esc key.
|
||||
Esc,
|
||||
|
||||
#[doc(hidden)]
|
||||
__IsNotComplete,
|
||||
}
|
||||
|
||||
/// Parse an Event from `item` and possibly subsequent bytes through `iter`.
|
||||
pub fn parse_event<I>(item: u8, iter: &mut I) -> Result<Event, Error>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
let error = Error::new(ErrorKind::Other, "Could not parse an event");
|
||||
match item {
|
||||
b'\x1B' => {
|
||||
// This is an escape character, leading a control sequence.
|
||||
Ok(match iter.next() {
|
||||
Some(Ok(b'O')) => {
|
||||
match iter.next() {
|
||||
// F1-F4
|
||||
Some(Ok(val @ b'P'...b'S')) => Event::Key(Key::F(1 + val - b'P')),
|
||||
_ => return Err(error),
|
||||
}
|
||||
}
|
||||
Some(Ok(b'[')) => {
|
||||
// This is a CSI sequence.
|
||||
parse_csi(iter).ok_or(error)?
|
||||
}
|
||||
Some(Ok(c)) => {
|
||||
let ch = parse_utf8_char(c, iter);
|
||||
Event::Key(Key::Alt(try!(ch)))
|
||||
}
|
||||
Some(Err(_)) | None => return Err(error),
|
||||
})
|
||||
}
|
||||
b'\n' | b'\r' => Ok(Event::Key(Key::Char('\n'))),
|
||||
b'\t' => Ok(Event::Key(Key::Char('\t'))),
|
||||
b'\x7F' => Ok(Event::Key(Key::Backspace)),
|
||||
c @ b'\x01'...b'\x1A' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1 + b'a') as char))),
|
||||
c @ b'\x1C'...b'\x1F' => Ok(Event::Key(Key::Ctrl((c as u8 - 0x1C + b'4') as char))),
|
||||
b'\0' => Ok(Event::Key(Key::Null)),
|
||||
c => {
|
||||
Ok({
|
||||
let ch = parse_utf8_char(c, iter);
|
||||
Event::Key(Key::Char(try!(ch)))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a CSI sequence, just after reading ^[
|
||||
///
|
||||
/// Returns None if an unrecognized sequence is found.
|
||||
fn parse_csi<I>(iter: &mut I) -> Option<Event>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
Some(match iter.next() {
|
||||
Some(Ok(b'[')) => match iter.next() {
|
||||
Some(Ok(val @ b'A'...b'E')) => Event::Key(Key::F(1 + val - b'A')),
|
||||
_ => return None,
|
||||
},
|
||||
Some(Ok(b'D')) => Event::Key(Key::Left),
|
||||
Some(Ok(b'C')) => Event::Key(Key::Right),
|
||||
Some(Ok(b'A')) => Event::Key(Key::Up),
|
||||
Some(Ok(b'B')) => Event::Key(Key::Down),
|
||||
Some(Ok(b'H')) => Event::Key(Key::Home),
|
||||
Some(Ok(b'F')) => Event::Key(Key::End),
|
||||
Some(Ok(b'M')) => {
|
||||
// X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only).
|
||||
let mut next = || iter.next().unwrap().unwrap();
|
||||
|
||||
let cb = next() as i8 - 32;
|
||||
// (1, 1) are the coords for upper left.
|
||||
let cx = next().saturating_sub(32) as u16;
|
||||
let cy = next().saturating_sub(32) as u16;
|
||||
Event::Mouse(match cb & 0b11 {
|
||||
0 => {
|
||||
if cb & 0x40 != 0 {
|
||||
MouseEvent::Press(MouseButton::WheelUp, cx, cy)
|
||||
} else {
|
||||
MouseEvent::Press(MouseButton::Left, cx, cy)
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
if cb & 0x40 != 0 {
|
||||
MouseEvent::Press(MouseButton::WheelDown, cx, cy)
|
||||
} else {
|
||||
MouseEvent::Press(MouseButton::Middle, cx, cy)
|
||||
}
|
||||
}
|
||||
2 => MouseEvent::Press(MouseButton::Right, cx, cy),
|
||||
3 => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
Some(Ok(b'<')) => {
|
||||
// xterm mouse encoding:
|
||||
// ESC [ < Cb ; Cx ; Cy (;) (M or m)
|
||||
let mut buf = Vec::new();
|
||||
let mut c = iter.next().unwrap().unwrap();
|
||||
while match c {
|
||||
b'm' | b'M' => false,
|
||||
_ => true,
|
||||
} {
|
||||
buf.push(c);
|
||||
c = iter.next().unwrap().unwrap();
|
||||
}
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
let nums = &mut str_buf.split(';');
|
||||
|
||||
let cb = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cx = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
let cy = nums.next()
|
||||
.unwrap()
|
||||
.parse::<u16>()
|
||||
.unwrap();
|
||||
|
||||
let event = match cb {
|
||||
0...2 | 64...65 => {
|
||||
let button = match cb {
|
||||
0 => MouseButton::Left,
|
||||
1 => MouseButton::Middle,
|
||||
2 => MouseButton::Right,
|
||||
64 => MouseButton::WheelUp,
|
||||
65 => MouseButton::WheelDown,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
match c {
|
||||
b'M' => MouseEvent::Press(button, cx, cy),
|
||||
b'm' => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
32 => MouseEvent::Hold(cx, cy),
|
||||
3 => MouseEvent::Release(cx, cy),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Event::Mouse(event)
|
||||
}
|
||||
Some(Ok(c @ b'0'...b'9')) => {
|
||||
// Numbered escape code.
|
||||
let mut buf = Vec::new();
|
||||
buf.push(c);
|
||||
let mut c = iter.next().unwrap().unwrap();
|
||||
// The final byte of a CSI sequence can be in the range 64-126, so
|
||||
// let's keep reading anything else.
|
||||
while c < 64 || c > 126 {
|
||||
buf.push(c);
|
||||
c = iter.next().unwrap().unwrap();
|
||||
}
|
||||
|
||||
match c {
|
||||
// rxvt mouse encoding:
|
||||
// ESC [ Cb ; Cx ; Cy ; M
|
||||
b'M' => {
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
let nums: Vec<u16> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
|
||||
|
||||
let cb = nums[0];
|
||||
let cx = nums[1];
|
||||
let cy = nums[2];
|
||||
|
||||
let event = match cb {
|
||||
32 => MouseEvent::Press(MouseButton::Left, cx, cy),
|
||||
33 => MouseEvent::Press(MouseButton::Middle, cx, cy),
|
||||
34 => MouseEvent::Press(MouseButton::Right, cx, cy),
|
||||
35 => MouseEvent::Release(cx, cy),
|
||||
64 => MouseEvent::Hold(cx, cy),
|
||||
96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Event::Mouse(event)
|
||||
}
|
||||
// Special key code.
|
||||
b'~' => {
|
||||
let str_buf = String::from_utf8(buf).unwrap();
|
||||
|
||||
// This CSI sequence can be a list of semicolon-separated
|
||||
// numbers.
|
||||
let nums: Vec<u8> = str_buf.split(';').map(|n| n.parse().unwrap()).collect();
|
||||
|
||||
if nums.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// TODO: handle multiple values for key modififiers (ex: values
|
||||
// [3, 2] means Shift+Delete)
|
||||
if nums.len() > 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match nums[0] {
|
||||
1 | 7 => Event::Key(Key::Home),
|
||||
2 => Event::Key(Key::Insert),
|
||||
3 => Event::Key(Key::Delete),
|
||||
4 | 8 => Event::Key(Key::End),
|
||||
5 => Event::Key(Key::PageUp),
|
||||
6 => Event::Key(Key::PageDown),
|
||||
v @ 11...15 => Event::Key(Key::F(v - 10)),
|
||||
v @ 17...21 => Event::Key(Key::F(v - 11)),
|
||||
v @ 23...24 => Event::Key(Key::F(v - 12)),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char.
|
||||
fn parse_utf8_char<I>(c: u8, iter: &mut I) -> Result<char, Error>
|
||||
where I: Iterator<Item = Result<u8, Error>>
|
||||
{
|
||||
let error = Err(Error::new(ErrorKind::Other, "Input character is not valid UTF-8"));
|
||||
if c.is_ascii() {
|
||||
Ok(c as char)
|
||||
} else {
|
||||
let bytes = &mut Vec::new();
|
||||
bytes.push(c);
|
||||
|
||||
loop {
|
||||
match iter.next() {
|
||||
Some(Ok(next)) => {
|
||||
bytes.push(next);
|
||||
if let Ok(st) = str::from_utf8(bytes) {
|
||||
return Ok(st.chars().next().unwrap());
|
||||
}
|
||||
if bytes.len() >= 4 {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
_ => return error,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_parse_utf8() {
|
||||
let st = "abcéŷ¤£€ù%323";
|
||||
let ref mut bytes = st.bytes().map(|x| Ok(x));
|
||||
let chars = st.chars();
|
||||
for c in chars {
|
||||
let b = bytes.next().unwrap().unwrap();
|
||||
assert!(c == parse_utf8_char(b, bytes).unwrap());
|
||||
}
|
||||
}
|
|
@ -1,388 +0,0 @@
|
|||
//! User input.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops;
|
||||
|
||||
use event::{self, Event, Key};
|
||||
use raw::IntoRawMode;
|
||||
|
||||
/// An iterator over input keys.
|
||||
pub struct Keys<R> {
|
||||
iter: Events<R>,
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Keys<R> {
|
||||
type Item = Result<Key, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Key, io::Error>> {
|
||||
loop {
|
||||
match self.iter.next() {
|
||||
Some(Ok(Event::Key(k))) => return Some(Ok(k)),
|
||||
Some(Ok(_)) => continue,
|
||||
e @ Some(Err(_)) => e,
|
||||
None => return None,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over input events.
|
||||
pub struct Events<R> {
|
||||
inner: EventsAndRaw<R>
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for Events<R> {
|
||||
type Item = Result<Event, io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<Event, io::Error>> {
|
||||
self.inner.next().map(|tuple| tuple.map(|(event, _raw)| event))
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over input events and the bytes that define them.
|
||||
pub struct EventsAndRaw<R> {
|
||||
source: R,
|
||||
leftover: Option<u8>,
|
||||
}
|
||||
|
||||
impl<R: Read> Iterator for EventsAndRaw<R> {
|
||||
type Item = Result<(Event, Vec<u8>), io::Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<(Event, Vec<u8>), io::Error>> {
|
||||
let mut source = &mut self.source;
|
||||
|
||||
if let Some(c) = self.leftover {
|
||||
// we have a leftover byte, use it
|
||||
self.leftover = None;
|
||||
return Some(parse_event(c, &mut source.bytes()));
|
||||
}
|
||||
|
||||
// Here we read two bytes at a time. We need to distinguish between single ESC key presses,
|
||||
// and escape sequences (which start with ESC or a x1B byte). The idea is that if this is
|
||||
// an escape sequence, we will read multiple bytes (the first byte being ESC) but if this
|
||||
// is a single ESC keypress, we will only read a single byte.
|
||||
let mut buf = [0u8; 2];
|
||||
let res = match source.read(&mut buf) {
|
||||
Ok(0) => return None,
|
||||
Ok(1) => {
|
||||
match buf[0] {
|
||||
b'\x1B' => Ok((Event::Key(Key::Esc), vec![b'\x1B'])),
|
||||
c => parse_event(c, &mut source.bytes()),
|
||||
}
|
||||
}
|
||||
Ok(2) => {
|
||||
let mut option_iter = &mut Some(buf[1]).into_iter();
|
||||
let result = {
|
||||
let mut iter = option_iter.map(|c| Ok(c)).chain(source.bytes());
|
||||
parse_event(buf[0], &mut iter)
|
||||
};
|
||||
// If the option_iter wasn't consumed, keep the byte for later.
|
||||
self.leftover = option_iter.next();
|
||||
result
|
||||
}
|
||||
Ok(_) => unreachable!(),
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_event<I>(item: u8, iter: &mut I) -> Result<(Event, Vec<u8>), io::Error>
|
||||
where I: Iterator<Item = Result<u8, io::Error>>
|
||||
{
|
||||
let mut buf = vec![item];
|
||||
let result = {
|
||||
let mut iter = iter.inspect(|byte| if let &Ok(byte) = byte {
|
||||
buf.push(byte);
|
||||
});
|
||||
event::parse_event(item, &mut iter)
|
||||
};
|
||||
result.or(Ok(Event::Unsupported(buf.clone()))).map(|e| (e, buf))
|
||||
}
|
||||
|
||||
|
||||
/// Extension to `Read` trait.
|
||||
pub trait TermRead {
|
||||
/// An iterator over input events.
|
||||
fn events(self) -> Events<Self> where Self: Sized;
|
||||
|
||||
/// An iterator over key inputs.
|
||||
fn keys(self) -> Keys<Self> where Self: Sized;
|
||||
|
||||
/// Read a line.
|
||||
///
|
||||
/// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
|
||||
/// complete the input.
|
||||
fn read_line(&mut self) -> io::Result<Option<String>>;
|
||||
|
||||
/// Read a password.
|
||||
///
|
||||
/// EOT and ETX will abort the prompt, returning `None`. Newline or carriage return will
|
||||
/// complete the input.
|
||||
fn read_passwd<W: Write>(&mut self, writer: &mut W) -> io::Result<Option<String>> {
|
||||
let _raw = try!(writer.into_raw_mode());
|
||||
self.read_line()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<R: Read + TermReadEventsAndRaw> TermRead for R {
|
||||
fn events(self) -> Events<Self> {
|
||||
Events {
|
||||
inner: self.events_and_raw()
|
||||
}
|
||||
}
|
||||
fn keys(self) -> Keys<Self> {
|
||||
Keys { iter: self.events() }
|
||||
}
|
||||
|
||||
fn read_line(&mut self) -> io::Result<Option<String>> {
|
||||
let mut buf = Vec::with_capacity(30);
|
||||
|
||||
for c in self.bytes() {
|
||||
match c {
|
||||
Err(e) => return Err(e),
|
||||
Ok(0) | Ok(3) | Ok(4) => return Ok(None),
|
||||
Ok(0x7f) => {
|
||||
buf.pop();
|
||||
}
|
||||
Ok(b'\n') | Ok(b'\r') => break,
|
||||
Ok(c) => buf.push(c),
|
||||
}
|
||||
}
|
||||
|
||||
let string = try!(String::from_utf8(buf)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)));
|
||||
Ok(Some(string))
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension to `TermRead` trait. A separate trait in order to maintain backwards compatibility.
|
||||
pub trait TermReadEventsAndRaw {
|
||||
/// An iterator over input events and the bytes that define them.
|
||||
fn events_and_raw(self) -> EventsAndRaw<Self> where Self: Sized;
|
||||
}
|
||||
|
||||
impl<R: Read> TermReadEventsAndRaw for R {
|
||||
fn events_and_raw(self) -> EventsAndRaw<Self> {
|
||||
EventsAndRaw {
|
||||
source: self,
|
||||
leftover: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A sequence of escape codes to enable terminal mouse support.
|
||||
const ENTER_MOUSE_SEQUENCE: &'static str = csi!("?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h");
|
||||
|
||||
/// A sequence of escape codes to disable terminal mouse support.
|
||||
const EXIT_MOUSE_SEQUENCE: &'static str = csi!("?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l");
|
||||
|
||||
/// A terminal with added mouse support.
|
||||
///
|
||||
/// This can be obtained through the `From` implementations.
|
||||
pub struct MouseTerminal<W: Write> {
|
||||
term: W,
|
||||
}
|
||||
|
||||
impl<W: Write> From<W> for MouseTerminal<W> {
|
||||
fn from(mut from: W) -> MouseTerminal<W> {
|
||||
from.write_all(ENTER_MOUSE_SEQUENCE.as_bytes()).unwrap();
|
||||
|
||||
MouseTerminal { term: from }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for MouseTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
self.term.write_all(EXIT_MOUSE_SEQUENCE.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for MouseTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.term
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for MouseTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.term
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for MouseTerminal<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.term.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.term.flush()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io;
|
||||
use event::{Key, Event, MouseEvent, MouseButton};
|
||||
|
||||
#[test]
|
||||
fn test_keys() {
|
||||
let mut i = b"\x1Bayo\x7F\x1B[D".keys();
|
||||
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Alt('a'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Char('y'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Char('o'));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Backspace);
|
||||
assert_eq!(i.next().unwrap().unwrap(), Key::Left);
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events() {
|
||||
let mut i =
|
||||
b"\x1B[\x00bc\x7F\x1B[D\
|
||||
\x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb"
|
||||
.events();
|
||||
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Unsupported(vec![0x1B, b'[', 0x00]));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('c')));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Backspace));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Left));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_events_and_raw() {
|
||||
let input = b"\x1B[\x00bc\x7F\x1B[D\
|
||||
\x1B[M\x00\x22\x24\x1B[<0;2;4;M\x1B[32;2;4M\x1B[<0;2;4;m\x1B[35;2;4Mb";
|
||||
let mut output = Vec::<u8>::new();
|
||||
{
|
||||
let mut i = input.events_and_raw().map(|res| res.unwrap())
|
||||
.inspect(|&(_, ref raw)| { output.extend(raw); }).map(|(event, _)| event);
|
||||
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Unsupported(vec![0x1B, b'[', 0x00]));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('c')));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Backspace));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Left));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::WheelUp, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Press(MouseButton::Left, 2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap(),
|
||||
Event::Mouse(MouseEvent::Release(2, 4)));
|
||||
assert_eq!(i.next().unwrap(), Event::Key(Key::Char('b')));
|
||||
assert!(i.next().is_none());
|
||||
}
|
||||
|
||||
assert_eq!(input.iter().map(|b| *b).collect::<Vec<u8>>(), output)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_function_keys() {
|
||||
let mut st = b"\x1BOP\x1BOQ\x1BOR\x1BOS".keys();
|
||||
for i in 1..5 {
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||
}
|
||||
|
||||
let mut st = b"\x1B[11~\x1B[12~\x1B[13~\x1B[14~\x1B[15~\
|
||||
\x1B[17~\x1B[18~\x1B[19~\x1B[20~\x1B[21~\x1B[23~\x1B[24~"
|
||||
.keys();
|
||||
for i in 1..13 {
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::F(i));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_special_keys() {
|
||||
let mut st = b"\x1B[2~\x1B[H\x1B[7~\x1B[5~\x1B[3~\x1B[F\x1B[8~\x1B[6~".keys();
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Insert);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Home);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Home);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::PageUp);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Delete);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::End);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::End);
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::PageDown);
|
||||
assert!(st.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_esc_key() {
|
||||
let mut st = b"\x1B".keys();
|
||||
assert_eq!(st.next().unwrap().unwrap(), Key::Esc);
|
||||
assert!(st.next().is_none());
|
||||
}
|
||||
|
||||
fn line_match(a: &str, b: Option<&str>) {
|
||||
let mut sink = io::sink();
|
||||
|
||||
let line = a.as_bytes().read_line().unwrap();
|
||||
let pass = a.as_bytes().read_passwd(&mut sink).unwrap();
|
||||
|
||||
// godammit rustc
|
||||
|
||||
assert_eq!(line, pass);
|
||||
|
||||
if let Some(l) = line {
|
||||
assert_eq!(Some(l.as_str()), b);
|
||||
} else {
|
||||
assert!(b.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read() {
|
||||
let test1 = "this is the first test";
|
||||
let test2 = "this is the second test";
|
||||
|
||||
line_match(test1, Some(test1));
|
||||
line_match(test2, Some(test2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backspace() {
|
||||
line_match("this is the\x7f first\x7f\x7f test",
|
||||
Some("this is th fir test"));
|
||||
line_match("this is the seco\x7fnd test\x7f",
|
||||
Some("this is the secnd tes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_end() {
|
||||
line_match("abc\nhttps://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
Some("abc"));
|
||||
line_match("hello\rhttps://www.youtube.com/watch?v=yPYZpwSpKmA",
|
||||
Some("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_abort() {
|
||||
line_match("abc\x03https://www.youtube.com/watch?v=dQw4w9WgXcQ", None);
|
||||
line_match("hello\x04https://www.youtube.com/watch?v=yPYZpwSpKmA", None);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
//! Termion is a pure Rust, bindless library for low-level handling, manipulating
|
||||
//! and reading information about terminals. This provides a full-featured
|
||||
//! alternative to Termbox.
|
||||
//!
|
||||
//! Termion aims to be simple and yet expressive. It is bindless, meaning that it
|
||||
//! is not a front-end to some other library (e.g., ncurses or termbox), but a
|
||||
//! standalone library directly talking to the TTY.
|
||||
//!
|
||||
//! Supports Redox, Mac OS X, and Linux (or, in general, ANSI terminals).
|
||||
//!
|
||||
//! For more information refer to the [README](https://github.com/ticki/termion).
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
#[path="sys/redox/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[path="sys/unix/mod.rs"]
|
||||
mod sys;
|
||||
|
||||
pub use sys::size::terminal_size;
|
||||
pub use sys::tty::{is_tty, get_tty};
|
||||
|
||||
mod async;
|
||||
pub use async::{AsyncReader, async_stdin};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
pub mod clear;
|
||||
pub mod color;
|
||||
pub mod cursor;
|
||||
pub mod event;
|
||||
pub mod input;
|
||||
pub mod raw;
|
||||
pub mod screen;
|
||||
pub mod scroll;
|
||||
pub mod style;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::sys;
|
||||
|
||||
#[test]
|
||||
fn test_get_terminal_attr() {
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::get_terminal_attr().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_terminal_attr() {
|
||||
let ios = sys::attr::get_terminal_attr().unwrap();
|
||||
sys::attr::set_terminal_attr(&ios).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_size() {
|
||||
sys::size::terminal_size().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
/// 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))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
//! Managing raw mode.
|
||||
//!
|
||||
//! Raw mode is a particular state a TTY can have. It signifies that:
|
||||
//!
|
||||
//! 1. No line buffering (the input is given byte-by-byte).
|
||||
//! 2. The input is not written out, instead it has to be done manually by the programmer.
|
||||
//! 3. The output is not canonicalized (for example, `\n` means "go one line down", not "line
|
||||
//! break").
|
||||
//!
|
||||
//! It is essential to design terminal programs.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! use termion::raw::IntoRawMode;
|
||||
//! use std::io::{Write, stdout};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let mut stdout = stdout().into_raw_mode().unwrap();
|
||||
//!
|
||||
//! write!(stdout, "Hey there.").unwrap();
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops;
|
||||
|
||||
use sys::Termios;
|
||||
use sys::attr::{get_terminal_attr, raw_terminal_attr, set_terminal_attr};
|
||||
|
||||
/// The timeout of an escape code control sequence, in milliseconds.
|
||||
pub const CONTROL_SEQUENCE_TIMEOUT: u64 = 100;
|
||||
|
||||
/// A terminal restorer, which keeps the previous state of the terminal, and restores it, when
|
||||
/// dropped.
|
||||
///
|
||||
/// Restoring will entirely bring back the old TTY state.
|
||||
pub struct RawTerminal<W: Write> {
|
||||
prev_ios: Termios,
|
||||
output: W,
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for RawTerminal<W> {
|
||||
fn drop(&mut self) {
|
||||
set_terminal_attr(&self.prev_ios).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for RawTerminal<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for RawTerminal<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for RawTerminal<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.output.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.output.flush()
|
||||
}
|
||||
}
|
||||
|
||||
/// Types which can be converted into "raw mode".
|
||||
///
|
||||
/// # Why is this type defined on writers and not readers?
|
||||
///
|
||||
/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the
|
||||
/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well.
|
||||
pub trait IntoRawMode: Write + Sized {
|
||||
/// Switch to raw mode.
|
||||
///
|
||||
/// Raw mode means that stdin won't be printed (it will instead have to be written manually by
|
||||
/// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can
|
||||
/// read from stdin one byte of a time). The output is neither modified in any way.
|
||||
fn into_raw_mode(self) -> io::Result<RawTerminal<Self>>;
|
||||
}
|
||||
|
||||
impl<W: Write> IntoRawMode for W {
|
||||
fn into_raw_mode(self) -> io::Result<RawTerminal<W>> {
|
||||
let mut ios = get_terminal_attr()?;
|
||||
let prev_ios = ios;
|
||||
|
||||
raw_terminal_attr(&mut ios);
|
||||
|
||||
set_terminal_attr(&ios)?;
|
||||
|
||||
Ok(RawTerminal {
|
||||
prev_ios: prev_ios,
|
||||
output: self,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::{Write, stdout};
|
||||
|
||||
#[test]
|
||||
fn test_into_raw_mode() {
|
||||
let mut out = stdout().into_raw_mode().unwrap();
|
||||
|
||||
out.write_all(b"this is a test, muahhahahah\r\n").unwrap();
|
||||
|
||||
drop(out);
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
//! Managing switching between main and alternate screen buffers.
|
||||
//!
|
||||
//! Note that this implementation uses xterm's new escape sequences for screen switching and thus
|
||||
//! only works for xterm compatible terminals (which should be most terminals nowadays).
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! use termion::screen::AlternateScreen;
|
||||
//! use std::io::{Write, stdout};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! {
|
||||
//! let mut screen = AlternateScreen::from(stdout());
|
||||
//! write!(screen, "Writing to alternate screen!").unwrap();
|
||||
//! screen.flush().unwrap();
|
||||
//! }
|
||||
//! println!("Writing to main screen.");
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::ops;
|
||||
use std::fmt;
|
||||
|
||||
/// Switch to the main screen buffer of the terminal.
|
||||
pub struct ToMainScreen;
|
||||
|
||||
impl fmt::Display for ToMainScreen {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("?1049l"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch to the alternate screen buffer of the terminal.
|
||||
pub struct ToAlternateScreen;
|
||||
|
||||
impl fmt::Display for ToAlternateScreen {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, csi!("?1049h"))
|
||||
}
|
||||
}
|
||||
|
||||
/// A terminal restorer, which wraps a type implementing Write, and causes all writes to be written
|
||||
/// to an alternate screen.
|
||||
///
|
||||
/// This is achieved by switching the terminal to the alternate screen on creation and
|
||||
/// automatically switching it back to the original screen on drop.
|
||||
pub struct AlternateScreen<W: Write> {
|
||||
/// The output target.
|
||||
output: W,
|
||||
}
|
||||
|
||||
impl<W: Write> AlternateScreen<W> {
|
||||
/// Create an alternate screen wrapper struct for the provided output and switch the terminal
|
||||
/// to the alternate screen.
|
||||
pub fn from(mut output: W) -> Self {
|
||||
write!(output, "{}", ToAlternateScreen).expect("switch to alternate screen");
|
||||
AlternateScreen { output: output }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Drop for AlternateScreen<W> {
|
||||
fn drop(&mut self) {
|
||||
write!(self, "{}", ToMainScreen).expect("switch to main screen");
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::Deref for AlternateScreen<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &W {
|
||||
&self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> ops::DerefMut for AlternateScreen<W> {
|
||||
fn deref_mut(&mut self) -> &mut W {
|
||||
&mut self.output
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> Write for AlternateScreen<W> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.output.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.output.flush()
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
//! 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)
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
//! 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");
|
|
@ -1,33 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, syscall, Termios};
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
let mut termios = Termios::default();
|
||||
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::read(fd, &mut termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(termios)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
let fd = cvt(syscall::dup(0, b"termios"))?;
|
||||
let res = cvt(syscall::write(fd, termios));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == termios.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to set the terminal attributes."))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(ios: &mut Termios) {
|
||||
ios.make_raw()
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
extern crate redox_termios;
|
||||
extern crate syscall;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::redox_termios::Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support function for converting syscall error to io error
|
||||
fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> {
|
||||
result.map_err(|err| io::Error::from_raw_os_error(err.errno))
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
use super::{cvt, redox_termios, syscall};
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
let mut winsize = redox_termios::Winsize::default();
|
||||
|
||||
let fd = cvt(syscall::dup(1, b"winsize"))?;
|
||||
let res = cvt(syscall::read(fd, &mut winsize));
|
||||
let _ = syscall::close(fd);
|
||||
|
||||
if res? == winsize.len() {
|
||||
Ok((winsize.ws_col, winsize.ws_row))
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "Unable to get the terminal size."))
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
use std::{env, fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::syscall;
|
||||
|
||||
/// Is this stream a TTY?
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
if let Ok(fd) = syscall::dup(stream.as_raw_fd(), b"termios") {
|
||||
let _ = syscall::close(fd);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
let tty = try!(env::var("TTY").map_err(|x| io::Error::new(io::ErrorKind::NotFound, x)));
|
||||
fs::OpenOptions::new().read(true).write(true).open(tty)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::{cvt, Termios};
|
||||
use super::libc::c_int;
|
||||
|
||||
pub fn get_terminal_attr() -> io::Result<Termios> {
|
||||
extern "C" {
|
||||
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
|
||||
}
|
||||
unsafe {
|
||||
let mut termios = mem::zeroed();
|
||||
cvt(tcgetattr(0, &mut termios))?;
|
||||
Ok(termios)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_terminal_attr(termios: &Termios) -> io::Result<()> {
|
||||
extern "C" {
|
||||
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
|
||||
}
|
||||
cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
|
||||
}
|
||||
|
||||
pub fn raw_terminal_attr(termios: &mut Termios) {
|
||||
extern "C" {
|
||||
pub fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
unsafe { cfmakeraw(termios) }
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
|
||||
pub use self::libc::termios as Termios;
|
||||
|
||||
pub mod attr;
|
||||
pub mod size;
|
||||
pub mod tty;
|
||||
|
||||
// Support functions for converting libc return values to io errors {
|
||||
trait IsMinusOne {
|
||||
fn is_minus_one(&self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! impl_is_minus_one {
|
||||
($($t:ident)*) => ($(impl IsMinusOne for $t {
|
||||
fn is_minus_one(&self) -> bool {
|
||||
*self == -1
|
||||
}
|
||||
})*)
|
||||
}
|
||||
|
||||
impl_is_minus_one! { i8 i16 i32 i64 isize }
|
||||
|
||||
fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
|
||||
if t.is_minus_one() {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
// } End of support functions
|
|
@ -1,48 +0,0 @@
|
|||
use std::{io, mem};
|
||||
|
||||
use super::cvt;
|
||||
use super::libc::{c_ushort, ioctl, STDOUT_FILENO};
|
||||
|
||||
#[repr(C)]
|
||||
struct TermSize {
|
||||
row: c_ushort,
|
||||
col: c_ushort,
|
||||
_x: c_ushort,
|
||||
_y: c_ushort,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub const TIOCGWINSZ: usize = 0x00005413;
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub const TIOCGWINSZ: usize = 0x40087468;
|
||||
|
||||
// Since attributes on non-item statements is not stable yet, we use a function.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u64 {
|
||||
TIOCGWINSZ as u64
|
||||
}
|
||||
#[cfg(not(target_os = "android"))]
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
fn tiocgwinsz() -> u32 {
|
||||
TIOCGWINSZ as u32
|
||||
}
|
||||
|
||||
#[cfg(any(target_env = "musl", target_os = "android"))]
|
||||
fn tiocgwinsz() -> i32 {
|
||||
TIOCGWINSZ as i32
|
||||
}
|
||||
|
||||
/// Get the size of the terminal.
|
||||
pub fn terminal_size() -> io::Result<(u16, u16)> {
|
||||
unsafe {
|
||||
let mut size: TermSize = mem::zeroed();
|
||||
cvt(ioctl(STDOUT_FILENO, tiocgwinsz(), &mut size as *mut _))?;
|
||||
Ok((size.col as u16, size.row as u16))
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
use std::{fs, io};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::libc;
|
||||
|
||||
|
||||
/// Is this stream a TTY?
|
||||
pub fn is_tty<T: AsRawFd>(stream: &T) -> bool {
|
||||
unsafe { libc::isatty(stream.as_raw_fd()) == 1 }
|
||||
}
|
||||
|
||||
/// Get the TTY device.
|
||||
///
|
||||
/// This allows for getting stdio representing _only_ the TTY, and not other streams.
|
||||
pub fn get_tty() -> io::Result<fs::File> {
|
||||
fs::OpenOptions::new().read(true).write(true).open("/dev/tty")
|
||||
}
|
Загрузка…
Ссылка в новой задаче