Introduced: Crossterm Workspace and feature flags. (#84)
* Introduced: crossterm workspace, feature flags.
This commit is contained in:
parent
2bfa7ffd5b
commit
ad74f6b524
39
Cargo.toml
39
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "crossterm"
|
||||
version = "0.5.5"
|
||||
version = "0.5.4"
|
||||
authors = ["T. Post"]
|
||||
description = "An crossplatform terminal library for manipulating terminals."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
@ -10,22 +10,39 @@ keywords = ["console", "color", "cursor", "input", "terminal"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.5", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi","errhandlingapi"] }
|
||||
crossterm_winapi = "0.1.0"
|
||||
[features]
|
||||
default = ["cursor", "style","terminal","screen","input"]
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.43"
|
||||
termios = "0.3.0"
|
||||
cursor = ["crossterm_cursor"]
|
||||
style = ["crossterm_style"]
|
||||
terminal = ["crossterm_terminal"]
|
||||
screen = ["crossterm_screen"]
|
||||
input = ["crossterm_input"]
|
||||
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"crossterm_winapi",
|
||||
"crossterm_utils",
|
||||
"crossterm_cursor",
|
||||
"crossterm_style",
|
||||
"crossterm_terminal",
|
||||
"crossterm_input",
|
||||
"crossterm_screen",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
crossterm_screen = { path = "./crossterm_screen", optional = true, version = "0.1.0" }
|
||||
crossterm_cursor = { path = "./crossterm_cursor", optional = true, version = "0.1.0" }
|
||||
crossterm_terminal = { path = "./crossterm_terminal", optional = true, version = "0.1.0" }
|
||||
crossterm_style = { path = "./crossterm_style", optional = true, version = "0.1.0" }
|
||||
crossterm_input = { path = "./crossterm_input", optional = true, version = "0.1.0" }
|
||||
crossterm_utils = { path = "./crossterm_utils", version = "0.1.0" }
|
||||
|
||||
[lib]
|
||||
name = "crossterm"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "examples"
|
||||
path = "examples/examples.rs"
|
||||
|
||||
[[example]]
|
||||
name = "logging"
|
||||
path = "examples/program_examples/logging.rs"
|
||||
|
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Timon
|
||||
|
||||
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.
|
67
README.md
67
README.md
@ -5,7 +5,10 @@
|
||||
[l1]: https://crates.io/crates/crossterm
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
[l2]: crossterm/LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm/badge.svg
|
||||
[l3]: https://docs.rs/crossterm/
|
||||
|
||||
[s3]: https://docs.rs/crossterm/badge.svg
|
||||
[l3]: https://docs.rs/crossterm/
|
||||
@ -21,6 +24,13 @@ Through the simplicity of Crossterm, you do not have to worry about the platform
|
||||
|
||||
This crate supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is exists out of six modules who are behind feature flags so that you can define which features you'd like to have:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
@ -40,30 +50,16 @@ This crate supports all UNIX and windows terminals down to windows 7 (not all te
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for Crossterm version `0.5.^` if you have an older version of Crossterm I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
This documentation is only for Crossterm version `0.5` if you have an older version of Crossterm I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
|
||||
Add the Crossterm package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
crossterm = "0.5.4"
|
||||
|
||||
crossterm = "0.6"
|
||||
```
|
||||
And import the Crossterm modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm;
|
||||
|
||||
// this module is used for styling the terminal
|
||||
use crossterm::style::*;
|
||||
// this module is used for cursor related actions
|
||||
use crossterm::cursor::*;
|
||||
// this module is used for terminal related actions
|
||||
use crossterm::terminal::*;
|
||||
// this module is used for input related actions
|
||||
use crossterm::input::*;
|
||||
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
@ -79,7 +75,7 @@ These are the features from this crate:
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependencies.
|
||||
- Very few dependenties.
|
||||
- Cursor.
|
||||
- Moving _n_ times Up, Down, Left, Right
|
||||
- Goto a certain position
|
||||
@ -130,13 +126,13 @@ println!("{}", crossterm.style("Black font on Green background color").with(Colo
|
||||
### Styled Font | [see more](http://atcentra.com/crossterm/styling.html)
|
||||
This module provides the functionalities to style the terminal.
|
||||
```rust
|
||||
use crossterm::style::{Color, style};
|
||||
use crossterm::{Color, style};
|
||||
|
||||
// store objcets so it could be painted later to the screen.
|
||||
let style1 = style("Some Blue font on Black background").with(Color::Blue).on(Color::Black);
|
||||
let style2 = style("Some Red font on Yellow background").with(Color::Red).on(Color::Yellow);
|
||||
|
||||
// styling font with (Windows 10 and UNIX systems)
|
||||
// syling font with (Windows 10 and UNIX systems)
|
||||
let normal = style("Normal text");
|
||||
let bold = style("Bold text").bold();
|
||||
let italic = style("Italic text").italic();
|
||||
@ -155,7 +151,7 @@ println!("{}", bold);
|
||||
println!("{}", hidden);
|
||||
...
|
||||
|
||||
// custom rgb value (Windows 10 and UNIX systems)
|
||||
// cursom rgb value (Windows 10 and UNIX systems)
|
||||
style("RGB color (10,10,10) ").with(Color::Rgb {
|
||||
r: 10,
|
||||
g: 10,
|
||||
@ -207,31 +203,12 @@ cursor.blink(true)
|
||||
|
||||
```
|
||||
|
||||
### Input | [see more](http://atcentra.com/crossterm/input.html)
|
||||
This module provides the functionalities to work with terminal input.
|
||||
|
||||
```rust
|
||||
use crossterm::input;
|
||||
|
||||
let mut input = input();
|
||||
|
||||
match input.read_char() {
|
||||
Ok(s) => println!("char typed: {}", s),
|
||||
Err(e) => println!("char error : {}", e),
|
||||
}
|
||||
|
||||
match input.read_line() {
|
||||
Ok(s) => println!("string typed: {}", s),
|
||||
Err(e) => println!("error: {}", e),
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Terminal | [see more](https://github.com/TimonPost/crossterm/blob/master/examples/terminal/terminal.rs)
|
||||
This module provides the functionalities to work with the terminal in general.
|
||||
|
||||
```rust
|
||||
use crossterm::terminal::{terminal,ClearType};
|
||||
use crossterm::{terminal,ClearType};
|
||||
|
||||
let mut terminal = terminal();
|
||||
|
||||
@ -283,17 +260,17 @@ This crate supports all Unix terminals and windows terminals down to Windows 7 b
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
This library is quite stable now, changes could be expected but they will probably be not that big.
|
||||
This library is average stable now but I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Todo
|
||||
I still have some things in mind to implement.
|
||||
|
||||
- Handling mouse events:
|
||||
- Handling mouse events
|
||||
I want to be able to do something based on the clicks the user has done with its mouse.
|
||||
- Handling key events:
|
||||
- Handling key events
|
||||
I want to be able to read key combination inputs.
|
||||
- Tests:
|
||||
- Tests
|
||||
Find a way to test: color, alternate screen, rawscreen
|
||||
|
||||
## Contributing
|
||||
|
@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="RUST_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
2
crossterm_cursor/.gitignore
vendored
Normal file
2
crossterm_cursor/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
23
crossterm_cursor/Cargo.toml
Normal file
23
crossterm_cursor/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "crossterm_cursor"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "A cross-platform library for moving the terminal cursor."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
documentation = "https://docs.rs/crossterm_cursor/"
|
||||
license = "MIT"
|
||||
keywords = ["cursor", "cli", "crossterm", "crossplatform", "terminal"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.5", features = ["wincon","winnt","minwindef"] }
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
||||
|
||||
[dependencies]
|
||||
crossterm_utils = { path = "../crossterm_utils" }
|
||||
|
||||
[[example]]
|
||||
name = "cursor"
|
||||
path = "examples/cursor.rs"
|
155
crossterm_cursor/README.md
Normal file
155
crossterm_cursor/README.md
Normal file
@ -0,0 +1,155 @@
|
||||
# Crossterm Cursor | cross-platform cursor movement.
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_cursor.svg
|
||||
[l1]: https://crates.io/crates/crossterm_cursor
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_cursor/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_cursor/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_cursor/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_cursor/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_cursor?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_cursor.svg?branch=master
|
||||
|
||||
This crate allows you to move the terminal cursor cross-platform.
|
||||
It supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is a sub-crate of [crossterm](https://crates.io/crates/crossterm) to move the cursor, and can be use individually.
|
||||
|
||||
Other sub-crates are:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
|
||||
When you want to use other modules as well you might want to use crossterm with [feature flags](https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html)
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
- [Features](#features)
|
||||
- [Examples](#examples)
|
||||
- [Tested Terminals](#tested-terminals)
|
||||
- [Notice](#notice)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for `crossterm_cursor` version `0.1` if you have an older version I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
|
||||
Add the `crossterm_cursor` package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
`crossterm_cursor` = "0.1"
|
||||
|
||||
```
|
||||
And import the crossterm_input modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm_cursor;
|
||||
|
||||
pub use crossterm_cursor::{cursor, TerminalCursor};
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Documentation](https://docs.rs/crossterm_cursor/)
|
||||
- [Crates.io](https://crates.io/crates/crossterm_cursor)
|
||||
- [Examples](/examples)
|
||||
|
||||
## Features
|
||||
These are the features of this crate:
|
||||
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependenties.
|
||||
- Cursor.
|
||||
- Moving _n_ times Up, Down, Left, Right
|
||||
- Goto a certain position
|
||||
- Get cursor position
|
||||
- Storing the current cursor position and resetting to that stored cursor position later
|
||||
- Hiding an showing the cursor
|
||||
- Control over blinking of the terminal cursor (only some terminals are supporting this)
|
||||
|
||||
## Examples
|
||||
Check out the [examples](/examples/) for more information about how to use this crate.
|
||||
|
||||
```rust
|
||||
use crossterm_cursor::cursor;
|
||||
|
||||
let mut cursor = cursor();
|
||||
|
||||
/// Moving the cursor
|
||||
// Set the cursor to position X: 10, Y: 5 in the terminal
|
||||
cursor.goto(10,5);
|
||||
|
||||
// Move the cursor up,right,down,left 3 cells.
|
||||
cursor.move_up(3);
|
||||
cursor.move_right(3);
|
||||
cursor.move_down(3);
|
||||
cursor.move_left(3);
|
||||
|
||||
/// Safe the current cursor position to recall later
|
||||
// Goto X: 5 Y: 5
|
||||
cursor.goto(5,5);
|
||||
// Safe cursor position: X: 5 Y: 5
|
||||
cursor.save_position();
|
||||
// Goto X: 5 Y: 20
|
||||
cursor.goto(5,20);
|
||||
// Print at X: 5 Y: 20.
|
||||
print!("Yea!");
|
||||
// Reset back to X: 5 Y: 5.
|
||||
cursor.reset_position();
|
||||
// Print 'Back' at X: 5 Y: 5.
|
||||
print!("Back");
|
||||
|
||||
// hide cursor
|
||||
cursor.hide();
|
||||
// show cursor
|
||||
cursor.show();
|
||||
// blink or not blinking of the cursor (not widely supported)
|
||||
cursor.blink(true)
|
||||
|
||||
```
|
||||
## Tested terminals
|
||||
|
||||
- Windows Powershell
|
||||
- Windows 10 (pro)
|
||||
- Windows CMD
|
||||
- Windows 10 (pro)
|
||||
- Windows 8.1 (N)
|
||||
- Ubuntu Desktop Terminal
|
||||
- Ubuntu 17.10
|
||||
- (Arch, Manjaro) KDE Konsole
|
||||
- Linux Mint
|
||||
|
||||
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
|
||||
This library is average stable now, I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
@ -2,9 +2,9 @@
|
||||
//! Examples of actions that could be performed with te cursor.
|
||||
//!
|
||||
|
||||
extern crate crossterm;
|
||||
use self::crossterm::cursor::{cursor, TerminalCursor};
|
||||
use self::crossterm::Screen;
|
||||
extern crate crossterm_cursor;
|
||||
|
||||
use crossterm_cursor::{cursor, TerminalCursor};
|
||||
|
||||
/// Set the cursor to position X: 10, Y: 5 in the terminal.
|
||||
pub fn goto() {
|
||||
@ -47,40 +47,6 @@ pub fn move_down() {
|
||||
cursor.move_down(3);
|
||||
}
|
||||
|
||||
/// Move the cursor 3 to the left | demonstration.
|
||||
pub fn move_left() {
|
||||
let mut cursor = cursor();
|
||||
|
||||
// Move the cursor to position 3 times to the left in the terminal
|
||||
cursor.move_left(3);
|
||||
}
|
||||
|
||||
///// Print character at X: 10 Y: 5 | demonstration.
|
||||
//pub fn print() {
|
||||
// let context = Context::new();
|
||||
//
|
||||
// // To print an some displayable content on an certain position.
|
||||
//
|
||||
// // Get the cursor
|
||||
// let mut cursor = cursor(&context);
|
||||
// // Set the cursor to position X: 10, Y: 5 in the terminal
|
||||
// cursor.goto(10, 5);
|
||||
// // Print the @ symbol at position X: 10, Y: 5 in the terminal
|
||||
// print!("@");
|
||||
// // Rust is line buffered inorder to print at an certain position we need to clear the buffer first.
|
||||
// use std;
|
||||
// use std::io::Write;
|
||||
// std::io::stdout().flush();
|
||||
//
|
||||
// /* Because the above method is a little to much code,
|
||||
// you can use the `print()` method for printing an value at an certain position in the terminal.
|
||||
//
|
||||
// Crossterm provides method chaining so that the above points can be inlined.
|
||||
// */
|
||||
//
|
||||
// cursor.goto(10, 5).print("@");
|
||||
//}
|
||||
|
||||
/// Save and reset cursor position | demonstration..
|
||||
pub fn safe_and_reset_position() {
|
||||
let cursor = cursor();
|
||||
@ -119,3 +85,8 @@ pub fn blink_cursor() {
|
||||
cursor.blink(false);
|
||||
cursor.blink(false);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
goto();
|
||||
pos();
|
||||
}
|
@ -2,8 +2,11 @@
|
||||
//! This module is used for windows 10 terminals and UNIX terminals by default.
|
||||
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position etc.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use super::ITerminalCursor;
|
||||
use crate::sys::get_cursor_position;
|
||||
|
||||
use crossterm_utils::{write, write_str, Result, TerminalOutput};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This struct is an ANSI implementation for cursor related actions.
|
||||
pub struct AnsiCursor {}
|
||||
@ -16,59 +19,59 @@ impl AnsiCursor {
|
||||
|
||||
impl ITerminalCursor for AnsiCursor {
|
||||
fn goto(&self, x: u16, y: u16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(stdout, format!(csi!("{};{}H"), y + 1, x + 1))?;
|
||||
write(stdout, format!(csi!("{};{}H"), y + 1, x + 1))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pos(&self) -> (u16, u16) {
|
||||
functions::get_cursor_position()
|
||||
get_cursor_position()
|
||||
}
|
||||
|
||||
fn move_up(&self, count: u16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(stdout, format!(csi!("{}A"), count))?;
|
||||
write(stdout, format!(csi!("{}A"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_right(&self, count: u16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(stdout, format!(csi!("{}C"), count))?;
|
||||
write(stdout, format!(csi!("{}C"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_down(&self, count: u16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(stdout, format!(csi!("{}B"), count))?;
|
||||
write(stdout, format!(csi!("{}B"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn move_left(&self, count: u16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(stdout, format!(csi!("{}D"), count))?;
|
||||
write(stdout, format!(csi!("{}D"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_position(&self, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write_str(stdout, csi!("s"))?;
|
||||
write_str(stdout, csi!("s"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reset_position(&self, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write_str(stdout, csi!("u"))?;
|
||||
write_str(stdout, csi!("u"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hide(&self, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write_str(stdout, csi!("?25l"))?;
|
||||
write_str(stdout, csi!("?25l"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show(&self, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write_str(stdout, csi!("?25h"))?;
|
||||
write_str(stdout, csi!("?25h"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn blink(&self, blink: bool, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
if blink {
|
||||
functions::write_str(stdout, csi!("?12h"))?;
|
||||
write_str(stdout, csi!("?12h"))?;
|
||||
} else {
|
||||
functions::write_str(stdout, csi!("?12l"))?;
|
||||
write_str(stdout, csi!("?12l"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -1,34 +1,32 @@
|
||||
//! A module that contains all the actions related to cursor movement in the terminal.
|
||||
//! Like: moving the cursor position; saving and resetting the cursor position; hiding showing and control the blinking of the cursor.
|
||||
//!
|
||||
//! Note that positions of the cursor are 0 -based witch means that the coordinates (cells) starts counting from 0
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use Screen;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Struct that stores a platform-specific implementation for cursor related actions.
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
|
||||
#[cfg(windows)]
|
||||
use crossterm_utils::get_module;
|
||||
|
||||
/// Allows you to preform actions with the terminal cursor.
|
||||
///
|
||||
/// # Features:
|
||||
///
|
||||
/// - Moving n times Up, Down, Left, Right
|
||||
/// - Goto a certain position
|
||||
/// - Get cursor position
|
||||
/// - Storing the current cursor position and resetting to that stored cursor position later
|
||||
/// - Hiding an showing the cursor
|
||||
/// - Control over blinking of the terminal cursor (only some terminals are supporting this)
|
||||
///
|
||||
/// Note that positions of the cursor are 0 -based witch means that the coordinates (cells) starts counting from 0
|
||||
///
|
||||
/// Check `/examples/cursor` in the library for more specific examples.
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate crossterm;
|
||||
/// use self::crossterm::cursor;
|
||||
/// use self::crossterm::Screen;
|
||||
/// # Remarks
|
||||
///
|
||||
/// let mut cursor = cursor();
|
||||
///
|
||||
/// // Get cursor and goto pos X: 5, Y: 10
|
||||
/// cursor.goto(5,10);
|
||||
///
|
||||
/// cursor.show();
|
||||
/// cursor.hide();
|
||||
/// cursor.blink(true);
|
||||
/// cursor.move_left(2);
|
||||
/// ```
|
||||
///
|
||||
/// When you want to use 'cursor' on 'alternate screen' use the `Screen` type instead and pass it to the `cursor::from_screen()` function.
|
||||
/// By doing that cursor actions will be performed on the alternate screen.
|
||||
/// When you want to use 'cursor' on 'alternate screen' use the 'crossterm_screen' crate.
|
||||
pub struct TerminalCursor<'stdout> {
|
||||
terminal_cursor: Box<ITerminalCursor + Sync + Send>,
|
||||
stdout: Option<&'stdout Arc<TerminalOutput>>,
|
||||
@ -38,7 +36,7 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
/// Create new `TerminalCursor` instance whereon cursor related actions can be performed.
|
||||
pub fn new() -> TerminalCursor<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let cursor = functions::get_module::<Box<ITerminalCursor + Sync + Send>>(
|
||||
let cursor = get_module::<Box<ITerminalCursor + Sync + Send>>(
|
||||
WinApiCursor::new(),
|
||||
AnsiCursor::new(),
|
||||
)
|
||||
@ -55,11 +53,13 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
|
||||
/// Create a new instance of `TerminalCursor` whereon cursor related actions could be preformed on the given output.
|
||||
///
|
||||
/// **Note**
|
||||
/// # Remarks
|
||||
///
|
||||
/// Use this function when you want your terminal to operate with a specific output.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode'.
|
||||
/// And you want your actions from the `TerminalCursor`, created by this function, to operate on the 'alternate screen'.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode',
|
||||
/// and you want your actions from the `TerminalCursor`, created by this function, to operate on the 'alternate screen'.
|
||||
///
|
||||
/// You should checkout the 'crossterm_screen' crate for more information about this.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
@ -71,7 +71,7 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
/// ```
|
||||
pub fn from_output(stdout: &'stdout Arc<TerminalOutput>) -> TerminalCursor<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let cursor = functions::get_module::<Box<ITerminalCursor + Sync + Send>>(
|
||||
let cursor = get_module::<Box<ITerminalCursor + Sync + Send>>(
|
||||
WinApiCursor::new(),
|
||||
AnsiCursor::new(),
|
||||
)
|
||||
@ -88,50 +88,27 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
|
||||
/// Goto some position (x,y) in the terminal.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // change the cursor to position, x: 4 and y: 5
|
||||
/// cursor.goto(4,5);
|
||||
///
|
||||
/// ```
|
||||
/// # Remarks
|
||||
/// position is 0-based, which means we start counting at 0.
|
||||
pub fn goto(&self, x: u16, y: u16) -> Result<()> {
|
||||
self.terminal_cursor.goto(x, y, &self.stdout)
|
||||
}
|
||||
|
||||
/// Get current cursor position (x,y) in the terminal.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // get the current cursor pos
|
||||
/// let (x,y) = cursor.pos();
|
||||
/// ```
|
||||
/// # Remarks
|
||||
/// position is 0-based, which means we start counting at 0.
|
||||
pub fn pos(&self) -> (u16, u16) {
|
||||
self.terminal_cursor.pos()
|
||||
}
|
||||
|
||||
/// Move the current cursor position `n` times up.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // Move the cursor to position 3 times to the up in the terminal
|
||||
/// cursor.move_up(3);
|
||||
/// ```
|
||||
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor<'stdout> {
|
||||
self.terminal_cursor.move_up(count, &self.stdout).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Move the current cursor position `n` times right.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // Move the cursor to position 3 times to the right in the terminal
|
||||
/// cursor.move_right(3);
|
||||
/// ```
|
||||
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor<'stdout> {
|
||||
self.terminal_cursor
|
||||
.move_right(count, &self.stdout)
|
||||
@ -140,26 +117,12 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
}
|
||||
|
||||
/// Move the current cursor position `n` times down.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // Move the cursor to position 3 times to the down in the terminal
|
||||
/// cursor.move_down(3);
|
||||
/// ```
|
||||
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor<'stdout> {
|
||||
self.terminal_cursor.move_down(count, &self.stdout).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
/// Move the current cursor position `n` times left.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// // Move the cursor to position 3 times to the left in the terminal
|
||||
/// cursor.move_left(3);
|
||||
/// ```
|
||||
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor<'stdout> {
|
||||
self.terminal_cursor.move_left(count, &self.stdout).unwrap();
|
||||
self
|
||||
@ -168,60 +131,29 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
/// Save cursor position for recall later.
|
||||
///
|
||||
/// Note that this position is stored program based not per instance of the `Cursor` struct.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// cursor.safe_position();
|
||||
/// ```
|
||||
pub fn save_position(&self) -> Result<()> {
|
||||
self.terminal_cursor.save_position(&self.stdout)
|
||||
}
|
||||
|
||||
/// Return to saved cursor position
|
||||
///
|
||||
/// Note that this method reset to the position set by `save_position()` and that this position is stored program based not per instance of the `Cursor` struct.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
///
|
||||
/// cursor.reset_position();
|
||||
/// ```
|
||||
pub fn reset_position(&self) -> Result<()> {
|
||||
self.terminal_cursor.reset_position(&self.stdout)
|
||||
}
|
||||
|
||||
/// Hide de cursor in the console.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
/// cursor.hide();
|
||||
/// ```
|
||||
pub fn hide(&self) -> Result<()> {
|
||||
self.terminal_cursor.hide(&self.stdout)
|
||||
}
|
||||
|
||||
/// Show the cursor in the console.
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// let cursor = cursor();
|
||||
/// cursor.show();
|
||||
///
|
||||
/// ```
|
||||
pub fn show(&self) -> Result<()> {
|
||||
self.terminal_cursor.show(&self.stdout)
|
||||
}
|
||||
|
||||
/// Enable or disable blinking of the terminal.
|
||||
///
|
||||
/// # Remarks
|
||||
/// Not all terminals are supporting this functionality. Windows versions lower than windows 10 also are not supporting this version.
|
||||
///
|
||||
/// ```rust
|
||||
/// let cursor = cursor();
|
||||
/// cursor.blink(true);
|
||||
/// cursor.blink(false);
|
||||
/// ```
|
||||
pub fn blink(&self, blink: bool) -> Result<()> {
|
||||
self.terminal_cursor.blink(blink, &self.stdout)
|
||||
}
|
||||
@ -231,9 +163,3 @@ impl<'stdout> TerminalCursor<'stdout> {
|
||||
pub fn cursor() -> TerminalCursor<'static> {
|
||||
TerminalCursor::new()
|
||||
}
|
||||
|
||||
/// Get a `TerminalCursor` instance whereon cursor related actions can be performed.
|
||||
/// Pass the reference to any `Screen` you want this type to perform actions on.
|
||||
pub fn from_screen(screen: &Screen) -> TerminalCursor {
|
||||
TerminalCursor::from_output(&screen.stdout)
|
||||
}
|
@ -16,11 +16,9 @@ use self::ansi_cursor::AnsiCursor;
|
||||
#[cfg(target_os = "windows")]
|
||||
use self::winapi_cursor::WinApiCursor;
|
||||
|
||||
pub use self::cursor::{cursor, from_screen, TerminalCursor};
|
||||
use super::functions;
|
||||
use common::error::Result;
|
||||
pub use self::cursor::{cursor, TerminalCursor};
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
use std::sync::Arc;
|
||||
use TerminalOutput;
|
||||
|
||||
///! This trait defines the actions that can be performed with the terminal cursor.
|
||||
///! This trait can be implemented so that a concrete implementation of the ITerminalCursor can fulfill
|
@ -1,22 +1,17 @@
|
||||
use modules::cursor::ansi_cursor::AnsiCursor;
|
||||
|
||||
use modules::cursor::ITerminalCursor;
|
||||
|
||||
use Screen;
|
||||
use super::AnsiCursor;
|
||||
use super::ITerminalCursor;
|
||||
|
||||
/* ======================== WinApi =========================== */
|
||||
#[cfg(windows)]
|
||||
mod winapi_tests {
|
||||
use super::*;
|
||||
use modules::cursor::winapi_cursor::WinApiCursor;
|
||||
|
||||
use super::super::WinApiCursor;
|
||||
use super::*;
|
||||
#[test]
|
||||
fn goto_winapi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let cursor = WinApiCursor::new();
|
||||
|
||||
cursor.goto(5, 5, &stdout);
|
||||
cursor.goto(5, 5, &None);
|
||||
let (x, y) = cursor.pos();
|
||||
|
||||
assert_eq!(x, 5);
|
||||
@ -25,14 +20,12 @@ mod winapi_tests {
|
||||
|
||||
#[test]
|
||||
fn reset_safe_winapi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let cursor = WinApiCursor::new();
|
||||
let (x, y) = cursor.pos();
|
||||
|
||||
cursor.save_position(&stdout);
|
||||
cursor.goto(5, 5, &stdout);
|
||||
cursor.reset_position(&stdout);
|
||||
cursor.save_position(&None);
|
||||
cursor.goto(5, 5, &None);
|
||||
cursor.reset_position(&None);
|
||||
|
||||
let (x_saved, y_saved) = cursor.pos();
|
||||
|
||||
@ -45,14 +38,12 @@ mod winapi_tests {
|
||||
#[test]
|
||||
fn reset_safe_ansi() {
|
||||
if try_enable_ansi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let cursor = AnsiCursor::new();
|
||||
let (x, y) = cursor.pos();
|
||||
|
||||
cursor.save_position(&stdout);
|
||||
cursor.goto(5, 5, &stdout);
|
||||
cursor.reset_position(&stdout);
|
||||
cursor.save_position(&None);
|
||||
cursor.goto(5, 5, &None);
|
||||
cursor.reset_position(&None);
|
||||
|
||||
let (x_saved, y_saved) = cursor.pos();
|
||||
|
||||
@ -64,11 +55,8 @@ fn reset_safe_ansi() {
|
||||
#[test]
|
||||
fn goto_ansi() {
|
||||
if try_enable_ansi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let cursor = AnsiCursor::new();
|
||||
|
||||
cursor.goto(5, 5, &stdout);
|
||||
cursor.goto(5, 5, &None);
|
||||
let (x, y) = cursor.pos();
|
||||
|
||||
assert_eq!(x, 5);
|
||||
@ -80,10 +68,12 @@ fn try_enable_ansi() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if cfg!(target_os = "windows") {
|
||||
use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
|
||||
use crossterm_utils::sys::winapi::ansi::set_virtual_terminal_processing;
|
||||
|
||||
if !try_enable_ansi_support() {
|
||||
return false;
|
||||
// if it is not listed we should try with WinApi to check if we do support ANSI-codes.
|
||||
match set_virtual_terminal_processing(true) {
|
||||
Ok(_) => return true,
|
||||
Err(e) => return false,
|
||||
}
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@
|
||||
//! This module is used for Windows terminals that do not support ANSI escape codes.
|
||||
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use kernel::windows_kernel::{Cursor, Handle};
|
||||
use super::ITerminalCursor;
|
||||
use crate::sys::winapi::{Cursor, Handle};
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This struct is a windows implementation for cursor related actions.
|
||||
pub struct WinApiCursor;
|
10
crossterm_cursor/src/lib.rs
Normal file
10
crossterm_cursor/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#[macro_use]
|
||||
extern crate crossterm_utils;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
mod cursor;
|
||||
pub mod sys;
|
||||
|
||||
pub use self::cursor::{cursor, TerminalCursor};
|
10
crossterm_cursor/src/sys/mod.rs
Normal file
10
crossterm_cursor/src/sys/mod.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::get_cursor_position;
|
||||
#[cfg(windows)]
|
||||
pub use self::winapi::get_cursor_position;
|
88
crossterm_cursor/src/sys/unix.rs
Normal file
88
crossterm_cursor/src/sys/unix.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use crossterm_utils::sys::unix;
|
||||
use std::io::{self, Error, ErrorKind, Read, Write};
|
||||
|
||||
/// Get the cursor position based on the current platform.
|
||||
#[cfg(unix)]
|
||||
pub fn get_cursor_position() -> (u16, u16) {
|
||||
if let Ok(pos) = pos() {
|
||||
pos
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pos() -> io::Result<(u16, u16)> {
|
||||
// if we enable raw modes with screen, this could cause problems if raw mode is already enabled in applicaition.
|
||||
// I am not completely happy with this approach so feel free to find an other way.
|
||||
|
||||
unsafe {
|
||||
if !unix::RAW_MODE_ENABLED_BY_USER || !unix::RAW_MODE_ENABLED_BY_SYSTEM {
|
||||
// set this boolean so that we know that the systems has enabled raw mode.
|
||||
unix::RAW_MODE_ENABLED_BY_SYSTEM = true;
|
||||
unix::into_raw_mode()?;
|
||||
}
|
||||
}
|
||||
|
||||
// Where is the cursor?
|
||||
// Use `ESC [ 6 n`.
|
||||
let mut stdout = io::stdout();
|
||||
|
||||
// Write command
|
||||
stdout.write_all(b"\x1B[6n")?;
|
||||
stdout.flush()?;
|
||||
|
||||
let mut buf = [0u8; 2];
|
||||
|
||||
// Expect `ESC[`
|
||||
io::stdin().read_exact(&mut buf)?;
|
||||
if buf[0] != 0x1B || buf[1] as char != '[' {
|
||||
return Err(Error::new(ErrorKind::Other, "test"));
|
||||
}
|
||||
|
||||
// Read rows and cols through a ad-hoc integer parsing function
|
||||
let read_num: fn() -> Result<(i32, char), Error> = || -> Result<(i32, char), Error> {
|
||||
let mut num = 0;
|
||||
let mut c: char;
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8; 1];
|
||||
io::stdin().read_exact(&mut buf)?;
|
||||
c = buf[0] as char;
|
||||
if let Some(d) = c.to_digit(10) {
|
||||
num = if num == 0 { 0 } else { num * 10 };
|
||||
num += d as i32;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((num, c))
|
||||
};
|
||||
|
||||
// Read rows and expect `;`
|
||||
let (rows, c) = read_num()?;
|
||||
if c != ';' {
|
||||
return Err(Error::new(ErrorKind::Other, "test"));
|
||||
}
|
||||
|
||||
// Read cols
|
||||
let (cols, c) = read_num()?;
|
||||
|
||||
// Expect `R`
|
||||
let res = if c == 'R' {
|
||||
Ok(((cols - 1) as u16, (rows - 1) as u16))
|
||||
} else {
|
||||
return Err(Error::new(ErrorKind::Other, "test"));
|
||||
};
|
||||
|
||||
// If raw mode is enabled from else where in the application (by the user) we do not want to disable raw modes.
|
||||
// I am not completely happy with this approach so feel free to find an other way.
|
||||
unsafe {
|
||||
if unix::RAW_MODE_ENABLED_BY_SYSTEM && !unix::RAW_MODE_ENABLED_BY_USER {
|
||||
unix::RAW_MODE_ENABLED_BY_SYSTEM = false;
|
||||
unix::disable_raw_mode()?;
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
//! This module handles some logic for cursor interaction in the windows console.
|
||||
|
||||
use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer};
|
||||
#[cfg(windows)]
|
||||
pub fn get_cursor_position() -> (u16, u16) {
|
||||
if let Ok(cursor) = Cursor::new() {
|
||||
cursor.position().unwrap().into()
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer};
|
||||
|
||||
use winapi::{
|
||||
shared::minwindef::{FALSE, TRUE},
|
2
crossterm_input/.gitignore
vendored
Normal file
2
crossterm_input/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
27
crossterm_input/Cargo.toml
Normal file
27
crossterm_input/Cargo.toml
Normal file
@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "crossterm_input"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "A cross-platform library for reading userinput."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
documentation = "https://docs.rs/crossterm_input/"
|
||||
license = "MIT"
|
||||
keywords = ["input", "keys", "crossterm", "crossplatform", "terminal"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.5", features = ["winnt"] }
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
||||
|
||||
[dependencies]
|
||||
crossterm_utils = { path = "../crossterm_utils" }
|
||||
|
||||
[[example]]
|
||||
name = "input"
|
||||
path = "examples/input.rs"
|
||||
|
||||
[[example]]
|
||||
name = "async_input"
|
||||
path = "examples/async_input.rs"
|
138
crossterm_input/README.md
Normal file
138
crossterm_input/README.md
Normal file
@ -0,0 +1,138 @@
|
||||
# Crossterm Input | cross-platform input reading .
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_input.svg
|
||||
[l1]: https://crates.io/crates/crossterm_input
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_input/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_input/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_input/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_input/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_input?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_input.svg?branch=master
|
||||
|
||||
This crate allows you to read the user input cross-platform.
|
||||
It supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is a sub-crate of [crossterm](https://crates.io/crates/crossterm) to read the user input, and can be use individually.
|
||||
|
||||
Other sub-crates are:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
|
||||
When you want to use other modules as well you might want to use crossterm with [feature flags](https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html)
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
- [Features](#features)
|
||||
- [Examples](#examples)
|
||||
- [Tested Terminals](#tested-terminals)
|
||||
- [Notice](#notice)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for `crossterm_input` version `0.1` if you have an older version I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
|
||||
Add the `crossterm_input` package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
`crossterm_input` = "0.1"
|
||||
|
||||
```
|
||||
And import the `crossterm_input` modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm_input;
|
||||
|
||||
pub use crossterm_input::{input, AsyncReader, KeyEvent, TerminalInput};
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Documentation](https://docs.rs/crossterm_input/)
|
||||
- [Crates.io](https://crates.io/crates/crossterm_input)
|
||||
- [Book](http://atcentra.com/crossterm/input.html)
|
||||
- [Examples](/examples)
|
||||
|
||||
## Features
|
||||
These are the features of this crate:
|
||||
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependenties.
|
||||
- Input
|
||||
- Read character
|
||||
- Read line
|
||||
- Read async
|
||||
- Read async until
|
||||
- Wait for key event (terminal pause)
|
||||
|
||||
Planned features:
|
||||
- Read mouse events
|
||||
- Read special keys events
|
||||
|
||||
## Examples
|
||||
Check out the [examples](/examples/) for more information about how to use this crate.
|
||||
|
||||
```rust
|
||||
use crossterm_input::input;
|
||||
|
||||
let mut input = input();
|
||||
|
||||
match input.read_char() {
|
||||
Ok(s) => println!("char typed: {}", s),
|
||||
Err(e) => println!("char error : {}", e),
|
||||
}
|
||||
|
||||
match input.read_line() {
|
||||
Ok(s) => println!("string typed: {}", s),
|
||||
Err(e) => println!("error: {}", e),
|
||||
}
|
||||
|
||||
```
|
||||
## Tested terminals
|
||||
|
||||
- Windows Powershell
|
||||
- Windows 10 (pro)
|
||||
- Windows CMD
|
||||
- Windows 10 (pro)
|
||||
- Windows 8.1 (N)
|
||||
- Ubuntu Desktop Terminal
|
||||
- Ubuntu 17.10
|
||||
- (Arch, Manjaro) KDE Konsole
|
||||
- Linux Mint
|
||||
|
||||
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
|
||||
This library is average stable now, I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
131
crossterm_input/examples/async_input.rs
Normal file
131
crossterm_input/examples/async_input.rs
Normal file
@ -0,0 +1,131 @@
|
||||
extern crate crossterm_input;
|
||||
|
||||
use self::crossterm_input::input;
|
||||
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::time::Duration;
|
||||
use std::{thread, time};
|
||||
|
||||
/// this will capture the input until the given key.
|
||||
/// TODO: make sure terminal is in raw mode before this function is called.
|
||||
/// for more information checkout `crossterm_screen` or use crossterm with the `screen` feature flag.
|
||||
pub fn read_async_until() {
|
||||
// TODO: make sure terminal is in raw mode.
|
||||
// for more information checkout `crossterm_screen` or use crossterm with the `screen` feature flag.
|
||||
|
||||
// init some modules we use for this demo
|
||||
let input = input();
|
||||
|
||||
let mut stdin = input.read_until_async(b'\r').bytes();
|
||||
|
||||
for _i in 0..100 {
|
||||
let a = stdin.next();
|
||||
|
||||
println!("pressed key: {:?}", a);
|
||||
|
||||
if let Some(Ok(b'\r')) = a {
|
||||
println!("The enter key is hit and program is not listening to input anymore.");
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(Ok(b'x')) = a {
|
||||
println!("The key: x was pressed and program is terminated.");
|
||||
break;
|
||||
}
|
||||
|
||||
thread::sleep(time::Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
|
||||
/// this will read pressed characters async until `x` is typed.
|
||||
/// TODO: make sure terminal is in raw mode before this function is called.
|
||||
/// for more information checkout `crossterm_screen` or use crossterm with the `screen` feature flag.
|
||||
pub fn read_async() {
|
||||
let input = input();
|
||||
|
||||
let mut stdin = input.read_async().bytes();
|
||||
|
||||
for _i in 0..100 {
|
||||
let a = stdin.next();
|
||||
|
||||
println!("pressed key: {:?}", a);
|
||||
|
||||
if let Some(Ok(b'x')) = a {
|
||||
println!("The key: `x` was pressed and program is terminated.");
|
||||
break;
|
||||
}
|
||||
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: make sure terminal is in raw mode before this function is called.
|
||||
/// for more information checkout `crossterm_screen` or use crossterm with the `screen` feature flag.
|
||||
pub fn read_async_demo() {
|
||||
// init some modules we use for this demo
|
||||
let input = input();
|
||||
|
||||
// this will setup the async reading.
|
||||
let mut stdin = input.read_async().bytes();
|
||||
|
||||
// clear terminal and reset the cursor.
|
||||
terminal.clear(ClearType::All);
|
||||
cursor.goto(1, 1);
|
||||
|
||||
// loop until the enter key (\r) is pressed.
|
||||
loop {
|
||||
terminal.clear(ClearType::All);
|
||||
cursor.goto(1, 1);
|
||||
|
||||
// get the next pressed key
|
||||
let pressed_key = stdin.next();
|
||||
terminal.write(format!("{:?} <- Character pressed", pressed_key));
|
||||
|
||||
// check if pressed key is enter (\r)
|
||||
if let Some(Ok(b'\r')) = pressed_key {
|
||||
break;
|
||||
}
|
||||
|
||||
// wait 200 ms and reset cursor write
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: make sure terminal is in raw mode before this function is called.
|
||||
/// for more information checkout `crossterm_screen` or use crossterm with the `screen` feature flag.
|
||||
pub fn async_reading_on_alternate_screen() {
|
||||
let screen = Screen::new(false);
|
||||
|
||||
// switch to alternate screen
|
||||
if let Ok(alternate) = screen.enable_alternate_modes(true) {
|
||||
let crossterm = Crossterm::from_screen(&alternate.screen);
|
||||
// init some modules we use for this demo
|
||||
let input = crossterm.input();
|
||||
let terminal = crossterm.terminal();
|
||||
let mut cursor = crossterm.cursor();
|
||||
|
||||
// this will setup the async reading.
|
||||
let mut stdin = input.read_async().bytes();
|
||||
|
||||
// loop until the enter key (\r) is pressed.
|
||||
loop {
|
||||
terminal.clear(ClearType::All);
|
||||
cursor.goto(1, 1);
|
||||
|
||||
// get the next pressed key
|
||||
let pressed_key = stdin.next();
|
||||
|
||||
terminal.write(format!("{:?} <- Character pressed", pressed_key));
|
||||
|
||||
// check if pressed key is enter (\r)
|
||||
if let Some(Ok(b'\r')) = pressed_key {
|
||||
break;
|
||||
}
|
||||
|
||||
// wait 200 ms and reset cursor write
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,7 +1,6 @@
|
||||
extern crate crossterm;
|
||||
extern crate crossterm_input;
|
||||
|
||||
use self::crossterm::input::{input, KeyEvent, TerminalInput};
|
||||
use self::crossterm::Screen;
|
||||
use self::crossterm_input::{input, KeyEvent, Screen, TerminalInput};
|
||||
|
||||
pub fn read_char() {
|
||||
let input = input();
|
||||
@ -23,5 +22,8 @@ pub fn read_line() {
|
||||
|
||||
pub fn pause_terminal() {
|
||||
println!("Press 'x' to quit...");
|
||||
TerminalInput::wait_until(KeyEvent::OnKeyPress(b'x'));
|
||||
let terminal_input = TerminalInput::new();
|
||||
terminal_input.wait_until(KeyEvent::OnKeyPress(b'x'));
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -3,30 +3,36 @@
|
||||
|
||||
use super::*;
|
||||
use std::{thread, time::Duration};
|
||||
use Screen;
|
||||
|
||||
/// Struct that stores a platform-specific implementation for input related actions.
|
||||
use crossterm_utils::TerminalOutput;
|
||||
|
||||
/// Allows you to preform actions with the < option >.
|
||||
///
|
||||
/// Check `/examples/input` the examples folder on github for more info.
|
||||
/// # Features:
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate crossterm;
|
||||
/// use self::crossterm::Screen;
|
||||
/// use self::crossterm::input;
|
||||
/// - features
|
||||
///
|
||||
/// let input = input();
|
||||
/// let result = input.read_line();
|
||||
/// let pressed_char = input.read_char();
|
||||
///```
|
||||
/// Check `/examples/` in the library for more specific examples.
|
||||
///
|
||||
/// **!! Take note when using input with raw mode you should use the `Screen` type. !!**
|
||||
/// # Remarks
|
||||
///
|
||||
/// ```
|
||||
/// let screen = Screen::new(true);
|
||||
/// let input = crossterm::input::from_screen(&screen);
|
||||
/// ```
|
||||
/// When you want to use 'input' related actions on 'alternate screen' use the `Screen` type instead, and pass it to the `terminal::from_screen()` function.
|
||||
/// By doing that terminal actions will be performed on the alternate screen.
|
||||
/// When you want to use '< name >' on 'alternate screen' use the 'crossterm_screen' crate.
|
||||
|
||||
/// Allows you to read user input.
|
||||
///
|
||||
/// # Features:
|
||||
///
|
||||
/// - Read character
|
||||
/// - Read line
|
||||
/// - Read async
|
||||
/// - Read async until
|
||||
/// - Wait for key event (terminal pause)
|
||||
///
|
||||
/// Check `/examples/` in the library for more specific examples.
|
||||
///
|
||||
/// # Remarks
|
||||
///
|
||||
/// When you want to use 'input' on 'alternate screen' use the 'crossterm_screen' crate.
|
||||
pub struct TerminalInput<'stdout> {
|
||||
terminal_input: Box<ITerminalInput + Sync + Send>,
|
||||
stdout: Option<&'stdout Arc<TerminalOutput>>,
|
||||
@ -49,14 +55,15 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
|
||||
/// Create a new instance of `TerminalInput` whereon input related actions could be preformed.
|
||||
///
|
||||
/// **Note**
|
||||
/// # Remarks
|
||||
///
|
||||
/// Use this function when you want your terminal to operate with a specific output.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode'.
|
||||
/// And you want your actions from the `TerminalInput`, created by this function, to operate on the 'alternate screen'.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode',
|
||||
/// and you want your actions from the `TerminalInput`, created by this function, to operate on the 'alternate screen'.
|
||||
///
|
||||
/// You should checkout the 'crossterm_screen' crate for more information about this.
|
||||
/// # Example
|
||||
/// ```
|
||||
/// ```rust
|
||||
/// let screen = Screen::default();
|
||||
//
|
||||
/// if let Ok(alternate) = screen.enable_alternate_modes(false) {
|
||||
@ -78,9 +85,12 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
|
||||
/// Read one line from the user input.
|
||||
///
|
||||
/// Note that this function only works when rawscreen is not turned on.
|
||||
/// When you do want to read a line in raw mode please checkout `read_async` or `read_async_until`.
|
||||
/// # Remark
|
||||
/// This function is not work when raw screen is turned on.
|
||||
/// When you do want to read a line in raw mode please, checkout `read_async` or `read_async_until`.
|
||||
/// Not sure what 'raw mode' is, checkout the 'crossterm_screen' crate.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let input = input();
|
||||
/// match input.read_line() {
|
||||
@ -118,8 +128,11 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
|
||||
/// Read the input asynchronously from the user.
|
||||
///
|
||||
/// This call will not block the current thread.
|
||||
/// Under the hood a thread is fired which will read input on unix systems from TTY and on windows systems with '_getwch' and '_getwche'
|
||||
/// # Remarks
|
||||
/// - This call will not block the current thread.
|
||||
/// A thread will be fired to read input, on unix systems from TTY and on windows systems with '_getwch' and '_getwche'.
|
||||
/// - Requires 'raw screen to be enabled'.
|
||||
/// Not sure what this is, please checkout the 'crossterm_screen' crate.
|
||||
///
|
||||
/// ```rust
|
||||
/// // we need to enable raw mode otherwise the characters will be outputted by default before we are able to read them.
|
||||
@ -152,6 +165,13 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
///
|
||||
/// This is the same as `read_async()` but stops reading when a certain character is hit.
|
||||
///
|
||||
/// # Remarks
|
||||
/// - This call will not block the current thread.
|
||||
/// A thread will be fired to read input, on unix systems from TTY and on windows systems with '_getwch' and '_getwche'.
|
||||
/// - Requires 'raw screen to be enabled'.
|
||||
/// Not sure what this is, please checkout the 'crossterm_screen' crate.
|
||||
/// - Thread is automatically destroyed when the 'delimiter' is hit.
|
||||
///
|
||||
/// ```rust
|
||||
/// // we need to enable raw mode otherwise the characters will be outputted by default before we are able to read them.
|
||||
/// let screen = Screen::new(true);
|
||||
@ -191,7 +211,9 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
|
||||
/// This will prevent the current thread from continuing until the passed `KeyEvent` has happened.
|
||||
///
|
||||
/// This function will put the terminal into raw mode so that any key presses will not be shown at the screen.
|
||||
/// # Remark
|
||||
/// - Requires 'raw screen to be enabled'.
|
||||
/// Not sure what this is, please checkout the 'crossterm_screen' crate.
|
||||
///
|
||||
/// ```
|
||||
/// use crossterm::input::{TerminalInput, KeyEvent};
|
||||
@ -201,11 +223,8 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
/// TerminalInput::wait_until(KeyEvent::OnKeyPress(b'x'));
|
||||
/// }
|
||||
/// ```
|
||||
pub fn wait_until(key_event: KeyEvent) {
|
||||
let screen = Screen::new(true);
|
||||
let input = from_screen(&screen);
|
||||
|
||||
let mut stdin = input.read_async().bytes();
|
||||
pub fn wait_until(&self, key_event: KeyEvent) {
|
||||
let mut stdin = self.read_async().bytes();
|
||||
|
||||
loop {
|
||||
let pressed_key: Option<Result<u8, Error>> = stdin.next();
|
||||
@ -239,9 +258,3 @@ impl<'stdout> TerminalInput<'stdout> {
|
||||
pub fn input<'stdout>() -> TerminalInput<'stdout> {
|
||||
TerminalInput::new()
|
||||
}
|
||||
|
||||
/// Get a `TerminalInput` instance whereon input related actions can be performed.
|
||||
/// Pass the reference to any `Screen` you want this type to perform actions on.
|
||||
pub fn from_screen(screen: &Screen) -> TerminalInput {
|
||||
TerminalInput::from_output(&screen.stdout)
|
||||
}
|
@ -13,12 +13,12 @@ use self::unix_input::UnixInput;
|
||||
#[cfg(target_os = "windows")]
|
||||
use self::windows_input::WindowsInput;
|
||||
|
||||
pub use self::input::{from_screen, input, TerminalInput};
|
||||
pub use self::input::{input, TerminalInput};
|
||||
|
||||
use std::io::{self, Error, ErrorKind, Read};
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
use TerminalOutput;
|
||||
use crossterm_utils::TerminalOutput;
|
||||
|
||||
/// This trait defines the actions that can be preformed with the terminal input.
|
||||
/// This trait can be implemented so that a concrete implementation of the ITerminalInput can fulfill
|
@ -1,8 +1,9 @@
|
||||
//! This is a UNIX specific implementation for input related action.
|
||||
|
||||
use super::*;
|
||||
use kernel::unix_kernel::terminal::{get_tty, read_char};
|
||||
use crate::sys::unix::{get_tty, read_char};
|
||||
|
||||
use crossterm_utils::TerminalOutput;
|
||||
use std::char;
|
||||
use std::thread;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use crossterm_utils::TerminalOutput;
|
||||
use std::char;
|
||||
use std::thread;
|
||||
use winapi::um::winnt::INT;
|
6
crossterm_input/src/lib.rs
Normal file
6
crossterm_input/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
||||
extern crate crossterm_utils;
|
||||
|
||||
mod input;
|
||||
mod sys;
|
||||
|
||||
pub use self::input::{input, AsyncReader, KeyEvent, TerminalInput};
|
2
crossterm_input/src/sys/mod.rs
Normal file
2
crossterm_input/src/sys/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
70
crossterm_input/src/sys/unix.rs
Normal file
70
crossterm_input/src/sys/unix.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use crossterm_utils::sys::unix;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
/// 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 mut tty_f: fs::File = unsafe { ::std::mem::zeroed() };
|
||||
|
||||
let _fd = unsafe {
|
||||
if libc::isatty(libc::STDIN_FILENO) == 1 {
|
||||
libc::STDIN_FILENO
|
||||
} else {
|
||||
tty_f = fs::File::open("/dev/tty")?;
|
||||
tty_f.as_raw_fd()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(tty_f)
|
||||
}
|
||||
|
||||
pub fn read_char() -> io::Result<char> {
|
||||
let mut buf = [0u8; 20];
|
||||
|
||||
let fd = unix::into_raw_mode()?;
|
||||
|
||||
// read input and convert it to char
|
||||
let rv = unsafe {
|
||||
let read = libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, 20);
|
||||
|
||||
if read < 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else if buf[0] == b'\x03' {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Interrupted,
|
||||
"read interrupted",
|
||||
))
|
||||
} else {
|
||||
let mut pressed_char = Ok(' ');
|
||||
|
||||
if let Ok(s) = ::std::str::from_utf8(&buf[..read as usize]) {
|
||||
if let Some(c) = s.chars().next() {
|
||||
pressed_char = Ok(c);
|
||||
}
|
||||
} else {
|
||||
pressed_char = Err(io::Error::new(
|
||||
io::ErrorKind::Interrupted,
|
||||
"Could not parse char to utf8 char",
|
||||
));
|
||||
}
|
||||
|
||||
pressed_char
|
||||
}
|
||||
};
|
||||
|
||||
unix::disable_raw_mode()?;
|
||||
|
||||
// if the user hit ^C we want to signal SIGINT to outselves.
|
||||
if let Err(ref err) = rv {
|
||||
if err.kind() == io::ErrorKind::Interrupted {
|
||||
unsafe {
|
||||
libc::raise(libc::SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv
|
||||
}
|
2
crossterm_screen/.gitignore
vendored
Normal file
2
crossterm_screen/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
19
crossterm_screen/Cargo.toml
Normal file
19
crossterm_screen/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "crossterm_screen"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "A cross-platform library for raw and alternate screen."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
documentation = "https://docs.rs/crossterm_screen/"
|
||||
license = "MIT"
|
||||
keywords = ["screen", "alternate", "raw", "crossterm", "terminal"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
crossterm_utils = { path = "../crossterm_utils" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.5", features = ["minwindef", "wincon"] }
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
121
crossterm_screen/README.md
Normal file
121
crossterm_screen/README.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Crossterm Screen | cross-platform alternate, raw screen.
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_screen.svg
|
||||
[l1]: https://crates.io/crates/crossterm_screen
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_screen/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_screen/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_screen/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_screen/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_screen?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_screen.svg?branch=master
|
||||
|
||||
This crate allows you to work with alternate and raw screen cross-platform.
|
||||
It supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is a sub-crate of [crossterm](https://crates.io/crates/crossterm) to move between screen buffers and switch to raw-mode, it can be use individually.
|
||||
|
||||
Other sub-crates are:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
|
||||
When you want to use other modules as well you might want to use crossterm with [feature flags](https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html)
|
||||
|
||||
In case you are wondering what 'alternate' or 'raw' screen is, you could checkout the [book](http://atcentra.com/crossterm/screen.html) describing this in more detail.
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
- [Features](#features)
|
||||
- [Examples](#examples)
|
||||
- [Tested Terminals](#tested-terminals)
|
||||
- [Notice](#notice)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for `crossterm_screen` version `0.1` if you have an older version I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md).
|
||||
Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate
|
||||
and the [book](http://atcentra.com/crossterm/screen.html) for more information about how to use the alternate or raw screen options.
|
||||
|
||||
Add the `crossterm_screen` package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
`crossterm_screen` = "0.1"
|
||||
|
||||
```
|
||||
And import the `crossterm_screen` modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm_screen;
|
||||
|
||||
pub use crossterm_screen::{AlternateScreen, RawScreen, Screen};
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Documentation](https://docs.rs/crossterm_screen/)
|
||||
- [Crates.io](https://crates.io/crates/crossterm_screen)
|
||||
- [Book](http://atcentra.com/crossterm/screen.html)
|
||||
- [Examples](/examples)
|
||||
|
||||
## Features
|
||||
These are the features of this crate:
|
||||
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependenties.
|
||||
- Alternate screen
|
||||
- Raw screen
|
||||
|
||||
Planned features:
|
||||
- make is possible to switch between multiple buffers.
|
||||
|
||||
## Examples
|
||||
Check out the [examples](/examples/) for more information about how to use this crate.
|
||||
|
||||
## Tested terminals
|
||||
|
||||
- Windows Powershell
|
||||
- Windows 10 (pro)
|
||||
- Windows CMD
|
||||
- Windows 10 (pro)
|
||||
- Windows 8.1 (N)
|
||||
- Ubuntu Desktop Terminal
|
||||
- Ubuntu 17.10
|
||||
- (Arch, Manjaro) KDE Konsole
|
||||
- Linux Mint
|
||||
|
||||
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
|
||||
This library is average stable now, I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
16
crossterm_screen/examples/alternate_screen.rs
Normal file
16
crossterm_screen/examples/alternate_screen.rs
Normal file
@ -0,0 +1,16 @@
|
||||
extern crate crossterm_screen;
|
||||
|
||||
use crossterm_screen::Screen;
|
||||
|
||||
use std::io::{stdout, Write};
|
||||
use std::{thread, time};
|
||||
|
||||
/// print wait screen on alternate screen, then switch back.
|
||||
pub fn print_wait_screen_on_alternate_window() {
|
||||
let screen = Screen::default();
|
||||
|
||||
// move to alternate screen, 'false' means if the alternate screen should be in raw modes.
|
||||
if let Ok(alternate) = screen.enable_alternate_modes(false) {
|
||||
// do some stuff on the alternate screen.
|
||||
} // <- alternate screen will be disabled when dropped.
|
||||
}
|
16
crossterm_screen/examples/raw_mode.rs
Normal file
16
crossterm_screen/examples/raw_mode.rs
Normal file
@ -0,0 +1,16 @@
|
||||
extern crate crossterm_screen;
|
||||
|
||||
use crossterm_screen::Screen;
|
||||
|
||||
use std::io::{stdout, Write};
|
||||
use std::{thread, time};
|
||||
|
||||
pub fn raw_modes() {
|
||||
// create a Screen instance who operates on the default output; io::stdout().
|
||||
let screen = Screen::default();
|
||||
|
||||
// create a Screen instance who operates on the default output; io::stdout(). By passing in 'true' we make this screen 'raw'
|
||||
let screen = Screen::new(true);
|
||||
|
||||
drop(screen); // <-- by dropping the screen raw modes will be disabled.
|
||||
}
|
18
crossterm_screen/src/lib.rs
Normal file
18
crossterm_screen/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//! A module which provides some functionalities to work with the terminal screen.
|
||||
//! Like allowing you to switch between main and alternate screen or putting the terminal into raw mode.
|
||||
#[macro_use]
|
||||
extern crate crossterm_utils;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
mod screen;
|
||||
mod sys;
|
||||
|
||||
pub use self::screen::{AlternateScreen, RawScreen, Screen};
|
@ -5,9 +5,14 @@
|
||||
//! For an example of this behavior, consider when vim is launched from bash.
|
||||
//! Vim uses the entirety of the screen to edit the file, then returning to bash leaves the original buffer unchanged.
|
||||
|
||||
use super::commands::{self, IAlternateScreenCommand};
|
||||
#[cfg(windows)]
|
||||
use crate::sys::winapi::ToAlternateScreenCommand;
|
||||
#[cfg(windows)]
|
||||
use crossterm_utils::get_module;
|
||||
|
||||
use crate::sys::{self, IAlternateScreenCommand};
|
||||
|
||||
use super::{RawScreen, Screen, TerminalOutput};
|
||||
use common::functions;
|
||||
use std::convert::From;
|
||||
use std::io;
|
||||
|
||||
@ -40,15 +45,14 @@ impl AlternateScreen {
|
||||
raw_mode: bool,
|
||||
) -> io::Result<AlternateScreen> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let command =
|
||||
functions::get_module::<Box<commands::IAlternateScreenCommand + Sync + Send>>(
|
||||
Box::from(commands::win_commands::ToAlternateScreenCommand::new()),
|
||||
Box::from(commands::shared_commands::ToAlternateScreenCommand::new()),
|
||||
let command = get_module::<Box<IAlternateScreenCommand + Sync + Send>>(
|
||||
Box::from(ToAlternateScreenCommand::new()),
|
||||
Box::from(sys::ToAlternateScreenCommand::new()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let command = Box::from(commands::shared_commands::ToAlternateScreenCommand::new());
|
||||
let command = Box::from(sys::ToAlternateScreenCommand::new());
|
||||
|
||||
let mut stdout = stdout;
|
||||
command.enable(&mut stdout)?;
|
@ -5,7 +5,7 @@ mod alternate;
|
||||
mod raw;
|
||||
mod screen;
|
||||
|
||||
use super::{commands, TerminalOutput};
|
||||
use crossterm_utils::TerminalOutput;
|
||||
|
||||
pub use self::alternate::AlternateScreen;
|
||||
pub use self::raw::RawScreen;
|
@ -14,7 +14,7 @@
|
||||
//!
|
||||
//! With these modes you can easier design the terminal screen.
|
||||
|
||||
use super::commands::*;
|
||||
use crate::sys;
|
||||
|
||||
use std::io;
|
||||
|
||||
@ -28,9 +28,9 @@ impl RawScreen {
|
||||
/// Put terminal in raw mode.
|
||||
pub fn into_raw_mode() -> io::Result<()> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut command = unix_command::RawModeCommand::new();
|
||||
let mut command = sys::unix::RawModeCommand::new();
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut command = win_commands::RawModeCommand::new();
|
||||
let mut command = sys::winapi::RawModeCommand::new();
|
||||
|
||||
let _result = command.enable();
|
||||
|
||||
@ -40,9 +40,9 @@ impl RawScreen {
|
||||
/// Put terminal back in original modes.
|
||||
pub fn disable_raw_modes() -> io::Result<()> {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let mut command = unix_command::RawModeCommand::new();
|
||||
let mut command = sys::unix::RawModeCommand::new();
|
||||
#[cfg(target_os = "windows")]
|
||||
let command = win_commands::RawModeCommand::new();
|
||||
let command = sys::winapi::RawModeCommand::new();
|
||||
|
||||
command.disable()?;
|
||||
Ok(())
|
@ -1,5 +1,5 @@
|
||||
use super::{AlternateScreen, RawScreen};
|
||||
use TerminalOutput;
|
||||
use crossterm_utils::TerminalOutput;
|
||||
|
||||
use std::io::Result;
|
||||
use std::io::Write;
|
||||
@ -58,7 +58,8 @@ use std::sync::Arc;
|
||||
/// let input = crossterm::input::from_screen(&alternate_screen.screen);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Remarks
|
||||
/// Note that using `Screen` is preferred over manually using `AlternateScreen` or `RawScreen`.
|
||||
pub struct Screen {
|
||||
buffer: Vec<u8>,
|
||||
pub stdout: Arc<TerminalOutput>,
|
55
crossterm_screen/src/sys/mod.rs
Normal file
55
crossterm_screen/src/sys/mod.rs
Normal file
@ -0,0 +1,55 @@
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod winapi;
|
||||
|
||||
use crossterm_utils::TerminalOutput;
|
||||
|
||||
use std::io;
|
||||
|
||||
/// This command is used for switching to alternate screen and back to main screen.
|
||||
pub struct ToAlternateScreenCommand;
|
||||
|
||||
impl ToAlternateScreenCommand {
|
||||
pub fn new() -> ToAlternateScreenCommand {
|
||||
ToAlternateScreenCommand
|
||||
}
|
||||
}
|
||||
|
||||
impl IAlternateScreenCommand for ToAlternateScreenCommand {
|
||||
/// enable alternate screen.
|
||||
fn enable(&self, stdout: &mut TerminalOutput) -> io::Result<()> {
|
||||
stdout.write_str(csi!("?1049h"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// disable alternate screen.
|
||||
fn disable(&self, stdout: &TerminalOutput) -> io::Result<()> {
|
||||
stdout.write_str(csi!("?1049l"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait provides a way to execute some state changing commands.
|
||||
pub trait IStateCommand {
|
||||
fn execute(&mut self) -> io::Result<()>;
|
||||
fn undo(&mut self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
pub trait IEnableAnsiCommand {
|
||||
fn enable(&self) -> io::Result<bool>;
|
||||
fn disable(&self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
// This trait provides an interface for switching to alternate screen and back.
|
||||
pub trait IAlternateScreenCommand: Sync + Send {
|
||||
fn enable(&self, stdout: &mut TerminalOutput) -> io::Result<()>;
|
||||
fn disable(&self, stdout: &TerminalOutput) -> io::Result<()>;
|
||||
}
|
||||
|
||||
// This trait provides an interface for switching to raw mode and back.
|
||||
pub trait IRawScreenCommand: Sync + Send {
|
||||
fn enable(&mut self) -> io::Result<()>;
|
||||
fn disable(&self) -> io::Result<()>;
|
||||
}
|
@ -1,6 +1,3 @@
|
||||
//! A module which contains the commands that can be used for UNIX systems.
|
||||
use kernel::unix_kernel::terminal;
|
||||
|
||||
use std::io::Result;
|
||||
|
||||
/// This command is used for enabling and disabling raw mode for the terminal.
|
||||
@ -13,17 +10,19 @@ impl RawModeCommand {
|
||||
|
||||
/// Enables raw mode.
|
||||
pub fn enable(&mut self) -> Result<()> {
|
||||
terminal::into_raw_mode()?;
|
||||
crossterm_utils::sys::unix::into_raw_mode()?;
|
||||
|
||||
unsafe { terminal::RAW_MODE_ENABLED_BY_USER = true }
|
||||
// will be removed in 6.1
|
||||
unsafe { crossterm_utils::sys::unix::RAW_MODE_ENABLED_BY_USER = true }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disables raw mode.
|
||||
pub fn disable(&mut self) -> Result<()> {
|
||||
terminal::disable_raw_mode()?;
|
||||
crossterm_utils::sys::unix::disable_raw_mode()?;
|
||||
|
||||
unsafe { terminal::RAW_MODE_ENABLED_BY_USER = false }
|
||||
// will be removed in 6.1
|
||||
unsafe { crossterm_utils::sys::unix::RAW_MODE_ENABLED_BY_USER = false }
|
||||
Ok(())
|
||||
}
|
||||
}
|
73
crossterm_screen/src/sys/winapi.rs
Normal file
73
crossterm_screen/src/sys/winapi.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use super::IAlternateScreenCommand;
|
||||
use crossterm_utils::TerminalOutput;
|
||||
use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
|
||||
use std::io;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::wincon;
|
||||
|
||||
/// This command is used for enabling and disabling raw mode for windows systems.
|
||||
/// For more info check: https://docs.microsoft.com/en-us/windows/console/high-level-console-modes.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RawModeCommand {
|
||||
mask: DWORD,
|
||||
}
|
||||
use self::wincon::{ENABLE_LINE_INPUT, ENABLE_WRAP_AT_EOL_OUTPUT};
|
||||
impl RawModeCommand {
|
||||
pub fn new() -> Self {
|
||||
RawModeCommand {
|
||||
mask: ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_LINE_INPUT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RawModeCommand {
|
||||
/// Enables raw mode.
|
||||
pub fn enable(&mut self) -> io::Result<()> {
|
||||
let console_mode = ConsoleMode::new()?;
|
||||
|
||||
let dw_mode = console_mode.mode()?;
|
||||
|
||||
let new_mode = dw_mode & !self.mask;
|
||||
|
||||
console_mode.set_mode(new_mode)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Disables raw mode.
|
||||
pub fn disable(&self) -> io::Result<()> {
|
||||
let console_mode = ConsoleMode::new()?;
|
||||
|
||||
let dw_mode = console_mode.mode()?;
|
||||
|
||||
let new_mode = dw_mode | self.mask;
|
||||
|
||||
console_mode.set_mode(new_mode)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
/// This command is used for switching to alternate screen and back to main screen.
|
||||
/// check https://docs.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes for more info
|
||||
pub struct ToAlternateScreenCommand;
|
||||
|
||||
impl ToAlternateScreenCommand {
|
||||
pub fn new() -> ToAlternateScreenCommand {
|
||||
return ToAlternateScreenCommand {};
|
||||
}
|
||||
}
|
||||
|
||||
impl IAlternateScreenCommand for ToAlternateScreenCommand {
|
||||
fn enable(&self, _stdout: &mut TerminalOutput) -> io::Result<()> {
|
||||
let alternate_screen = ScreenBuffer::create();
|
||||
alternate_screen.show()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disable(&self, _stdout: &TerminalOutput) -> io::Result<()> {
|
||||
let screen_buffer = ScreenBuffer::from(Handle::output_handle()?);
|
||||
screen_buffer.show()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
2
crossterm_style/.gitignore
vendored
Normal file
2
crossterm_style/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
23
crossterm_style/Cargo.toml
Normal file
23
crossterm_style/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "crossterm_style"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "A cross-platform library styling the terminal output."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
documentation = "https://docs.rs/crossterm_style/"
|
||||
license = "MIT"
|
||||
keywords = ["style", "color", "attributes", "crossterm", "terminal"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3.5", features = ["wincon"] }
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
||||
|
||||
[dependencies]
|
||||
crossterm_utils = { path = "../crossterm_utils" }
|
||||
|
||||
[[example]]
|
||||
name = "style"
|
||||
path = "examples/style.rs"
|
157
crossterm_style/README.md
Normal file
157
crossterm_style/README.md
Normal file
@ -0,0 +1,157 @@
|
||||
# Crossterm Style | cross-platform styling.
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_style.svg
|
||||
[l1]: https://crates.io/crates/crossterm_style
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_style/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_style/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_style/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_style/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_style?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_style.svg?branch=master
|
||||
|
||||
This crate allows you to style te terminal cross-platform.
|
||||
It supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is a sub-crate of [crossterm](https://crates.io/crates/crossterm) to style te terminal, and can be use individually.
|
||||
|
||||
Other sub-crates are:
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
|
||||
When you want to use other modules as well you might want to use crossterm with [feature flags](https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html)
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
- [Features](#features)
|
||||
- [Examples](#examples)
|
||||
- [Tested Terminals](#tested-terminals)
|
||||
- [Notice](#notice)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for `crossterm_style` version `0.1` if you have an older version I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
|
||||
Add the `crossterm_style` package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
`crossterm_style` = "0.1"
|
||||
|
||||
```
|
||||
And import the `crossterm_style` modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm_style;
|
||||
|
||||
pub use crossterm_style::{color, style, Attribute, Color, ColorType, ObjectStyle, StyledObject, TerminalColor};
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Documentation](https://docs.rs/crossterm_input/)
|
||||
- [Crates.io](https://crates.io/crates/crossterm_input)
|
||||
- [Book](http://atcentra.com/crossterm/styling.html)
|
||||
- [Examples](/examples)
|
||||
|
||||
## Features
|
||||
These are the features of this crate:
|
||||
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependenties.
|
||||
- Styled output
|
||||
- Foreground color (16 base colors)
|
||||
- Background color (16 base colors)
|
||||
- 256 color support (Windows 10 and UNIX only)
|
||||
- RGB support (Windows 10 and UNIX only)
|
||||
- Text Attributes like: bold, italic, underscore and crossed word ect (Windows 10 and UNIX only)
|
||||
|
||||
Planned features:
|
||||
- Easier usage; e.g. `println!("{}Bold{}Blue", Attribute::Bold, Color::Blue)`
|
||||
|
||||
## Examples
|
||||
Check out the [examples](/examples/) for more information about how to use this crate.
|
||||
```rust
|
||||
use crossterm::style::{Color, style};
|
||||
|
||||
// store objcets so it could be painted later to the screen.
|
||||
let style1 = style("Some Blue font on Black background").with(Color::Blue).on(Color::Black);
|
||||
let style2 = style("Some Red font on Yellow background").with(Color::Red).on(Color::Yellow);
|
||||
|
||||
// syling font with (Windows 10 and UNIX systems)
|
||||
let normal = style("Normal text");
|
||||
let bold = style("Bold text").bold();
|
||||
let italic = style("Italic text").italic();
|
||||
let slow_blink = style("Slow blinking text").slow_blink();
|
||||
let rapid_blink = style("Rapid blinking text").rapid_blink();
|
||||
let hidden = style("Hidden text").hidden();
|
||||
let underlined = style("Underlined text").underlined();
|
||||
let reversed = style("Reversed text").reverse();
|
||||
let dimmed = style("Dim text").dim();
|
||||
let crossed_out = style("Crossed out font").crossed_out();
|
||||
|
||||
// paint styled text to screen (this could also be called inline)
|
||||
println!("{}", style1);
|
||||
println!("{}", style2);
|
||||
println!("{}", bold);
|
||||
println!("{}", hidden);
|
||||
...
|
||||
|
||||
// cursom rgb value (Windows 10 and UNIX systems)
|
||||
style("RGB color (10,10,10) ").with(Color::Rgb {
|
||||
r: 10,
|
||||
g: 10,
|
||||
b: 10
|
||||
}));
|
||||
|
||||
// custom ansi color value (Windows 10 and UNIX systems)
|
||||
style("ANSI color value (50) ").with(Color::AnsiValue(50));
|
||||
|
||||
```
|
||||
## Tested terminals
|
||||
|
||||
- Windows Powershell
|
||||
- Windows 10 (pro)
|
||||
- Windows CMD
|
||||
- Windows 10 (pro)
|
||||
- Windows 8.1 (N)
|
||||
- Ubuntu Desktop Terminal
|
||||
- Ubuntu 17.10
|
||||
- (Arch, Manjaro) KDE Konsole
|
||||
- Linux Mint
|
||||
|
||||
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
|
||||
This library is average stable now, I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
236
crossterm_style/examples/style.rs
Normal file
236
crossterm_style/examples/style.rs
Normal file
@ -0,0 +1,236 @@
|
||||
//!
|
||||
//! Examples of coloring the terminal.
|
||||
//!
|
||||
extern crate crossterm_style;
|
||||
|
||||
use self::crossterm_style::{color, style, Color};
|
||||
|
||||
/// print some red font | demonstration.
|
||||
pub fn paint_foreground() {
|
||||
// Create a styled object.
|
||||
// Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum.
|
||||
let styledobject = style("Red foreground").with(Color::Red);
|
||||
|
||||
// Print the object to the given screen and.
|
||||
println!("Colored text: {}", styledobject);
|
||||
|
||||
// Or print inline
|
||||
println!(
|
||||
"Colored text: {}",
|
||||
style("Blue foreground").with(Color::Blue)
|
||||
);
|
||||
}
|
||||
|
||||
/// print some font on red background | demonstration.
|
||||
pub fn paint_background() {
|
||||
// Create a styled object.
|
||||
// Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum.
|
||||
let styledobject = style("Red foreground").on(Color::Red);
|
||||
|
||||
// Print the object to the given screen and.
|
||||
println!("Colored text: {}", styledobject);
|
||||
|
||||
// Or print inline
|
||||
println!("Colored text: {}", style("Red foreground").on(Color::Blue));
|
||||
}
|
||||
|
||||
/// Print all available foreground colors | demonstration.
|
||||
pub fn print_all_foreground_colors() {
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Black : \t\t {} \n", "■")).with(Color::Black)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Red : \t\t {} \n", "■")).with(Color::Red)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Cyan : \t\t {} \n", "■")).with(Color::Cyan)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkCyan : \t {} \n", "■")).with(Color::DarkCyan)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkRed : \t {} \n", "■")).with(Color::DarkRed)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Green : \t {} \n", "■")).with(Color::Green)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkGreen : \t {} \n", "■")).with(Color::DarkGreen)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Blue : \t\t {} \n", "■")).with(Color::Blue)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkBlue : \t {} \n", "■")).with(Color::DarkBlue)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Magenta : \t {} \n", "■")).with(Color::Magenta)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkMagenta : \t {} \n", "■")).with(Color::DarkMagenta)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Yellow : \t {} \n", "■")).with(Color::Yellow)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkYellow : \t {} \n", "■")).with(Color::DarkYellow)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Grey : \t\t {} \n", "■")).with(Color::Grey)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("White : \t {} \n", "■")).with(Color::White)
|
||||
);
|
||||
|
||||
// supported by Unix and < Windows 10 terminals
|
||||
println!(
|
||||
"{}",
|
||||
style("RGB color (10,10,10) ").with(Color::Rgb {
|
||||
r: 10,
|
||||
g: 10,
|
||||
b: 10
|
||||
})
|
||||
);
|
||||
|
||||
// supported by Unix and < Windows 10 terminals
|
||||
println!(
|
||||
"{}",
|
||||
style("RGB color (10,10,10) ").with(Color::AnsiValue(50))
|
||||
);
|
||||
}
|
||||
|
||||
/// Print all available foreground colors | demonstration.
|
||||
pub fn print_all_background_colors() {
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Black : \t {} \n", "■")).on(Color::Black)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Red : \t\t {} \n", "■")).on(Color::Red)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Cyan : \t\t {} \n", "■")).on(Color::Cyan)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkCyan : \t {} \n", "■")).on(Color::DarkCyan)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkRed : \t {} \n", "■")).on(Color::DarkRed)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Green : \t {} \n", "■")).on(Color::Green)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkGreen : \t {} \n", "■")).on(Color::DarkGreen)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Blue : \t\t {} \n", "■")).on(Color::Blue)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkBlue : \t {} \n", "■")).on(Color::DarkBlue)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Magenta : \t {} \n", "■")).on(Color::Magenta)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkMagenta : \t {} \n", "■")).on(Color::DarkMagenta)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Yellow : \t {} \n", "■")).on(Color::Yellow)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("DarkYellow : \t {} \n", "■")).on(Color::DarkYellow)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("Grey : \t\t {} \n", "■")).on(Color::Grey)
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("White : \t {} \n", "■")).on(Color::White)
|
||||
);
|
||||
|
||||
// supported by Unix and < Windows 10 terminals
|
||||
println!(
|
||||
"{}",
|
||||
style("RGB color (10,10,10) ").on(Color::Rgb {
|
||||
r: 10,
|
||||
g: 10,
|
||||
b: 10
|
||||
})
|
||||
);
|
||||
// supported by Unix and < Windows 10 terminals
|
||||
println!(
|
||||
"{}",
|
||||
style("RGB color (10,10,10) ").on(Color::AnsiValue(50))
|
||||
);
|
||||
}
|
||||
|
||||
/// Print font with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration..
|
||||
#[cfg(unix)]
|
||||
pub fn print_font_with_attributes() {
|
||||
println!("{}", style("Normal text"));
|
||||
println!("{}", style("Bold text").bold());
|
||||
println!("{}", style("Italic text").italic());
|
||||
println!("{}", style("Slow blinking text").slow_blink());
|
||||
println!("{}", style("Rapid blinking text").rapid_blink());
|
||||
println!("{}", style("Hidden text").hidden());
|
||||
println!("{}", style("Underlined text").underlined());
|
||||
println!("{}", style("Reversed text").reverse());
|
||||
println!("{}", style("Dim text").dim());
|
||||
println!("{}", style("Crossed out font").crossed_out());
|
||||
}
|
||||
|
||||
/// Print font with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration..
|
||||
#[cfg(windows)]
|
||||
pub fn print_font_with_attributes() {
|
||||
println!("{}", style("Normal text"));
|
||||
println!("{}", style("Bold text").bold());
|
||||
println!("{}", style("Underlined text").underlined());
|
||||
println!("{}", style("Negative text").negative());
|
||||
}
|
||||
|
||||
/// Print all supported RGB colors, not supported for Windows systems < 10 | demonstration.
|
||||
pub fn print_supported_colors() {
|
||||
let count = color().get_available_color_count().unwrap();
|
||||
|
||||
for i in 0..count {
|
||||
println!(
|
||||
"{}",
|
||||
style(format!("White : \t {}", i)).on(Color::AnsiValue(i as u8))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print_all_background_colors();
|
||||
print_all_foreground_colors();
|
||||
print_font_with_attributes();
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
//! This is a ANSI specific implementation for styling related action.
|
||||
//! This module is used for Windows 10 terminals and Unix terminals by default.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use crate::{Color, ColorType, ITerminalColor};
|
||||
use crossterm_utils::{write, write_str, Result, TerminalOutput};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This struct is an ANSI escape code implementation for color related actions.
|
||||
pub struct AnsiColor;
|
||||
@ -15,7 +17,7 @@ impl AnsiColor {
|
||||
|
||||
impl ITerminalColor for AnsiColor {
|
||||
fn set_fg(&self, fg_color: Color, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(
|
||||
write(
|
||||
stdout,
|
||||
format!(
|
||||
csi!("{}m"),
|
||||
@ -26,7 +28,7 @@ impl ITerminalColor for AnsiColor {
|
||||
}
|
||||
|
||||
fn set_bg(&self, bg_color: Color, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(
|
||||
write(
|
||||
stdout,
|
||||
format!(
|
||||
csi!("{}m"),
|
||||
@ -37,7 +39,7 @@ impl ITerminalColor for AnsiColor {
|
||||
}
|
||||
|
||||
fn reset(&self, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write_str(stdout, csi!("0m"))?;
|
||||
write_str(stdout, csi!("0m"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,32 +1,32 @@
|
||||
//! A module that contains all the actions related to the styling of the terminal.
|
||||
//! Like applying attributes to font and changing the foreground and background.
|
||||
|
||||
use super::*;
|
||||
use std::io;
|
||||
use Screen;
|
||||
|
||||
/// Struct that stores a platform-specific implementation for color related actions.
|
||||
use super::*;
|
||||
use crate::{Color, ITerminalColor};
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
|
||||
#[cfg(windows)]
|
||||
use crossterm_utils::get_module;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Allows you to style the terminal.
|
||||
///
|
||||
/// For styling text use the `::crossterm::style()` function. `TerminalColor` will set the colors of the screen permanently and the `style()` will only style the text given.
|
||||
/// # Features:
|
||||
///
|
||||
/// Check `/examples/color` in the library for more specific examples.
|
||||
/// - Foreground color (16 base colors)
|
||||
/// - Background color (16 base colors)
|
||||
/// - 256 color support (Windows 10 and UNIX only)
|
||||
/// - RGB support (Windows 10 and UNIX only)
|
||||
/// - Text Attributes like: bold, italic, underscore and crossed word ect (Windows 10 and UNIX only)
|
||||
///
|
||||
/// Check `/examples/` in the library for more specific examples.
|
||||
///
|
||||
/// ```rust
|
||||
/// use crossterm::style::color;
|
||||
/// # Remarks
|
||||
///
|
||||
/// let colored_terminal = color();
|
||||
///
|
||||
/// // set foreground color
|
||||
/// colored_terminal.set_fg(Color::Red);
|
||||
/// // set background color
|
||||
/// colored_terminal.set_bg(Color::Red);
|
||||
/// // reset color to default
|
||||
/// colored_terminal.reset();
|
||||
/// ```
|
||||
///
|
||||
/// When you want to use 'color' on 'alternate screen' use the `Screen` type instead and pass it to the `color::from_screen()` function.
|
||||
/// By doing that styling actions will be performed on the alternate screen.
|
||||
/// When you want to 'style' on 'alternate screen' use the 'crossterm_screen' crate.
|
||||
pub struct TerminalColor<'stdout> {
|
||||
color: Box<ITerminalColor + Sync + Send>,
|
||||
stdout: Option<&'stdout Arc<TerminalOutput>>,
|
||||
@ -36,11 +36,11 @@ impl<'stdout> TerminalColor<'stdout> {
|
||||
/// Create new instance whereon color related actions can be performed.
|
||||
pub fn new() -> TerminalColor<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let color = functions::get_module::<Box<ITerminalColor + Sync + Send>>(
|
||||
let color = get_module::<Box<ITerminalColor + Sync + Send>>(
|
||||
Box::from(WinApiColor::new()),
|
||||
Box::from(AnsiColor::new()),
|
||||
)
|
||||
.unwrap();
|
||||
.expect("could not extract module");
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let color = Box::from(AnsiColor::new()) as Box<ITerminalColor + Sync + Send>;
|
||||
@ -53,11 +53,13 @@ impl<'stdout> TerminalColor<'stdout> {
|
||||
|
||||
/// Create a new instance of `TerminalColor` whereon coloring could be preformed on the given output.
|
||||
///
|
||||
/// **Note**
|
||||
/// # Remarks
|
||||
///
|
||||
/// Use this function when you want your terminal to operate with a specific output.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode'.
|
||||
/// And you want your actions from the `TerminalColor`, created by this function, to operate on the 'alternate screen'.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode',
|
||||
/// and you want your actions from the `TerminalColor`, created by this function, to operate on the 'alternate screen'.
|
||||
///
|
||||
/// You should checkout the 'crossterm_screen' crate for more information about this.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
@ -69,7 +71,7 @@ impl<'stdout> TerminalColor<'stdout> {
|
||||
/// ```
|
||||
pub fn from_output(stdout: &'stdout Arc<TerminalOutput>) -> TerminalColor<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let color = functions::get_module::<Box<ITerminalColor + Sync + Send>>(
|
||||
let color = get_module::<Box<ITerminalColor + Sync + Send>>(
|
||||
Box::from(WinApiColor::new()),
|
||||
Box::from(AnsiColor::new()),
|
||||
)
|
||||
@ -85,39 +87,16 @@ impl<'stdout> TerminalColor<'stdout> {
|
||||
}
|
||||
|
||||
/// Set the foreground color to the given color.
|
||||
///
|
||||
/// ```rust
|
||||
/// let colored_terminal = color();
|
||||
///
|
||||
/// // Set foreground color of the font
|
||||
/// colored_terminal.set_fg(Color::Red);
|
||||
/// // crossterm provides to set the background from &str or String
|
||||
/// colored_terminal.set_fg(Color::from("Red"));
|
||||
/// ```
|
||||
pub fn set_fg(&self, color: Color) -> Result<()> {
|
||||
self.color.set_fg(color, &self.stdout)
|
||||
}
|
||||
|
||||
/// Set the background color to the given color.
|
||||
///
|
||||
/// ```rust
|
||||
/// let colored_terminal = color();
|
||||
///
|
||||
/// // Set background color of the font
|
||||
/// colored_terminal.set_bg(Color::Red);
|
||||
/// // crossterm provides to set the background from &str or String
|
||||
/// colored_terminal.set_bg(Color::from("Red"));
|
||||
/// ```
|
||||
pub fn set_bg(&self, color: Color) -> Result<()> {
|
||||
self.color.set_bg(color, &self.stdout)
|
||||
}
|
||||
|
||||
/// Reset the terminal colors and attributes to default.
|
||||
///
|
||||
/// ```rust
|
||||
/// let colored_terminal = color();
|
||||
/// colored_terminal.reset();
|
||||
/// ```
|
||||
pub fn reset(&self) -> Result<()> {
|
||||
self.color.reset(&self.stdout)
|
||||
}
|
||||
@ -143,9 +122,3 @@ impl<'stdout> TerminalColor<'stdout> {
|
||||
pub fn color<'stdout>() -> TerminalColor<'stdout> {
|
||||
TerminalColor::new()
|
||||
}
|
||||
|
||||
/// Get a `TerminalColor` instance whereon color related actions can be performed.
|
||||
/// Pass the reference to any `Screen` you want this type to perform actions on.
|
||||
pub fn from_screen(screen: &Screen) -> TerminalColor {
|
||||
TerminalColor::from_output(&screen.stdout)
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
//! A module that contains all the actions related to the styling of the terminal.
|
||||
//! Like applying attributes to font and changing the foreground and background.
|
||||
|
||||
pub mod color;
|
||||
#[macro_use]
|
||||
extern crate crossterm_utils;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
mod color;
|
||||
pub mod objectstyle;
|
||||
pub mod styledobject;
|
||||
|
||||
@ -18,13 +23,11 @@ use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use self::color::{color, from_screen, TerminalColor};
|
||||
pub use self::color::{color, TerminalColor};
|
||||
pub use self::objectstyle::ObjectStyle;
|
||||
pub use self::styledobject::DisplayableObject;
|
||||
pub use self::styledobject::StyledObject;
|
||||
use common::{error::Result, functions};
|
||||
|
||||
use TerminalOutput;
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
|
||||
/// This trait defines the actions that can be preformed with terminal color.
|
||||
/// This trait can be implemented so that a concrete implementation of the ITerminalColor can fulfill
|
||||
@ -45,16 +48,12 @@ trait ITerminalColor {
|
||||
fn color_value(&self, color: Color, color_type: ColorType) -> String;
|
||||
}
|
||||
|
||||
/// This could be used to style an `Displayable` type with colors and attributes.
|
||||
/// This could be used to style a type who is implementing `Display` with colors and attributes.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// extern crate crossterm;
|
||||
/// use crossterm::Crossterm;
|
||||
///
|
||||
/// let crossterm = Crossterm::new();
|
||||
///
|
||||
/// // get an styled object which could be painted to the terminal.
|
||||
/// let styled_object = crossterm.style("Some Blue colored text on black background")
|
||||
/// let styled_object = style("Some Blue colored text on black background")
|
||||
/// .with(Color::Blue)
|
||||
/// .on(Color::Black);
|
||||
///
|
||||
@ -123,8 +122,15 @@ pub enum Color {
|
||||
|
||||
Grey,
|
||||
White,
|
||||
|
||||
Rgb { r: u8, g: u8, b: u8 },
|
||||
/// Color representing RGB-colors;
|
||||
/// r = red
|
||||
/// g = green
|
||||
/// b = blue
|
||||
Rgb {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
},
|
||||
AnsiValue(u8),
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
//! This module contains the logic to style an object that contains some 'content' which can be styled.
|
||||
|
||||
use super::{color, from_screen, Color, ObjectStyle};
|
||||
use Screen;
|
||||
|
||||
use common::error::Result;
|
||||
use super::{color, Color, ObjectStyle};
|
||||
//use Screen;
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::io::Write;
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Attribute;
|
||||
|
||||
@ -19,21 +19,10 @@ pub struct StyledObject<D: Display> {
|
||||
impl<'a, D: Display + 'a> StyledObject<D> {
|
||||
/// Set the foreground of the styled object to the passed `Color`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use self::crossterm::style::{style,Color};
|
||||
/// # Remarks
|
||||
///
|
||||
/// // create an styled object with the foreground color red.
|
||||
/// let styledobject = style("Some colored text").with(Color::Red);
|
||||
/// // create an styled object with the foreground color blue.
|
||||
/// let styledobject1 = style("Some colored text").with(Color::Blue);
|
||||
///
|
||||
/// // print the styledobject to see the result
|
||||
/// println!("{}", styledobject);
|
||||
/// println!("{}", styledobject1);
|
||||
///
|
||||
/// // print an styled object directly.
|
||||
/// println!("{}", style("Some colored text").on(Color::Blue));
|
||||
/// ```
|
||||
/// This methods consumes 'self', and works like a builder.
|
||||
/// By having this functionality you can do: `with().on().attr()`
|
||||
pub fn with(mut self, foreground_color: Color) -> StyledObject<D> {
|
||||
self.object_style = self.object_style.fg(foreground_color);
|
||||
self
|
||||
@ -41,23 +30,10 @@ impl<'a, D: Display + 'a> StyledObject<D> {
|
||||
|
||||
/// Set the background of the styled object to the passed `Color`.
|
||||
///
|
||||
/// #Example
|
||||
/// # Remarks
|
||||
///
|
||||
/// ```rust
|
||||
/// use self::crossterm::style::{style,Color};
|
||||
///
|
||||
/// // create an styled object with the background color red.
|
||||
/// let styledobject = style("Some colored text").on(Color::Red);
|
||||
/// // create an styled object with the foreground color blue.
|
||||
/// let styledobject1 = style("Some colored text").on(Color::Blue);
|
||||
///
|
||||
/// // print the styledobject to see the result
|
||||
/// println!("{}", styledobject);
|
||||
/// println!("{}", styledobject1);
|
||||
///
|
||||
/// // print an styled object directly.
|
||||
/// println!("{}", style("Some colored text").on(Color::Blue));
|
||||
/// ```
|
||||
/// This methods consumes 'self', and works like a builder.
|
||||
/// By having this functionality you can do: `with().on().attr()`
|
||||
pub fn on(mut self, background_color: Color) -> StyledObject<D> {
|
||||
self.object_style = self.object_style.bg(background_color);
|
||||
self
|
||||
@ -65,14 +41,10 @@ impl<'a, D: Display + 'a> StyledObject<D> {
|
||||
|
||||
/// Set the attribute of an styled object to the passed `Attribute`.
|
||||
///
|
||||
/// #Example
|
||||
/// # Remarks
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate crossterm;
|
||||
/// use self::crossterm::style::{style,Attribute};
|
||||
///
|
||||
/// println!("{}", style("Some bold text").attr(Attribute::Bold);
|
||||
/// ```
|
||||
/// This methods consumes 'self', and works like a builder.
|
||||
/// By having this functionality you can do: `with().on().attr()`
|
||||
pub fn attr(mut self, attr: Attribute) -> StyledObject<D> {
|
||||
self.object_style.add_attr(attr);
|
||||
self
|
||||
@ -137,50 +109,6 @@ impl<'a, D: Display + 'a> StyledObject<D> {
|
||||
self.attr(Attribute::CrossedOut)
|
||||
}
|
||||
|
||||
/// This could be used to paint the styled object onto the given screen. You have to pass a reference to the screen whereon you want to perform the painting.
|
||||
///
|
||||
/// ``` rust
|
||||
/// style("Some colored text")
|
||||
/// .with(Color::Blue)
|
||||
/// .on(Color::Black)
|
||||
/// .paint(&screen);
|
||||
/// ```
|
||||
///
|
||||
/// You should take not that `StyledObject` implements `Display`. You don't need to call paint unless you are on alternate screen.
|
||||
/// Checkout `into_displayable()` for more information about this.
|
||||
pub fn paint(&self, screen: &Screen) -> Result<()> {
|
||||
let colored_terminal = from_screen(&screen);
|
||||
let mut reset = true;
|
||||
|
||||
if let Some(bg) = self.object_style.bg_color {
|
||||
colored_terminal.set_bg(bg)?;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
if let Some(fg) = self.object_style.fg_color {
|
||||
colored_terminal.set_fg(fg)?;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
for attr in self.object_style.attrs.iter() {
|
||||
screen
|
||||
.stdout
|
||||
.write_string(format!(csi!("{}m"), *attr as i16))?;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
use std::fmt::Write;
|
||||
let mut content = String::new();
|
||||
write!(content, "{}", self.content)?;
|
||||
screen.stdout.write_string(content)?;
|
||||
screen.stdout.flush()?;
|
||||
|
||||
if reset {
|
||||
colored_terminal.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This converts an styled object into an `DisplayableObject` witch implements: `Display` and could be used inside the write function of the standard library.
|
||||
///
|
||||
/// _StyledObject already implements `Display` right?_
|
||||
@ -195,8 +123,48 @@ impl<'a, D: Display + 'a> StyledObject<D> {
|
||||
/// let display_object = styled_object.into_displayable(&screen);
|
||||
/// println!("Colored text: {}. Default color", display_object);
|
||||
/// ```
|
||||
pub fn into_displayable(self, screen: &'a Screen) -> DisplayableObject<'a, D> {
|
||||
DisplayableObject::new(screen, self)
|
||||
pub fn into_displayable(self, stdout: &'a Arc<TerminalOutput>) -> DisplayableObject<'a, D> {
|
||||
DisplayableObject::new(stdout, self)
|
||||
}
|
||||
|
||||
/// This could be used to paint the styled object onto the given screen. You have to pass a reference to the screen whereon you want to perform the painting.
|
||||
///
|
||||
/// ``` rust
|
||||
/// style("Some colored text")
|
||||
/// .with(Color::Blue)
|
||||
/// .on(Color::Black)
|
||||
/// .paint(&screen);
|
||||
/// ```
|
||||
///
|
||||
/// You should take not that `StyledObject` implements `Display`. You don't need to call paint unless you are on alternate screen.
|
||||
/// Checkout `into_displayable()` for more information about this.
|
||||
pub fn paint(&self, stdout: &Arc<TerminalOutput>) -> Result<()> {
|
||||
let colored_terminal = super::TerminalColor::from_output(stdout);
|
||||
|
||||
let mut reset = true;
|
||||
|
||||
if let Some(bg) = self.object_style.bg_color {
|
||||
colored_terminal.set_bg(bg)?;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
if let Some(fg) = self.object_style.fg_color {
|
||||
colored_terminal.set_fg(fg)?;
|
||||
reset = true;
|
||||
}
|
||||
for attr in self.object_style.attrs.iter() {
|
||||
stdout.write_string(format!(csi!("{}m"), *attr as i16))?;
|
||||
reset = true;
|
||||
}
|
||||
use std::fmt::Write;
|
||||
let mut content = String::new();
|
||||
write!(content, "{}", self.content)?;
|
||||
stdout.write_string(content)?;
|
||||
stdout.flush()?;
|
||||
if reset {
|
||||
colored_terminal.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,6 +194,7 @@ impl<D: Display> Display for StyledObject<D> {
|
||||
colored_terminal.reset().unwrap();
|
||||
std::io::stdout().flush().unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -238,13 +207,16 @@ impl<D: Display> Display for StyledObject<D> {
|
||||
/// ```
|
||||
pub struct DisplayableObject<'a, D: Display + 'a> {
|
||||
styled_object: StyledObject<D>,
|
||||
screen: &'a Screen,
|
||||
output: &'a Arc<TerminalOutput>,
|
||||
}
|
||||
|
||||
impl<'a, D: Display + 'a> DisplayableObject<'a, D> {
|
||||
pub fn new(screen: &'a Screen, styled_object: StyledObject<D>) -> DisplayableObject<'a, D> {
|
||||
pub fn new(
|
||||
screen: &'a Arc<TerminalOutput>,
|
||||
styled_object: StyledObject<D>,
|
||||
) -> DisplayableObject<'a, D> {
|
||||
DisplayableObject {
|
||||
screen,
|
||||
output: screen,
|
||||
styled_object,
|
||||
}
|
||||
}
|
||||
@ -252,7 +224,7 @@ impl<'a, D: Display + 'a> DisplayableObject<'a, D> {
|
||||
|
||||
impl<'a, D: Display + 'a> Display for DisplayableObject<'a, D> {
|
||||
fn fmt(&self, _f: &mut Formatter) -> result::Result<(), fmt::Error> {
|
||||
self.styled_object.paint(&self.screen).unwrap();
|
||||
self.styled_object.paint(self.output).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
//! This is an `WinApi` specific implementation for styling related action.
|
||||
//! This module is used for non supporting `ANSI` Windows terminals.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use kernel::windows_kernel::{Console, Handle, HandleType, ScreenBuffer};
|
||||
use crate::{Color, ColorType, ITerminalColor};
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer};
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
use winapi::um::wincon;
|
||||
|
||||
@ -83,8 +84,6 @@ impl ITerminalColor for WinApiColor {
|
||||
|
||||
/// This will get the winapi color value from the Color and ColorType struct
|
||||
fn color_value(&self, color: Color, color_type: ColorType) -> String {
|
||||
use style::{Color, ColorType};
|
||||
|
||||
let winapi_color: u16;
|
||||
|
||||
let fg_green = wincon::FOREGROUND_GREEN;
|
2
crossterm_terminal/.gitignore
vendored
Normal file
2
crossterm_terminal/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
26
crossterm_terminal/Cargo.toml
Normal file
26
crossterm_terminal/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "crossterm_terminal"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "A cross-platform library for doing terminal related actions."
|
||||
repository = "https://github.com/TimonPost/crossterm"
|
||||
documentation = "https://docs.rs/crossterm_terminal/"
|
||||
license = "MIT"
|
||||
keywords = ["terminal", "clear", "crossplatform", "crossterm", "terminal size"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.43"
|
||||
|
||||
[dependencies]
|
||||
crossterm_utils = { path = "../crossterm_utils" }
|
||||
crossterm_cursor = { path = "../crossterm_cursor" }
|
||||
|
||||
[[example]]
|
||||
name = "terminal"
|
||||
path = "examples/terminal.rs"
|
153
crossterm_terminal/README.md
Normal file
153
crossterm_terminal/README.md
Normal file
@ -0,0 +1,153 @@
|
||||
# Crossterm Terminal | cross-platform terminal actions.
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_terminal.svg
|
||||
[l1]: https://crates.io/crates/crossterm_terminal
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_terminal/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_terminal/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_terminal/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_terminal/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_terminal?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_terminal.svg?branch=master
|
||||
|
||||
This crate allows you to perform terminal related actions cross-platform e.g clearing, resizing etc.
|
||||
It supports all UNIX and windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals](#tested-terminals) for more info)
|
||||
|
||||
This crate is a sub-crate of [crossterm](https://crates.io/crates/crossterm) to perform terminal related actions, and can be use individually.
|
||||
|
||||
Other sub-crates are:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
|
||||
When you want to use other modules as well you might want to use crossterm with [feature flags](https://doc.rust-lang.org/1.30.0/book/first-edition/conditional-compilation.html)
|
||||
|
||||
## Table of contents:
|
||||
- [Getting started](#getting-started)
|
||||
- [Useful links](#useful-links)
|
||||
- [Features](#features)
|
||||
- [Examples](#examples)
|
||||
- [Tested Terminals](#tested-terminals)
|
||||
- [Notice](#notice)
|
||||
- [Contributing](#contributing)
|
||||
- [Authors](#authors)
|
||||
- [License](#license)
|
||||
|
||||
## Getting Started
|
||||
|
||||
This documentation is only for `crossterm_terminal` version `0.1` if you have an older version I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders with detailed examples for all functionality of this crate.
|
||||
|
||||
Add the `crossterm_terminal` package to your `Cargo.toml` file.
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
`crossterm_terminal` = "0.1"
|
||||
|
||||
```
|
||||
And import the `crossterm_terminal` modules you want to use.
|
||||
|
||||
```rust
|
||||
extern crate crossterm_terminal;
|
||||
|
||||
pub use crossterm_terminal::{terminal, Terminal, ClearType};
|
||||
```
|
||||
|
||||
### Useful Links
|
||||
|
||||
- [Documentation](https://docs.rs/crossterm_terminal/)
|
||||
- [Crates.io](https://crates.io/crates/crossterm_terminal)
|
||||
- [Examples](/examples)
|
||||
|
||||
## Features
|
||||
These are the features of this crate:
|
||||
|
||||
- Cross-platform
|
||||
- Everything is multithreaded (Send, Sync)
|
||||
- Detailed documentation on every item
|
||||
- Very few dependenties.
|
||||
- Terminal
|
||||
- Clearing (all lines, current line, from cursor down and up, until new line)
|
||||
- Scrolling (Up, down)
|
||||
- Get the size of the terminal
|
||||
- Set the size of the terminal
|
||||
- Alternate screen
|
||||
- Raw screen
|
||||
- Exit the current process
|
||||
|
||||
## Examples
|
||||
Check out the [examples](/examples/) for more information about how to use this crate.
|
||||
|
||||
```rust
|
||||
use crossterm::terminal::{terminal,ClearType};
|
||||
|
||||
let mut terminal = terminal();
|
||||
|
||||
// Clear all lines in terminal;
|
||||
terminal.clear(ClearType::All);
|
||||
// Clear all cells from current cursor position down.
|
||||
terminal.clear(ClearType::FromCursorDown);
|
||||
// Clear all cells from current cursor position down.
|
||||
terminal.clear(ClearType::FromCursorUp);
|
||||
// Clear current line cells.
|
||||
terminal.clear(ClearType::CurrentLine);
|
||||
// Clear all the cells until next line.
|
||||
terminal.clear(ClearType::UntilNewLine);
|
||||
|
||||
// Get terminal size
|
||||
let (width, height) = terminal.terminal_size();
|
||||
print!("X: {}, y: {}", width, height);
|
||||
|
||||
// Scroll down, up 10 lines.
|
||||
terminal.scroll_down(10);
|
||||
terminal.scroll_up(10);
|
||||
|
||||
// Set terminal size (width, height)
|
||||
terminal.set_size(10,10);
|
||||
|
||||
// exit the current process.
|
||||
terminal.exit();
|
||||
|
||||
// write to the terminal whether you are on the main screen or alternate screen.
|
||||
terminal.write("Some text\n Some text on new line");
|
||||
```
|
||||
|
||||
## Tested terminals
|
||||
|
||||
- Windows Powershell
|
||||
- Windows 10 (pro)
|
||||
- Windows CMD
|
||||
- Windows 10 (pro)
|
||||
- Windows 8.1 (N)
|
||||
- Ubuntu Desktop Terminal
|
||||
- Ubuntu 17.10
|
||||
- (Arch, Manjaro) KDE Konsole
|
||||
- Linux Mint
|
||||
|
||||
This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested.
|
||||
If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it.
|
||||
|
||||
## Notice
|
||||
|
||||
This library is average stable now, I don't expect it to not to change that much.
|
||||
If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
132
crossterm_terminal/examples/terminal.rs
Normal file
132
crossterm_terminal/examples/terminal.rs
Normal file
@ -0,0 +1,132 @@
|
||||
//!
|
||||
//! Terminal Examples
|
||||
//!
|
||||
extern crate crossterm_cursor;
|
||||
extern crate crossterm_terminal;
|
||||
|
||||
use crossterm_cursor::cursor;
|
||||
use crossterm_terminal::{terminal, ClearType};
|
||||
|
||||
fn print_test_data() {
|
||||
for i in 0..100 {
|
||||
println!("Test data to test terminal: {}", i);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all lines in terminal | demonstration
|
||||
pub fn clear_all_lines() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Clear all lines in terminal;
|
||||
terminal.clear(ClearType::All);
|
||||
}
|
||||
|
||||
/// Clear all lines from cursor position X:4, Y:4 down | demonstration
|
||||
pub fn clear_from_cursor_down() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Set terminal cursor position (see example for more info).
|
||||
cursor().goto(4, 8);
|
||||
|
||||
// Clear all cells from current cursor position down.
|
||||
terminal.clear(ClearType::FromCursorDown);
|
||||
}
|
||||
|
||||
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
|
||||
pub fn clear_from_cursor_up() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Set terminal cursor position (see example for more info).
|
||||
cursor().goto(4, 4);
|
||||
|
||||
// Clear all cells from current cursor position down.
|
||||
terminal.clear(ClearType::FromCursorUp);
|
||||
}
|
||||
|
||||
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
|
||||
pub fn clear_current_line() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Set terminal cursor position (see example for more info).
|
||||
cursor().goto(4, 3);
|
||||
|
||||
// Clear current line cells.
|
||||
terminal.clear(ClearType::CurrentLine);
|
||||
}
|
||||
|
||||
/// Clear all lines from cursor position X:4, Y:7 up | demonstration
|
||||
pub fn clear_until_new_line() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Set terminal cursor position (see example for more info).
|
||||
cursor().goto(4, 20);
|
||||
|
||||
// Clear all the cells until next line.
|
||||
terminal.clear(ClearType::UntilNewLine);
|
||||
}
|
||||
|
||||
/// Print the the current terminal size | demonstration.
|
||||
pub fn print_terminal_size() {
|
||||
let terminal = terminal();
|
||||
|
||||
// Get terminal size
|
||||
let (width, height) = terminal.terminal_size();
|
||||
|
||||
// Print results
|
||||
print!("X: {}, y: {}", width, height);
|
||||
}
|
||||
|
||||
/// Set the terminal size to width 10, height: 10 | demonstration.
|
||||
pub fn set_terminal_size() {
|
||||
let terminal = terminal();
|
||||
|
||||
terminal.set_size(10, 10);
|
||||
}
|
||||
|
||||
/// Scroll down 10 lines | demonstration.
|
||||
pub fn scroll_down() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Scroll down 10 lines.
|
||||
terminal.scroll_down(10);
|
||||
}
|
||||
|
||||
/// Scroll down 10 lines | demonstration.
|
||||
pub fn scroll_up() {
|
||||
let terminal = terminal();
|
||||
|
||||
print_test_data();
|
||||
|
||||
// Scroll up 10 lines.
|
||||
terminal.scroll_up(5);
|
||||
}
|
||||
|
||||
/// Resize the terminal to X: 10, Y: 10 | demonstration.
|
||||
pub fn resize_terminal() {
|
||||
let terminal = terminal();
|
||||
|
||||
// Get terminal size
|
||||
terminal.set_size(10, 10);
|
||||
}
|
||||
|
||||
/// exit the current proccess.
|
||||
pub fn exit() {
|
||||
let terminal = terminal();
|
||||
terminal.exit();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
resize_terminal()
|
||||
}
|
14
crossterm_terminal/src/lib.rs
Normal file
14
crossterm_terminal/src/lib.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#[macro_use]
|
||||
extern crate crossterm_utils;
|
||||
extern crate crossterm_cursor;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate libc;
|
||||
|
||||
mod sys;
|
||||
mod terminal;
|
||||
|
||||
pub use self::terminal::{terminal, ClearType, Terminal};
|
10
crossterm_terminal/src/sys/mod.rs
Normal file
10
crossterm_terminal/src/sys/mod.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#[cfg(windows)]
|
||||
pub mod winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::{exit, get_terminal_size};
|
||||
#[cfg(windows)]
|
||||
pub use self::winapi::{exit, get_terminal_size};
|
37
crossterm_terminal/src/sys/unix.rs
Normal file
37
crossterm_terminal/src/sys/unix.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use libc::{c_ushort, ioctl, STDOUT_FILENO, TIOCGWINSZ};
|
||||
|
||||
pub fn exit() {
|
||||
::std::process::exit(0);
|
||||
}
|
||||
|
||||
/// A representation of the size of the current terminal.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct UnixSize {
|
||||
/// number of rows
|
||||
pub rows: c_ushort,
|
||||
/// number of columns
|
||||
pub cols: c_ushort,
|
||||
pub ws_xpixel: c_ushort,
|
||||
pub ws_ypixel: c_ushort,
|
||||
}
|
||||
|
||||
/// Get the current terminal size.
|
||||
pub fn get_terminal_size() -> (u16, u16) {
|
||||
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
|
||||
let us = UnixSize {
|
||||
rows: 0,
|
||||
cols: 0,
|
||||
ws_xpixel: 0,
|
||||
ws_ypixel: 0,
|
||||
};
|
||||
|
||||
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &us) };
|
||||
|
||||
if r == 0 {
|
||||
// because crossterm works starts counting at 0 and unix terminal starts at cell 1 you have subtract one to get 0-based results.
|
||||
(us.cols, us.rows)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
16
crossterm_terminal/src/sys/winapi.rs
Normal file
16
crossterm_terminal/src/sys/winapi.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use crossterm_winapi::ScreenBuffer;
|
||||
|
||||
/// Exit the current process.
|
||||
pub fn exit() {
|
||||
::std::process::exit(256);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn get_terminal_size() -> (u16, u16) {
|
||||
if let Ok(buffer) = ScreenBuffer::current() {
|
||||
let size = buffer.info().unwrap().terminal_size();
|
||||
(size.width as u16, size.height as u16)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
//! This is an `ANSI escape code` specific implementation for terminal related action.
|
||||
//! This module is used for windows 10 terminals and unix terminals by default.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use super::ITerminal;
|
||||
use crate::{sys::get_terminal_size, ClearType};
|
||||
use crossterm_cursor::TerminalCursor;
|
||||
use crossterm_utils::{write, write_str, Result, TerminalOutput};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// This struct is an ansi escape code implementation for terminal related actions.
|
||||
pub struct AnsiTerminal;
|
||||
use cursor::TerminalCursor;
|
||||
|
||||
impl AnsiTerminal {
|
||||
pub fn new() -> AnsiTerminal {
|
||||
@ -18,36 +20,36 @@ impl ITerminal for AnsiTerminal {
|
||||
fn clear(&self, clear_type: ClearType, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
match clear_type {
|
||||
ClearType::All => {
|
||||
functions::write_str(&stdout, csi!("2J"))?;
|
||||
write_str(&stdout, csi!("2J"))?;
|
||||
TerminalCursor::new().goto(0, 0)?;
|
||||
}
|
||||
ClearType::FromCursorDown => {
|
||||
functions::write_str(&stdout, csi!("J"))?;
|
||||
write_str(&stdout, csi!("J"))?;
|
||||
}
|
||||
ClearType::FromCursorUp => {
|
||||
functions::write_str(&stdout, csi!("1J"))?;
|
||||
write_str(&stdout, csi!("1J"))?;
|
||||
}
|
||||
ClearType::CurrentLine => {
|
||||
functions::write_str(&stdout, csi!("2K"))?;
|
||||
write_str(&stdout, csi!("2K"))?;
|
||||
}
|
||||
ClearType::UntilNewLine => {
|
||||
functions::write_str(&stdout, csi!("K"))?;
|
||||
write_str(&stdout, csi!("K"))?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn terminal_size(&self, _stdout: &Option<&Arc<TerminalOutput>>) -> (u16, u16) {
|
||||
functions::get_terminal_size()
|
||||
get_terminal_size()
|
||||
}
|
||||
|
||||
fn scroll_up(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(&stdout, format!(csi!("{}S"), count))?;
|
||||
write(&stdout, format!(csi!("{}S"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scroll_down(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()> {
|
||||
functions::write(&stdout, format!(csi!("{}T"), count))?;
|
||||
write(&stdout, format!(csi!("{}T"), count))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -57,16 +59,7 @@ impl ITerminal for AnsiTerminal {
|
||||
height: i16,
|
||||
stdout: &Option<&Arc<TerminalOutput>>,
|
||||
) -> Result<()> {
|
||||
functions::write(&stdout, format!(csi!("8;{};{}t"), height, width))?;
|
||||
write(&stdout, format!(csi!("8;{};{}t"), height, width))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exit(&self, stdout: &Option<&Arc<TerminalOutput>>) {
|
||||
if let Some(output) = stdout {
|
||||
// drop the screen with the current stdout. This will make sure when in raw mode this will be disabled first.
|
||||
let screen = Screen::from(output.to_owned().clone());
|
||||
drop(screen);
|
||||
functions::exit_terminal();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
//! A module that contains all the actions related to the terminal. like clearing, resizing, pausing and scrolling the terminal.
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
@ -13,18 +12,23 @@ use self::ansi_terminal::AnsiTerminal;
|
||||
#[cfg(target_os = "windows")]
|
||||
use self::winapi_terminal::WinApiTerminal;
|
||||
|
||||
pub use self::terminal::{from_screen, terminal, Terminal};
|
||||
pub use self::terminal::{terminal, Terminal};
|
||||
|
||||
use crossterm_utils::{Result, TerminalOutput};
|
||||
|
||||
use common::{error, functions};
|
||||
use std::sync::Arc;
|
||||
use {Screen, TerminalOutput};
|
||||
|
||||
/// Enum that specifies a way of clearing.
|
||||
pub enum ClearType {
|
||||
/// clear all cells in terminal.
|
||||
All,
|
||||
/// clear all cells from the cursor position downwards in terminal.
|
||||
FromCursorDown,
|
||||
/// clear all cells from the cursor position upwards in terminal.
|
||||
FromCursorUp,
|
||||
/// clear current line cells in terminal.
|
||||
CurrentLine,
|
||||
/// clear all cells from cursor position until new line in terminal.
|
||||
UntilNewLine,
|
||||
}
|
||||
|
||||
@ -38,24 +42,18 @@ pub enum ClearType {
|
||||
/// so that terminal related actions can be preformed on both Unix and Windows systems.
|
||||
trait ITerminal {
|
||||
/// Clear the current cursor by specifying the clear type
|
||||
fn clear(
|
||||
&self,
|
||||
clear_type: ClearType,
|
||||
stdout: &Option<&Arc<TerminalOutput>>,
|
||||
) -> error::Result<()>;
|
||||
fn clear(&self, clear_type: ClearType, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()>;
|
||||
/// Get the terminal size (x,y)
|
||||
fn terminal_size(&self, stdout: &Option<&Arc<TerminalOutput>>) -> (u16, u16);
|
||||
/// Scroll `n` lines up in the current terminal.
|
||||
fn scroll_up(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> error::Result<()>;
|
||||
fn scroll_up(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()>;
|
||||
/// Scroll `n` lines down in the current terminal.
|
||||
fn scroll_down(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> error::Result<()>;
|
||||
fn scroll_down(&self, count: i16, stdout: &Option<&Arc<TerminalOutput>>) -> Result<()>;
|
||||
/// Resize terminal to the given width and height.
|
||||
fn set_size(
|
||||
&self,
|
||||
width: i16,
|
||||
height: i16,
|
||||
stdout: &Option<&Arc<TerminalOutput>>,
|
||||
) -> error::Result<()>;
|
||||
/// Close the current terminal
|
||||
fn exit(&self, stdout: &Option<&Arc<TerminalOutput>>);
|
||||
) -> Result<()>;
|
||||
}
|
@ -1,35 +1,44 @@
|
||||
//! A module that contains all the actions related to the terminal.
|
||||
//! Like clearing and scrolling in the terminal or getting the window size from the terminal.
|
||||
|
||||
use super::*;
|
||||
use common::error::Result;
|
||||
use std::fmt;
|
||||
use super::{AnsiTerminal, ClearType, ITerminal};
|
||||
use crossterm_utils::{write, Result, TerminalOutput};
|
||||
|
||||
/// Struct that stores a platform-specific platform implementation for terminal related actions.
|
||||
#[cfg(windows)]
|
||||
use super::WinApiTerminal;
|
||||
#[cfg(windows)]
|
||||
use crossterm_utils::get_module;
|
||||
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Allows you to preform actions on the terminal.
|
||||
///
|
||||
/// Check `/examples/terminal` in the library for more specific examples.
|
||||
/// # Features:
|
||||
///
|
||||
/// ```rust
|
||||
/// use crossterm::terminal;
|
||||
/// - Clearing (all lines, current line, from cursor down and up, until new line)
|
||||
/// - Scrolling (Up, down)
|
||||
/// - Get the size of the terminal
|
||||
/// - Set the size of the terminal
|
||||
/// - Alternate screen
|
||||
/// - Raw screen
|
||||
/// - Exit the current process
|
||||
///
|
||||
/// let term = terminal();
|
||||
/// Check `/examples/` in the library for more specific examples.
|
||||
///
|
||||
/// term.scroll_down(5);
|
||||
/// term.scroll_up(4);
|
||||
/// let (with, height) = term.terminal_size();
|
||||
/// ```
|
||||
/// When you want to use 'terminal' related actions on 'alternate screen' use the `Screen` type instead, and pass it to the `terminal::from_screen()` function.
|
||||
/// By doing that terminal actions will be performed on the alternate screen.
|
||||
/// # Remarks
|
||||
///
|
||||
/// When you want to perform terminal actions on 'alternate screen' use the 'crossterm_screen' crate.
|
||||
pub struct Terminal<'stdout> {
|
||||
terminal: Box<ITerminal + Sync + Send>,
|
||||
screen: Option<&'stdout Arc<TerminalOutput>>,
|
||||
stdout: Option<&'stdout Arc<TerminalOutput>>,
|
||||
}
|
||||
|
||||
impl<'stdout> Terminal<'stdout> {
|
||||
/// Create new terminal instance whereon terminal related actions can be performed.
|
||||
pub fn new() -> Terminal<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let terminal = functions::get_module::<Box<ITerminal + Sync + Send>>(
|
||||
let terminal = get_module::<Box<ITerminal + Sync + Send>>(
|
||||
Box::new(WinApiTerminal::new()),
|
||||
Box::new(AnsiTerminal::new()),
|
||||
)
|
||||
@ -40,17 +49,19 @@ impl<'stdout> Terminal<'stdout> {
|
||||
|
||||
Terminal {
|
||||
terminal,
|
||||
screen: None,
|
||||
stdout: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new instance of `Terminal` whereon terminal related actions could be preformed on the given output.
|
||||
///
|
||||
/// **Note**
|
||||
/// # Remarks
|
||||
///
|
||||
/// Use this function when you want your terminal to operate with a specific output.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode'.
|
||||
/// And you want your actions from the `Terminal`, created by this function, to operate on the 'alternate screen'.
|
||||
/// This could be useful when you have a screen which is in 'alternate mode',
|
||||
/// and you want your actions from the `Terminal`, created by this function, to operate on the 'alternate screen'.
|
||||
///
|
||||
/// You should checkout the 'crossterm_screen' crate for more information about this.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
@ -62,7 +73,7 @@ impl<'stdout> Terminal<'stdout> {
|
||||
/// ```
|
||||
pub fn from_output(stdout: &'stdout Arc<TerminalOutput>) -> Terminal<'stdout> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let terminal = functions::get_module::<Box<ITerminal + Sync + Send>>(
|
||||
let terminal = get_module::<Box<ITerminal + Sync + Send>>(
|
||||
Box::new(WinApiTerminal::new()),
|
||||
Box::new(AnsiTerminal::new()),
|
||||
)
|
||||
@ -73,12 +84,13 @@ impl<'stdout> Terminal<'stdout> {
|
||||
|
||||
Terminal {
|
||||
terminal,
|
||||
screen: Some(stdout),
|
||||
stdout: Some(stdout),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the current cursor by specifying the clear type.
|
||||
/// Clear the current cursor by specifying the `ClearType`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// let mut term = terminal();
|
||||
///
|
||||
@ -94,43 +106,31 @@ impl<'stdout> Terminal<'stdout> {
|
||||
/// term.clear(terminal::ClearType::UntilNewLine);
|
||||
/// ```
|
||||
pub fn clear(&self, clear_type: ClearType) -> Result<()> {
|
||||
self.terminal.clear(clear_type, &self.screen)
|
||||
self.terminal.clear(clear_type, &self.stdout)
|
||||
}
|
||||
|
||||
/// Get the terminal size (x,y).
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut term = terminal();
|
||||
///
|
||||
/// let size = term.terminal_size();
|
||||
/// println!("{:?}", size);
|
||||
/// ```
|
||||
/// # Remark
|
||||
/// This will return a tuple of (x: u16, y: u16)
|
||||
pub fn terminal_size(&self) -> (u16, u16) {
|
||||
self.terminal.terminal_size(&self.screen)
|
||||
self.terminal.terminal_size(&self.stdout)
|
||||
}
|
||||
|
||||
/// Scroll `n` lines up in the current terminal.
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut term = terminal();
|
||||
///
|
||||
/// // scroll up by 5 lines
|
||||
/// let size = term.scroll_up(5);
|
||||
/// ```
|
||||
/// # Parameter
|
||||
/// - `count`: the number of rows should be shifted up.
|
||||
pub fn scroll_up(&self, count: i16) -> Result<()> {
|
||||
self.terminal.scroll_up(count, &self.screen)
|
||||
self.terminal.scroll_up(count, &self.stdout)
|
||||
}
|
||||
|
||||
/// Scroll `n` lines up in the current terminal.
|
||||
/// Scroll `n` lines down in the current terminal.
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut term = terminal();
|
||||
///
|
||||
/// // scroll down by 5 lines
|
||||
/// let size = term.scroll_down(5);
|
||||
/// ```
|
||||
/// # Parameter
|
||||
/// - `count`: the number of rows should be shifted down.
|
||||
pub fn scroll_down(&self, count: i16) -> Result<()> {
|
||||
self.terminal.scroll_down(count, &self.screen)
|
||||
self.terminal.scroll_down(count, &self.stdout)
|
||||
}
|
||||
|
||||
/// Set the terminal size. Note that not all terminals can be set to a very small scale.
|
||||
@ -142,7 +142,7 @@ impl<'stdout> Terminal<'stdout> {
|
||||
/// let size = term.set_size(10,10);
|
||||
/// ```
|
||||
pub fn set_size(&self, width: i16, height: i16) -> Result<()> {
|
||||
self.terminal.set_size(width, height, &self.screen)
|
||||
self.terminal.set_size(width, height, &self.stdout)
|
||||
}
|
||||
|
||||
/// Exit the current process.
|
||||
@ -153,7 +153,7 @@ impl<'stdout> Terminal<'stdout> {
|
||||
/// let size = term.exit();
|
||||
/// ```
|
||||
pub fn exit(&self) {
|
||||
self.terminal.exit(&self.screen);
|
||||
crate::sys::exit();
|
||||
}
|
||||
|
||||
/// Write any displayable content to the current terminal screen.
|
||||
@ -167,7 +167,7 @@ impl<'stdout> Terminal<'stdout> {
|
||||
use std::fmt::Write;
|
||||
let mut string = String::new();
|
||||
write!(string, "{}", value)?;
|
||||
let size = functions::write(&self.screen, string)?;
|
||||
let size = write(&self.stdout, string)?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
@ -176,9 +176,3 @@ impl<'stdout> Terminal<'stdout> {
|
||||
pub fn terminal<'stdout>() -> Terminal<'stdout> {
|
||||
Terminal::new()
|
||||
}
|
||||
|
||||
/// Get a `Terminal` instance whereon terminal related actions can be performed.
|
||||
/// Pass the reference to any `Screen` you want this type to perform actions on.
|
||||
pub fn from_screen(screen: &Screen) -> Terminal {
|
||||
Terminal::from_output(&screen.stdout)
|
||||
}
|
@ -1,27 +1,20 @@
|
||||
use modules::terminal::ansi_terminal::AnsiTerminal;
|
||||
|
||||
use modules::terminal::ITerminal;
|
||||
|
||||
use Screen;
|
||||
use super::{AnsiTerminal, ITerminal, WinApiTerminal};
|
||||
|
||||
/* ======================== WinApi =========================== */
|
||||
#[cfg(windows)]
|
||||
mod winapi_tests {
|
||||
use super::*;
|
||||
use modules::terminal::winapi_terminal::WinApiTerminal;
|
||||
|
||||
#[test]
|
||||
fn resize_winapi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let terminal = WinApiTerminal::new();
|
||||
|
||||
terminal.set_size(20, 10, &stdout);
|
||||
terminal.set_size(30, 30, &None);
|
||||
|
||||
let (x, y) = terminal.terminal_size(&stdout);
|
||||
let (x, y) = terminal.terminal_size(&None);
|
||||
|
||||
assert_eq!(x, 20);
|
||||
assert_eq!(y, 10);
|
||||
assert_eq!(x, 30);
|
||||
assert_eq!(y, 30);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,16 +23,14 @@ mod winapi_tests {
|
||||
fn resize_ansi() {
|
||||
use std::{thread, time};
|
||||
if try_enable_ansi() {
|
||||
let screen = Screen::default();
|
||||
let stdout = Some(&screen.stdout);
|
||||
let terminal = AnsiTerminal::new();
|
||||
|
||||
terminal.set_size(50, 50, &stdout);
|
||||
terminal.set_size(50, 50, &None).unwrap();
|
||||
|
||||
// see issue: https://github.com/eminence/terminal-size/issues/11
|
||||
thread::sleep(time::Duration::from_millis(30));
|
||||
|
||||
let (x, y) = terminal.terminal_size(&stdout);
|
||||
let (x, y) = terminal.terminal_size(&None);
|
||||
|
||||
assert_eq!(x, 50);
|
||||
assert_eq!(y, 50);
|
||||
@ -50,10 +41,12 @@ fn try_enable_ansi() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if cfg!(target_os = "windows") {
|
||||
use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
|
||||
use crossterm_utils::sys::winapi::ansi::set_virtual_terminal_processing;
|
||||
|
||||
if !try_enable_ansi_support() {
|
||||
return false;
|
||||
// if it is not listed we should try with WinApi to check if we do support ANSI-codes.
|
||||
match set_virtual_terminal_processing(true) {
|
||||
Ok(_) => return true,
|
||||
Err(e) => return false,
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@
|
||||
//! Windows versions lower then windows 10 are not supporting ANSI codes. Those versions will use this implementation instead.
|
||||
|
||||
use super::*;
|
||||
use common::error::{ErrorKind, Result};
|
||||
use kernel::windows_kernel::{Console, Coord, Cursor, Handle, ScreenBuffer, Size};
|
||||
use crossterm_cursor::sys::winapi::Cursor;
|
||||
use crossterm_utils::{ErrorKind, Result, TerminalOutput};
|
||||
use crossterm_winapi::{Console, Coord, Handle, ScreenBuffer, Size};
|
||||
|
||||
/// This struct is an winapi implementation for terminal related actions.
|
||||
pub struct WinApiTerminal;
|
||||
@ -166,15 +167,6 @@ impl ITerminal for WinApiTerminal {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exit(&self, stdout: &Option<&Arc<TerminalOutput>>) {
|
||||
if let Some(output) = stdout {
|
||||
// drop the screen with the current stdout. This will make sure when in raw mode this will be disabled first.
|
||||
let mut screen = Screen::from(output.to_owned().clone());
|
||||
drop(screen);
|
||||
functions::exit_terminal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_after_cursor(
|
2
crossterm_utils/.gitignore
vendored
Normal file
2
crossterm_utils/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
13
crossterm_utils/Cargo.toml
Normal file
13
crossterm_utils/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "crossterm_utils"
|
||||
version = "0.1.0"
|
||||
authors = ["Timon Post <timonpost@hotmail.nl>"]
|
||||
edition = "2018"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
crossterm_winapi = { path = "../crossterm_winapi" }
|
||||
winapi = { version = "0.3.5", features = ["wincon"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.43"
|
||||
termios = "0.3.1"
|
40
crossterm_utils/README.md
Normal file
40
crossterm_utils/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Crossterm Utils | crossterm common used code.
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_utils.svg
|
||||
[l1]: https://crates.io/crates/crossterm_utils
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: ./LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_utils/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_utils/
|
||||
|
||||
[s3]: https://docs.rs/crossterm_utils/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_utils/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_utils?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_utils.svg?branch=master
|
||||
|
||||
This crate is a utilities crate used by the following [crossterm](https://crates.io/crates/crossterm) modules:
|
||||
- [Crossterm Style](https://crates.io/crates/crossterm_style)
|
||||
- [Crossterm Input](https://crates.io/crates/crossterm_input)
|
||||
- [Crossterm Screen](https://crates.io/crates/crossterm_screen)
|
||||
- [Crossterm Cursor](https://crates.io/crates/crossterm_cursor)
|
||||
- [Crossterm Terminal](https://crates.io/crates/crossterm_terminal)
|
||||
|
||||
This crate is not meant for standalone use and is really a library with some common used code for crossterm and the above named modules.
|
||||
|
||||
## Contributing
|
||||
|
||||
I highly appreciate it when you are contributing to this crate.
|
||||
Also Since my native language is not English my grammar and sentence order will not be perfect.
|
||||
So improving this by correcting these mistakes will help both me and the reader of the docs.
|
||||
|
||||
## Authors
|
||||
|
||||
* **Timon Post** - *Project Owner & creator*
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/TimonPost/crossterm/blob/master/LICENSE) file for details
|
@ -1,12 +1,12 @@
|
||||
//! This module contains some commands that could be executed for a specific task. A `Command` is just a little wrapper.
|
||||
|
||||
use super::super::output::TerminalOutput;
|
||||
use crate::output::TerminalOutput;
|
||||
use std::io;
|
||||
|
||||
pub mod shared_commands;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub mod unix_command;
|
||||
//pub mod shared_commands;
|
||||
//
|
||||
//#[cfg(not(target_os = "windows"))]
|
||||
//pub mod unix_command;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod win_commands;
|
||||
@ -17,11 +17,6 @@ pub trait IStateCommand {
|
||||
fn undo(&mut self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
pub trait IEnableAnsiCommand {
|
||||
fn enable(&self) -> io::Result<bool>;
|
||||
fn disable(&self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
// This trait provides an interface for switching to alternate screen and back.
|
||||
pub trait IAlternateScreenCommand: Sync + Send {
|
||||
fn enable(&self, stdout: &mut TerminalOutput) -> io::Result<()>;
|
@ -1,63 +1,13 @@
|
||||
//! A module which contains the commands that can be used for windows systems.
|
||||
|
||||
use super::{IAlternateScreenCommand, IEnableAnsiCommand, TerminalOutput};
|
||||
use super::{IAlternateScreenCommand, TerminalOutput};
|
||||
|
||||
use kernel::windows_kernel::{ansi_support, ConsoleMode, Handle, ScreenBuffer};
|
||||
use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer};
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::wincon;
|
||||
use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
use std::io::Result;
|
||||
|
||||
/// This command is used for enabling and disabling ANSI code support for windows systems,
|
||||
/// For more info check: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct EnableAnsiCommand {
|
||||
mask: DWORD,
|
||||
}
|
||||
|
||||
impl EnableAnsiCommand {
|
||||
pub fn new() -> EnableAnsiCommand {
|
||||
let command = EnableAnsiCommand {
|
||||
mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
};
|
||||
command
|
||||
}
|
||||
}
|
||||
|
||||
impl IEnableAnsiCommand for EnableAnsiCommand {
|
||||
fn enable(&self) -> Result<bool> {
|
||||
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded.
|
||||
if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled() {
|
||||
return Ok(ansi_support::windows_supportable());
|
||||
} else {
|
||||
let console_mode = ConsoleMode::new()?;
|
||||
|
||||
let mut dw_mode = console_mode.mode()?;
|
||||
|
||||
dw_mode |= self.mask;
|
||||
|
||||
console_mode.set_mode(dw_mode)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn disable(&self) -> Result<()> {
|
||||
if ansi_support::ansi_enabled() {
|
||||
let console_mode = ConsoleMode::new()?;
|
||||
|
||||
let mut dw_mode = console_mode.mode()?;
|
||||
|
||||
dw_mode &= !self.mask;
|
||||
|
||||
console_mode.set_mode(dw_mode)?;
|
||||
|
||||
ansi_support::set_ansi_enabled(false);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// This command is used for enabling and disabling raw mode for windows systems.
|
||||
/// For more info check: https://docs.microsoft.com/en-us/windows/console/high-level-console-modes.
|
||||
#[derive(Clone, Copy)]
|
@ -1,87 +1,35 @@
|
||||
//! Some actions need to preformed platform independently since they can not be solved `ANSI escape codes`.
|
||||
|
||||
use super::TerminalOutput;
|
||||
use crate::output::TerminalOutput;
|
||||
use std::io::{self, Write};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(windows)]
|
||||
use kernel::windows_kernel::ansi_support::{try_enable_ansi_support, windows_supportable};
|
||||
|
||||
#[cfg(windows)]
|
||||
use kernel::windows_kernel::exit;
|
||||
#[cfg(windows)]
|
||||
use kernel::windows_kernel::ScreenBuffer;
|
||||
|
||||
#[cfg(windows)]
|
||||
use kernel::windows_kernel::Cursor;
|
||||
|
||||
#[cfg(unix)]
|
||||
use kernel::unix_kernel::terminal::{exit, pos, terminal_size};
|
||||
|
||||
/// Get the terminal size based on the current platform.
|
||||
#[cfg(unix)]
|
||||
pub fn get_terminal_size() -> (u16, u16) {
|
||||
terminal_size()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn get_terminal_size() -> (u16, u16) {
|
||||
if let Ok(buffer) = ScreenBuffer::current() {
|
||||
let size = buffer.info().unwrap().terminal_size();
|
||||
(size.width as u16, size.height as u16)
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the cursor position based on the current platform.
|
||||
#[cfg(unix)]
|
||||
pub fn get_cursor_position() -> (u16, u16) {
|
||||
if let Ok(pos) = pos() {
|
||||
pos
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn get_cursor_position() -> (u16, u16) {
|
||||
if let Ok(cursor) = Cursor::new() {
|
||||
cursor.position().unwrap().into()
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/// exit the current terminal.
|
||||
pub fn exit_terminal() {
|
||||
exit();
|
||||
}
|
||||
use crate::sys::winapi::ansi::set_virtual_terminal_processing;
|
||||
|
||||
#[cfg(windows)]
|
||||
/// Get an module specific implementation of a the generic given type based on the current platform.
|
||||
/// If the current platform is windows and it supports ansi escape codes it will return the ansi implementation and if not it will return the winapi implementation.
|
||||
/// If the current platform is unix it will return the ansi implementation.
|
||||
pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T> {
|
||||
let mut term: Option<T> = None;
|
||||
let mut does_support = true;
|
||||
pub fn get_module<T>(winapi_impl: T, ansi_impl: T) -> Option<T> {
|
||||
// Some terminals on windows like GitBash can't use WinaApi calls directly so when we try to enable the ANSI-flag for windows this won't work.
|
||||
// Because of that we should check first if the TERM-variable is set and see if the current terminal is a terminal who does support ANSI.
|
||||
let supports_ansi = is_specific_term();
|
||||
|
||||
if !windows_supportable() {
|
||||
// Try to enable ansi on windows if not than use WINAPI.
|
||||
does_support = try_enable_ansi_support();
|
||||
//
|
||||
// uncomment this line when you want to use the winapi implementation.
|
||||
// does_support = false;
|
||||
if !does_support {
|
||||
term = Some(winapi_impl);
|
||||
match supports_ansi {
|
||||
true => {
|
||||
return Some(ansi_impl);
|
||||
}
|
||||
false => {
|
||||
// if it is not listed we should try with WinApi to check if we do support ANSI-codes.
|
||||
match set_virtual_terminal_processing(true) {
|
||||
Ok(_) => {
|
||||
return Some(ansi_impl);
|
||||
}
|
||||
Err(_) => {
|
||||
return Some(winapi_impl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if does_support {
|
||||
term = Some(unix_impl);
|
||||
}
|
||||
|
||||
term
|
||||
}
|
||||
|
||||
/// This function is used by 'ANSI' modules. Those modules are using an `Option` of `TerminalOutput`.
|
||||
@ -120,3 +68,27 @@ pub fn write_str(stdout: &Option<&Arc<TerminalOutput>>, string: &str) -> io::Res
|
||||
Some(output) => output.write_str(string),
|
||||
}
|
||||
}
|
||||
|
||||
// checks if the 'TERM' environment variable is set to check if the terminal supports ANSI-codes.
|
||||
// I got the list of terminals from here: https://github.com/keqingrong/supports-ansi/blob/master/index.js
|
||||
fn is_specific_term() -> bool {
|
||||
const TERMS: [&'static str; 15] = [
|
||||
"xterm", // xterm, PuTTY, Mintty
|
||||
"rxvt", // RXVT
|
||||
"eterm", // Eterm
|
||||
"screen", // GNU screen, tmux
|
||||
"tmux", // tmux
|
||||
"vt100", "vt102", "vt220", "vt320", // DEC VT series
|
||||
"ansi", // ANSI
|
||||
"scoansi", // SCO ANSI
|
||||
"cygwin", // Cygwin, MinGW
|
||||
"linux", // Linux console
|
||||
"konsole", // Konsole
|
||||
"bvterm", // Bitvise SSH Client
|
||||
];
|
||||
|
||||
match std::env::var("TERM") {
|
||||
Ok(val) => val != "dumb" || TERMS.contains(&val.as_str()),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
22
crossterm_utils/src/lib.rs
Normal file
22
crossterm_utils/src/lib.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#[cfg(windows)]
|
||||
extern crate crossterm_winapi;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
extern crate termios;
|
||||
|
||||
pub mod commands;
|
||||
pub mod error;
|
||||
pub mod macros;
|
||||
pub mod sys;
|
||||
|
||||
mod functions;
|
||||
mod output;
|
||||
|
||||
pub use self::error::{ErrorKind, Result};
|
||||
pub use self::output::TerminalOutput;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use self::functions::get_module;
|
||||
pub use self::functions::{write, write_str};
|
4
crossterm_utils/src/macros.rs
Normal file
4
crossterm_utils/src/macros.rs
Normal file
@ -0,0 +1,4 @@
|
||||
#[macro_export]
|
||||
macro_rules! csi {
|
||||
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
//! A module that provides a uniformed way to write to the output no matter if it is in main, alternate or raw mode.
|
||||
|
||||
mod output;
|
||||
|
||||
#[cfg(test)]
|
||||
@ -15,7 +14,6 @@ use self::winapi_output::WinApiOutput;
|
||||
|
||||
pub use self::output::TerminalOutput;
|
||||
|
||||
use super::functions;
|
||||
use std::io;
|
||||
|
||||
/// This trait defines represents an stdout of an screen.
|
@ -1,14 +1,9 @@
|
||||
//! This module provides one place to work with the screen.
|
||||
//!
|
||||
//! In Rust we can call `stdout()` to get a handle to the current default console handle.
|
||||
//! When working with UNIX or Windows10 systems you could print some text to the screen by doing the following:
|
||||
//!
|
||||
//! ```
|
||||
//! write!(std::io::stdout(), "{}", "some text").
|
||||
//! ```
|
||||
//!
|
||||
//! But things change when we are in alternate screen modes.
|
||||
//! We can not simply use `stdout()` to get a handle to the 'alternate screen', since this call returns the current default console handle (main screen).
|
||||
//! However, we can't use `stdout()` to access the alternate screen handle therefore we also won't be able to use `print!(), println!(), or write!()`.
|
||||
//! The same goes for coloring, cursor movement, input, and terminal actions.
|
||||
//! All of those functions are writing to the standard output and not to our alternate screen we are currently on.
|
||||
//!
|
||||
//! To get the handle to the `alternate screen` we first need to store this handle so that we are able to call it later on.
|
||||
//! Through this stored handle, crossterm can write to or execute commands at the current screen whether it be an alternate screen or main screen.
|
||||
@ -16,16 +11,16 @@
|
||||
//! For UNIX and Windows10 systems, we store the handle gotten from `stdout()`. For Windows systems who are not supporting ANSI escape codes, we can call `CONOUT$` to get the current screen `HANDLE`.
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::functions;
|
||||
use std::default::Default;
|
||||
use std::io::Write;
|
||||
|
||||
/// Struct that is a handle to a terminal screen.
|
||||
/// This handle could be used to write to the current screen
|
||||
/// Struct that is a handle to the current terminal screen.
|
||||
///
|
||||
/// For UNIX and Windows 10 `stdout()` will be used as handle. And for Windows systems, not supporting ANSI escape codes, will use WinApi's `HANDLE` as handle.
|
||||
pub struct TerminalOutput {
|
||||
stdout: Box<IStdout + Send + Sync>,
|
||||
/// checks if this output is in raw mode.
|
||||
pub is_in_raw_mode: bool,
|
||||
}
|
||||
|
1
crossterm_utils/src/output/sys/mod.rs
Normal file
1
crossterm_utils/src/output/sys/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod winapi;
|
13
crossterm_utils/src/output/sys/winapi.rs
Normal file
13
crossterm_utils/src/output/sys/winapi.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! This module contains the logic to write to the terminal.
|
||||
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::shared::ntdef::NULL;
|
||||
use winapi::um::consoleapi::WriteConsoleW;
|
||||
use winapi::um::wincon::{WriteConsoleOutputA, CHAR_INFO, COORD, PSMALL_RECT};
|
||||
use winapi::um::winnt::HANDLE;
|
||||
|
||||
use crossterm_winapi::{is_true, ScreenBuffer};
|
||||
|
||||
use std::io::{self, Result};
|
||||
use std::str;
|
||||
|
@ -1,17 +1,12 @@
|
||||
use modules::output::ansi_output::AnsiOutput;
|
||||
|
||||
use modules::output::IStdout;
|
||||
|
||||
use Screen;
|
||||
use super::{AnsiOutput, IStdout, WinApiOutput};
|
||||
|
||||
#[cfg(windows)]
|
||||
mod winapi_tests {
|
||||
use super::*;
|
||||
use modules::output::winapi_output::WinApiOutput;
|
||||
|
||||
/* ======================== WinApi =========================== */
|
||||
#[test]
|
||||
fn write_winapi() {
|
||||
let _screen = Screen::default();
|
||||
let output = WinApiOutput::new();
|
||||
|
||||
let bytes = "test".as_bytes();
|
||||
@ -21,7 +16,6 @@ mod winapi_tests {
|
||||
|
||||
#[test]
|
||||
fn write_str_winapi() {
|
||||
let _screen = Screen::default();
|
||||
let output = WinApiOutput::new();
|
||||
|
||||
let bytes = "test".as_bytes();
|
||||
@ -33,7 +27,6 @@ mod winapi_tests {
|
||||
/* ======================== ANSI =========================== */
|
||||
#[test]
|
||||
fn write_ansi() {
|
||||
let _screen = Screen::default();
|
||||
let output = AnsiOutput::new();
|
||||
|
||||
let bytes = "test".as_bytes();
|
||||
@ -43,7 +36,6 @@ fn write_ansi() {
|
||||
|
||||
#[test]
|
||||
fn write_str_ansi() {
|
||||
let _screen = Screen::default();
|
||||
let output = AnsiOutput::new();
|
||||
|
||||
let bytes = "test".as_bytes();
|
||||
@ -68,10 +60,12 @@ fn try_enable_ansi() -> bool {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if cfg!(target_os = "windows") {
|
||||
use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
|
||||
use crate::sys::winapi::ansi::set_virtual_terminal_processing;
|
||||
|
||||
if !try_enable_ansi_support() {
|
||||
return false;
|
||||
// if it is not listed we should try with WinApi to check if we do support ANSI-codes.
|
||||
match set_virtual_terminal_processing(true) {
|
||||
Ok(_) => return true,
|
||||
Err(e) => return false,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use super::IStdout;
|
||||
use kernel::windows_kernel::{writing, Handle};
|
||||
use crossterm_winapi::{Console, Handle};
|
||||
|
||||
use std::io;
|
||||
|
||||
@ -19,7 +19,8 @@ impl IStdout for WinApiOutput {
|
||||
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
let handle = Handle::current_out_handle()?;
|
||||
writing::write_char_buffer(&handle, buf)
|
||||
let console = Console::from(handle);
|
||||
console.write_char_buffer(buf)
|
||||
}
|
||||
|
||||
fn flush(&self) -> io::Result<()> {
|
5
crossterm_utils/src/sys/mod.rs
Normal file
5
crossterm_utils/src/sys/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#[cfg(windows)]
|
||||
pub mod winapi;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod unix;
|
65
crossterm_utils/src/sys/unix.rs
Normal file
65
crossterm_utils/src/sys/unix.rs
Normal file
@ -0,0 +1,65 @@
|
||||
//! This module contains all `unix` specific terminal related logic.
|
||||
|
||||
use libc::{self, TCSADRAIN};
|
||||
|
||||
use crate::termios::{tcsetattr, Termios};
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
static mut ORIGINAL_TERMINAL_MODE: Option<Termios> = None;
|
||||
pub static mut RAW_MODE_ENABLED_BY_SYSTEM: bool = false;
|
||||
pub static mut RAW_MODE_ENABLED_BY_USER: bool = false;
|
||||
|
||||
/// Transform the given mode into an raw mode (non-canonical) mode.
|
||||
pub fn make_raw(termios: &mut Termios) {
|
||||
extern "C" {
|
||||
pub fn cfmakeraw(termptr: *mut Termios);
|
||||
}
|
||||
unsafe { cfmakeraw(termios) }
|
||||
}
|
||||
|
||||
pub fn into_raw_mode() -> io::Result<RawFd> {
|
||||
let tty_f;
|
||||
|
||||
let fd = unsafe {
|
||||
if libc::isatty(libc::STDIN_FILENO) == 1 {
|
||||
libc::STDIN_FILENO
|
||||
} else {
|
||||
tty_f = fs::File::open("/dev/tty")?;
|
||||
tty_f.as_raw_fd()
|
||||
}
|
||||
};
|
||||
|
||||
let mut termios = Termios::from_fd(fd)?;
|
||||
let original = termios.clone();
|
||||
|
||||
unsafe {
|
||||
if ORIGINAL_TERMINAL_MODE.is_none() {
|
||||
ORIGINAL_TERMINAL_MODE = Some(original.clone())
|
||||
}
|
||||
}
|
||||
|
||||
make_raw(&mut termios);
|
||||
tcsetattr(fd, TCSADRAIN, &termios)?;
|
||||
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
pub fn disable_raw_mode() -> io::Result<()> {
|
||||
let tty_f;
|
||||
|
||||
let fd = unsafe {
|
||||
if libc::isatty(libc::STDIN_FILENO) == 1 {
|
||||
libc::STDIN_FILENO
|
||||
} else {
|
||||
tty_f = fs::File::open("/dev/tty")?;
|
||||
tty_f.as_raw_fd()
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(original) = unsafe { ORIGINAL_TERMINAL_MODE } {
|
||||
tcsetattr(fd, TCSADRAIN, &original)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
33
crossterm_utils/src/sys/winapi.rs
Normal file
33
crossterm_utils/src/sys/winapi.rs
Normal file
@ -0,0 +1,33 @@
|
||||
pub mod ansi {
|
||||
use crossterm_winapi::ConsoleMode;
|
||||
use std::io;
|
||||
use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
/// Toggle virtual terminal processing.
|
||||
///
|
||||
/// This method attempts to toggle virtual terminal processing for this
|
||||
/// console. If there was a problem toggling it, then an error returned.
|
||||
/// On success, the caller may assume that toggling it was successful.
|
||||
///
|
||||
/// When virtual terminal processing is enabled, characters emitted to the
|
||||
/// console are parsed for VT100 and similar control character sequences
|
||||
/// that control color and other similar operations.
|
||||
pub fn set_virtual_terminal_processing(yes: bool) -> io::Result<()> {
|
||||
let mask = ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
let console_mode = ConsoleMode::new()?;
|
||||
let old_mode = console_mode.mode()?;
|
||||
|
||||
let new_mode = if yes {
|
||||
old_mode | mask
|
||||
} else {
|
||||
old_mode & !mask
|
||||
};
|
||||
if old_mode == new_mode {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
console_mode.set_mode(new_mode)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
4
crossterm_winapi/.gitignore
vendored
Normal file
4
crossterm_winapi/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
target/
|
||||
.idea/
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
19
crossterm_winapi/.travis.yml
Normal file
19
crossterm_winapi/.travis.yml
Normal file
@ -0,0 +1,19 @@
|
||||
language: rust
|
||||
rust:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
before_script:
|
||||
- export PATH=$PATH:/home/travis/.cargo/bin
|
||||
- rustup component add rustfmt-preview
|
||||
|
||||
os:
|
||||
- windows
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
script:
|
||||
- cargo build
|
||||
- cargo fmt -- --check
|
14
crossterm_winapi/Cargo.toml
Normal file
14
crossterm_winapi/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.1.0"
|
||||
authors = ["T. Post"]
|
||||
description = "An WinApi wrapper that provides some basic simple abstractions aground common WinApi calls"
|
||||
repository = "https://github.com/TimonPost/crossterm_winapi"
|
||||
documentation = "https://docs.rs/crossterm_winapi/"
|
||||
license = "MIT"
|
||||
keywords = ["winapi", "abstractions", "crossterm", "windows", "screen_buffer"]
|
||||
exclude = ["target", "Cargo.lock"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
winapi = { version = "0.3.5", features = ["winbase","consoleapi","processenv", "handleapi"] }
|
67
crossterm_winapi/README.md
Normal file
67
crossterm_winapi/README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# Crossterm Winapi | Common WinApi Abstractions
|
||||
![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6]
|
||||
|
||||
[s1]: https://img.shields.io/crates/v/crossterm_winapi.svg
|
||||
[l1]: https://crates.io/crates/crossterm_winapi
|
||||
|
||||
[s2]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[l2]: LICENSE
|
||||
|
||||
[s3]: https://docs.rs/crossterm_winapi/badge.svg
|
||||
[l3]: https://docs.rs/crossterm_winapi/
|
||||
|
||||
[s6]: https://tokei.rs/b1/github/TimonPost/crossterm_winapi?category=code
|
||||
[s7]: https://travis-ci.org/TimonPost/crossterm_winapi.svg?branch=master
|
||||
|
||||
This crate provides some wrappers aground common used WinApi functions.
|
||||
The purpose of this library is originally meant for [crossterm](https://github.com/TimonPost/crossterm),
|
||||
and it is very unstable right because of that some changes could be expected.
|
||||
|
||||
# Features
|
||||
This crate provides some abstractions over:
|
||||
|
||||
- CONSOLE_SCREEN_BUFFER_INFO (used to extract information like cursor pos, terminal size etc.)
|
||||
- HANDLE (the handle needed to run functions from WinApi)
|
||||
- SetConsoleActiveScreenBuffer (activate an other screen buffer)
|
||||
- Set/GetConsoleMode (e.g. console modes like disabling output)
|
||||
- SetConsoleTextAttribute (eg. coloring)
|
||||
- SetConsoleWindowInfo (changing the buffer location e.g. scrolling)
|
||||
- FillConsoleOutputAttribute, FillConsoleOutputCharacter (used to replace some block of cells with a color or character.)
|
||||
- SetConsoleInfo
|
||||
|
||||
# Example
|
||||
Here are some examples do demonstrate how to work whit this crate.
|
||||
Please see [examples](https://github.com/TimonPost/crossterm_winapi) for more
|
||||
## Screenbuffer information
|
||||
```rust
|
||||
use crossterm_winapi::{ScreenBuffer, Handle};
|
||||
|
||||
fn print_screen_buffer_information() {
|
||||
let screen_buffer = ScreenBuffer::current().unwrap();
|
||||
|
||||
// get console screen buffer information
|
||||
let csbi = screen_buffer.info().unwrap();
|
||||
|
||||
println!("cursor post: {:?}", csbi.cursor_pos());
|
||||
println!("attributes: {:?}", csbi.attributes());
|
||||
println!("terminal window dimentions {:?}", csbi.terminal_window());
|
||||
println!("terminal size {:?}", csbi.terminal_size());
|
||||
}
|
||||
```
|
||||
## Handle
|
||||
```rust
|
||||
use crossterm_winapi::{HandleType, Handle};
|
||||
|
||||
fn get_different_handle_types() {
|
||||
let out_put_handle = Handle::new(HandleType::OutputHandle).unwrap();
|
||||
let out_put_handle = Handle::new(HandleType::InputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Inspiration
|
||||
I wanted to expose some of the api crossterm uses for WinApi.
|
||||
1. I thought it would be helpful for other people to, to have a small rust seemable abstraction over the WinApi bindings.
|
||||
2. I have some future plans for crossterm wherefore I needed to seperate the WinAPi logic out of the currenbt librarie.
|
51
crossterm_winapi/examples/coloring_example.rs
Normal file
51
crossterm_winapi/examples/coloring_example.rs
Normal file
@ -0,0 +1,51 @@
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
use crossterm_winapi::{Console, ScreenBuffer};
|
||||
|
||||
fn set_background_color() -> std::io::Result<()> {
|
||||
// background value
|
||||
const BLUE_BACKGROUND: u16 = 0x0010;
|
||||
|
||||
let screen_buffer = ScreenBuffer::current()?;
|
||||
let csbi = screen_buffer.info()?;
|
||||
|
||||
// Notice that the color values are stored in wAttribute.
|
||||
// So wee need to use bitwise operators to check if the values exists or to get current console colors.
|
||||
let attrs = csbi.attributes();
|
||||
let fg_color = attrs & 0x0007;
|
||||
|
||||
// apply the blue background flag to the current attributes
|
||||
let mut new_color = fg_color | BLUE_BACKGROUND;
|
||||
|
||||
// set the console text attribute to the new color value.
|
||||
Console::from(**screen_buffer.get_handle()).set_text_attribute(new_color)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_foreground_color() -> std::io::Result<()> {
|
||||
// background value
|
||||
const BLUE_FOREGROUND: u16 = 0x0001;
|
||||
|
||||
let screen_buffer = ScreenBuffer::current()?;
|
||||
let csbi = screen_buffer.info()?;
|
||||
|
||||
// Notice that the color values are stored in wAttribute.
|
||||
// So we need to use bitwise operators to check if the values exists or to get current console colors.
|
||||
let attrs = csbi.attributes();
|
||||
let bg_color = attrs & 0x0070;
|
||||
let mut color = BLUE_FOREGROUND | bg_color;
|
||||
|
||||
// background intensity is a separate value in attrs,
|
||||
// wee need to check if this was applied to the current bg color.
|
||||
if (attrs & 0x0080 as u16) != 0 {
|
||||
color = color | 0x0080 as u16;
|
||||
}
|
||||
|
||||
// set the console text attribute to the new color value.
|
||||
Console::from(**screen_buffer.get_handle()).set_text_attribute(color)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {}
|
15
crossterm_winapi/examples/console.rs
Normal file
15
crossterm_winapi/examples/console.rs
Normal file
@ -0,0 +1,15 @@
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
use crossterm_winapi::ConsoleMode;
|
||||
|
||||
pub fn change_console_mode() {
|
||||
let console_mode = ConsoleMode::new().unwrap();
|
||||
|
||||
// get the current console mode:
|
||||
let mode: u32 = console_mode.mode().unwrap();
|
||||
|
||||
// set the console mode (not sure if this is an actual value xp)
|
||||
console_mode.set_mode(10);
|
||||
}
|
||||
|
||||
fn main() {}
|
19
crossterm_winapi/examples/handle.rs
Normal file
19
crossterm_winapi/examples/handle.rs
Normal file
@ -0,0 +1,19 @@
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
use crossterm_winapi::{Handle, HandleType};
|
||||
|
||||
fn main() {
|
||||
/// see the description of the types to see what they do.
|
||||
let out_put_handle = Handle::new(HandleType::OutputHandle).unwrap();
|
||||
let out_put_handle = Handle::new(HandleType::InputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle).unwrap();
|
||||
|
||||
// now you have this handle you might want to get the WinApi `HANDLE` it is wrapping.
|
||||
// you can do this by defencing.
|
||||
|
||||
let handle /*:HANDLE*/ = *out_put_handle;
|
||||
|
||||
// you can also pass you own `HANDLE` to create an instance of `Handle`
|
||||
let handle = Handle::from(handle); /* winapi::um::winnt::HANDLE */
|
||||
}
|
25
crossterm_winapi/examples/screen_buffer.rs
Normal file
25
crossterm_winapi/examples/screen_buffer.rs
Normal file
@ -0,0 +1,25 @@
|
||||
extern crate crossterm_winapi;
|
||||
|
||||
use crossterm_winapi::{Handle, ScreenBuffer};
|
||||
|
||||
fn main() {}
|
||||
|
||||
fn print_screen_buffer_information() {
|
||||
let screen_buffer = ScreenBuffer::current().unwrap();
|
||||
|
||||
// get console screen buffer information
|
||||
let csbi = screen_buffer.info().unwrap();
|
||||
|
||||
println!("cursor post: {:?}", csbi.cursor_pos());
|
||||
println!("attributes: {:?}", csbi.attributes());
|
||||
println!("terminal window dimentions {:?}", csbi.terminal_window());
|
||||
println!("terminal size {:?}", csbi.terminal_size());
|
||||
}
|
||||
|
||||
fn multiple_screen_buffers() {
|
||||
// create new screen buffer
|
||||
let screen_buffer = ScreenBuffer::create();
|
||||
|
||||
// which to this screen buffer
|
||||
screen_buffer.show();
|
||||
}
|
178
crossterm_winapi/src/console.rs
Normal file
178
crossterm_winapi/src/console.rs
Normal file
@ -0,0 +1,178 @@
|
||||
use super::{is_true, Coord, Handle, HandleType, WindowPositions};
|
||||
use std::io::{self, Error, Result};
|
||||
use std::str;
|
||||
|
||||
use winapi::ctypes::c_void;
|
||||
use winapi::shared::ntdef::NULL;
|
||||
use winapi::um::{
|
||||
consoleapi::WriteConsoleW,
|
||||
wincon::{
|
||||
FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize,
|
||||
SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, SMALL_RECT,
|
||||
},
|
||||
winnt::HANDLE,
|
||||
};
|
||||
|
||||
/// Could be used to do some basic things with the console.
|
||||
pub struct Console {
|
||||
handle: Handle,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
/// Create new instance of `Console`.
|
||||
///
|
||||
/// This created instance will use the default output handle (STD_OUTPUT_HANDLE) as handle for the function call it wraps.
|
||||
pub fn new() -> Result<Console> {
|
||||
Ok(Console {
|
||||
handle: Handle::new(HandleType::OutputHandle)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the attributes of characters written to the console screen buffer by the WriteFile or WriteConsole function, or echoed by the ReadFile or ReadConsole function.
|
||||
/// This function affects text written after the function call.
|
||||
///
|
||||
/// parameter: [wAttributes]
|
||||
/// Wraps the underlying function call: [SetConsoleTextAttribute]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute]
|
||||
pub fn set_text_attribute(&self, value: u16) -> Result<()> {
|
||||
unsafe {
|
||||
if !is_true(SetConsoleTextAttribute(*self.handle, value)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the current size and position of a console screen buffer's window.
|
||||
///
|
||||
/// Wraps the underlying function call: [SetConsoleTextAttribute]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute]
|
||||
pub fn set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()> {
|
||||
let absolute = match absolute {
|
||||
true => 1,
|
||||
false => 0,
|
||||
};
|
||||
let a = SMALL_RECT::from(rect);
|
||||
|
||||
unsafe {
|
||||
if !is_true(SetConsoleWindowInfo(*self.handle, absolute, &a)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates
|
||||
///
|
||||
/// Wraps the underlying function call: [FillConsoleOutputCharacterA]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter]
|
||||
pub fn fill_whit_character(
|
||||
&self,
|
||||
start_location: Coord,
|
||||
cells_to_write: u32,
|
||||
filling_char: char,
|
||||
) -> Result<u32> {
|
||||
let mut chars_written = 0;
|
||||
unsafe {
|
||||
// fill the cells in console with blanks
|
||||
if !is_true(FillConsoleOutputCharacterA(
|
||||
*self.handle,
|
||||
filling_char as i8,
|
||||
cells_to_write,
|
||||
COORD::from(start_location),
|
||||
&mut chars_written,
|
||||
)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(chars_written)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the character attributes for a specified number of character cells, beginning at the specified coordinates in a screen buffer.
|
||||
///
|
||||
/// Wraps the underlying function call: [FillConsoleOutputAttribute]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputattribute]
|
||||
pub fn fill_whit_attribute(
|
||||
&self,
|
||||
start_location: Coord,
|
||||
cells_to_write: u32,
|
||||
dw_attribute: u16,
|
||||
) -> Result<u32> {
|
||||
let mut cells_written = 0;
|
||||
// Get the position of the current console window
|
||||
unsafe {
|
||||
if !is_true(FillConsoleOutputAttribute(
|
||||
*self.handle,
|
||||
dw_attribute,
|
||||
cells_to_write,
|
||||
COORD::from(start_location),
|
||||
&mut cells_written,
|
||||
)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cells_written)
|
||||
}
|
||||
|
||||
/// Retrieves the size of the largest possible console window, based on the current font and the size of the display.
|
||||
///
|
||||
/// Wraps the underlying function call: [GetLargestConsoleWindowSize]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getlargestconsolewindowsize]
|
||||
pub fn largest_window_size(&self) -> Coord {
|
||||
Coord::from(unsafe { GetLargestConsoleWindowSize(*self.handle) })
|
||||
}
|
||||
|
||||
/// Writes a character string to a console screen buffer beginning at the current cursor location.
|
||||
///
|
||||
/// Wraps the underlying function call: [WriteConsoleW]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/writeconsole]
|
||||
pub fn write_char_buffer(&self, buf: &[u8]) -> Result<usize> {
|
||||
// get string from u8[] and parse it to an c_str
|
||||
let utf8 = match str::from_utf8(buf) {
|
||||
Ok(string) => string,
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Could not parse to utf8 string",
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let utf16: Vec<u16> = utf8.encode_utf16().collect();
|
||||
let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void;
|
||||
|
||||
let mut cells_written: u32 = 0;
|
||||
// write to console
|
||||
unsafe {
|
||||
if !is_true(WriteConsoleW(
|
||||
*self.handle,
|
||||
utf16_ptr,
|
||||
utf16.len() as u32,
|
||||
&mut cells_written,
|
||||
NULL,
|
||||
)) {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(utf8.as_bytes().len())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Handle> for Console {
|
||||
/// Create a `Console` instance who's functions will be executed on the the given `Handle`
|
||||
fn from(handle: Handle) -> Self {
|
||||
Console { handle }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HANDLE> for Console {
|
||||
/// Create a `Console` instance who's functions will be executed on the the given `HANDLE`
|
||||
fn from(handle: HANDLE) -> Self {
|
||||
Console {
|
||||
handle: Handle::from(handle),
|
||||
}
|
||||
}
|
||||
}
|
93
crossterm_winapi/src/console_mode.rs
Normal file
93
crossterm_winapi/src/console_mode.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use super::{is_true, Handle, HandleType};
|
||||
use std::io::{Error, Result};
|
||||
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
|
||||
use winapi::um::winnt::HANDLE;
|
||||
|
||||
/// This abstracts away some WinaApi calls to set and get the console mode.
|
||||
///
|
||||
/// Wraps the underlying function call: [SetConsoleMode]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsolemode]
|
||||
///
|
||||
/// Wraps the underlying function call: [GetConsoleMode]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getconsolemode]
|
||||
pub struct ConsoleMode {
|
||||
// the handle used for the functions of this type.
|
||||
handle: Handle,
|
||||
}
|
||||
|
||||
impl ConsoleMode {
|
||||
/// Create a new `ConsoleMode` instance.
|
||||
///
|
||||
/// This will use the `STD_OUTPUT_HANDLE` as default handle.
|
||||
/// When you explicitly want to specify the handle used for the function calls use `ConsoleMode::from(handle)` instead.
|
||||
pub fn new() -> Result<ConsoleMode> {
|
||||
Ok(ConsoleMode {
|
||||
handle: Handle::new(HandleType::OutputHandle)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the console mode to the given console mode.
|
||||
///
|
||||
/// This function sets the `dwMode`.
|
||||
///
|
||||
/// Wraps the underlying function call: [SetConsoleMode]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsolemode]
|
||||
pub fn set_mode(&self, console_mode: u32) -> Result<()> {
|
||||
unsafe {
|
||||
if !is_true(SetConsoleMode(*self.handle, console_mode)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the console mode.
|
||||
///
|
||||
/// This function returns the `lpMode`.
|
||||
///
|
||||
/// Wraps the underlying function call: [GetConsoleMode]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getconsolemode]
|
||||
pub fn mode(&self) -> Result<u32> {
|
||||
let mut console_mode = 0;
|
||||
unsafe {
|
||||
if !is_true(GetConsoleMode(*self.handle, &mut console_mode)) {
|
||||
println!("Getting mode failed");
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(console_mode)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HANDLE> for ConsoleMode {
|
||||
fn from(handle: HANDLE) -> Self {
|
||||
ConsoleMode {
|
||||
handle: Handle::from(handle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Handle> for ConsoleMode {
|
||||
fn from(handle: Handle) -> Self {
|
||||
ConsoleMode { handle }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ConsoleMode;
|
||||
|
||||
#[test]
|
||||
fn set_get_mode() {
|
||||
let mode = ConsoleMode::new().unwrap();
|
||||
|
||||
let original_mode = mode.mode().unwrap();
|
||||
|
||||
mode.set_mode(0x0004);
|
||||
let console_mode = mode.mode().unwrap();
|
||||
|
||||
assert!((console_mode & 0x0004) != 0);
|
||||
|
||||
mode.set_mode(original_mode);
|
||||
}
|
||||
}
|
61
crossterm_winapi/src/csbi.rs
Normal file
61
crossterm_winapi/src/csbi.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use super::{Coord, Size, WindowPositions};
|
||||
|
||||
use winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO;
|
||||
|
||||
use std::mem::zeroed;
|
||||
|
||||
/// This type is a wrapper for `CONSOLE_SCREEN_BUFFER_INFO` and has some methods to extract information from it.
|
||||
///
|
||||
/// Wraps the underlying type: [CONSOLE_SCREEN_BUFFER_INFO]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str]
|
||||
pub struct ScreenBufferInfo(pub CONSOLE_SCREEN_BUFFER_INFO);
|
||||
|
||||
impl ScreenBufferInfo {
|
||||
pub fn new() -> ScreenBufferInfo {
|
||||
ScreenBufferInfo(unsafe { zeroed() })
|
||||
}
|
||||
|
||||
/// This will return the buffer size.
|
||||
///
|
||||
/// Will take `dwSize`from the current screen buffer and convert it into the `Size`.
|
||||
pub fn buffer_size(&self) -> Size {
|
||||
Size::from(self.0.dwSize)
|
||||
}
|
||||
|
||||
/// This will return the terminal size.
|
||||
///
|
||||
/// Will calculate the whit and height from `srWindow` and convert it into a `Size`.
|
||||
pub fn terminal_size(&self) -> Size {
|
||||
(Size::new(
|
||||
self.0.srWindow.Right - self.0.srWindow.Left,
|
||||
self.0.srWindow.Bottom - self.0.srWindow.Top,
|
||||
))
|
||||
}
|
||||
|
||||
/// This will return the terminal window properties.
|
||||
///
|
||||
/// Will take `srWindow` and convert it into the `WindowPositions` type.
|
||||
pub fn terminal_window(&self) -> WindowPositions {
|
||||
WindowPositions::from(self.0)
|
||||
}
|
||||
|
||||
/// This will return the terminal window properties.
|
||||
///
|
||||
/// Will take `wAttributes` from the current screen buffer.
|
||||
pub fn attributes(&self) -> u16 {
|
||||
self.0.wAttributes
|
||||
}
|
||||
|
||||
/// This will return the current cursor position.
|
||||
///
|
||||
/// Will take `dwCursorPosition` from the current screen buffer.
|
||||
pub fn cursor_pos(&self) -> Coord {
|
||||
Coord::from(self.0.dwCursorPosition)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CONSOLE_SCREEN_BUFFER_INFO> for ScreenBufferInfo {
|
||||
fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self {
|
||||
ScreenBufferInfo(csbi)
|
||||
}
|
||||
}
|
188
crossterm_winapi/src/handle.rs
Normal file
188
crossterm_winapi/src/handle.rs
Normal file
@ -0,0 +1,188 @@
|
||||
//! This module contains some logic for working with the console handle.
|
||||
|
||||
use winapi::um::{
|
||||
fileapi::{CreateFileW, OPEN_EXISTING},
|
||||
handleapi::INVALID_HANDLE_VALUE,
|
||||
processenv::GetStdHandle,
|
||||
winbase::{STD_INPUT_HANDLE, STD_OUTPUT_HANDLE},
|
||||
winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE},
|
||||
};
|
||||
|
||||
use std::io::{self, Result};
|
||||
use std::ops::Deref;
|
||||
use std::ptr::null_mut;
|
||||
|
||||
/// This enum represents the different handles that could be requested.
|
||||
///
|
||||
/// Some more details could be found [here](https://docs.microsoft.com/en-us/windows/console/getstdhandle#parameters)
|
||||
pub enum HandleType {
|
||||
/// This represents the `STD_OUTPUT_HANDLE`
|
||||
OutputHandle,
|
||||
/// This represents the `STD_INPUT_HANDLE`
|
||||
InputHandle,
|
||||
/// This represents the `CONOUT$` file handle
|
||||
/// When using multiple screen buffers this will always point to the to the current screen output buffer.
|
||||
CurrentOutputHandle,
|
||||
/// This represents the `CONIN$` file handle.
|
||||
/// When using multiple screen buffers this will always point to the to the current screen input buffer.
|
||||
CurrentInputHandle,
|
||||
}
|
||||
|
||||
/// This abstracts away some WinaApi calls to set and get some console handles.
|
||||
///
|
||||
// Wraps the underlying WinApi type: [HANDLE]
|
||||
pub struct Handle {
|
||||
handle: HANDLE,
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
pub fn new(handle: HandleType) -> Result<Handle> {
|
||||
let handle = match handle {
|
||||
HandleType::OutputHandle => Handle::output_handle(),
|
||||
HandleType::InputHandle => Handle::input_handle(),
|
||||
HandleType::CurrentOutputHandle => Handle::current_out_handle(),
|
||||
HandleType::CurrentInputHandle => Handle::current_in_handle(),
|
||||
}?;
|
||||
|
||||
Ok(Handle { handle })
|
||||
}
|
||||
|
||||
/// Get the handle of the active screen buffer.
|
||||
/// When using multiple screen buffers this will always point to the to the current screen output buffer.
|
||||
///
|
||||
/// On success this function returns the `HANDLE` to `STD_OUTPUT_HANDLE`.
|
||||
///
|
||||
/// This function uses `CONOUT$` to create a file handle to the current output buffer.
|
||||
///
|
||||
/// Wraps the underlying function call: [CreateFileW]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilew]
|
||||
pub fn current_out_handle() -> Result<HANDLE> {
|
||||
let utf16: Vec<u16> = "CONOUT$\0".encode_utf16().collect();
|
||||
let utf16_ptr: *const u16 = utf16.as_ptr();
|
||||
|
||||
let handle = unsafe {
|
||||
CreateFileW(
|
||||
utf16_ptr,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
null_mut(),
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
if !Handle::is_valid_handle(&handle) {
|
||||
println!("invalid!!");
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
/// Get the handle of the active input screen buffer.
|
||||
/// When using multiple screen buffers this will always point to the to the current screen input buffer.
|
||||
///
|
||||
/// On success this function returns the `HANDLE` to `STD_INPUT_HANDLE`.
|
||||
///
|
||||
/// This function uses `CONIN$` to create a file handle to the current input buffer.
|
||||
///
|
||||
/// Wraps the underlying function call: [CreateFileW]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilew]
|
||||
pub fn current_in_handle() -> Result<HANDLE> {
|
||||
let utf16: Vec<u16> = "CONIN$\0".encode_utf16().collect();
|
||||
let utf16_ptr: *const u16 = utf16.as_ptr();
|
||||
|
||||
let handle = unsafe {
|
||||
CreateFileW(
|
||||
utf16_ptr,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
null_mut(),
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
null_mut(),
|
||||
)
|
||||
};
|
||||
|
||||
if !Handle::is_valid_handle(&handle) {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
/// Get the handle of the output screen buffer.
|
||||
///
|
||||
/// On success this function returns the `HANDLE` to `STD_OUTPUT_HANDLE`.
|
||||
///
|
||||
/// Wraps the underlying function call: [GetStdHandle] whit argument `STD_OUTPUT_HANDLE`
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getstdhandle]
|
||||
pub fn output_handle() -> Result<HANDLE> {
|
||||
unsafe {
|
||||
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
if !Handle::is_valid_handle(&handle) {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the handle of the input screen buffer.
|
||||
///
|
||||
/// On success this function returns the `HANDLE` to `STD_INPUT_HANDLE`.
|
||||
///
|
||||
/// Wraps the underlying function call: [GetStdHandle] whit argument `STD_INPUT_HANDLE`
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getstdhandle]
|
||||
pub fn input_handle() -> Result<HANDLE> {
|
||||
unsafe {
|
||||
let handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
if !Handle::is_valid_handle(&handle) {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the console handle is an invalid handle value.
|
||||
///
|
||||
/// This is done by checking if the passed `HANDLE` is equal to `INVALID_HANDLE_VALUE`
|
||||
pub fn is_valid_handle(handle: &HANDLE) -> bool {
|
||||
if *handle == INVALID_HANDLE_VALUE {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Handle {
|
||||
type Target = HANDLE;
|
||||
|
||||
fn deref(&self) -> &<Self as Deref>::Target {
|
||||
&self.handle
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HANDLE> for Handle {
|
||||
fn from(handle: HANDLE) -> Self {
|
||||
Handle { handle }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{Handle, HandleType};
|
||||
|
||||
#[test]
|
||||
fn get_handle() {
|
||||
let out_put_handle = Handle::new(HandleType::OutputHandle).unwrap();
|
||||
let out_put_handle = Handle::new(HandleType::InputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle).unwrap();
|
||||
let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle).unwrap();
|
||||
}
|
||||
}
|
28
crossterm_winapi/src/lib.rs
Normal file
28
crossterm_winapi/src/lib.rs
Normal file
@ -0,0 +1,28 @@
|
||||
extern crate winapi;
|
||||
|
||||
mod console;
|
||||
mod console_mode;
|
||||
mod csbi;
|
||||
mod handle;
|
||||
mod screen_buffer;
|
||||
mod structs;
|
||||
|
||||
pub use self::{
|
||||
console::Console,
|
||||
console_mode::ConsoleMode,
|
||||
csbi::ScreenBufferInfo,
|
||||
handle::{Handle, HandleType},
|
||||
screen_buffer::ScreenBuffer,
|
||||
structs::{Coord, Size, WindowPositions},
|
||||
};
|
||||
|
||||
/// Parses the given integer to an bool by checking if the value is 0 or 1.
|
||||
/// This is currently used for checking if a WinApi called succeeded, this might be moved into a macro at some time.
|
||||
/// So please don't use this :(.
|
||||
pub fn is_true(value: i32) -> bool {
|
||||
if value == 0 {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
137
crossterm_winapi/src/screen_buffer.rs
Normal file
137
crossterm_winapi/src/screen_buffer.rs
Normal file
@ -0,0 +1,137 @@
|
||||
//! This contains the logic for working with the console buffer.
|
||||
|
||||
use super::{is_true, Handle, HandleType, ScreenBufferInfo};
|
||||
|
||||
use winapi::{
|
||||
shared::minwindef::TRUE,
|
||||
shared::ntdef::NULL,
|
||||
um::{
|
||||
minwinbase::SECURITY_ATTRIBUTES,
|
||||
wincon::{
|
||||
CreateConsoleScreenBuffer, GetConsoleScreenBufferInfo, SetConsoleActiveScreenBuffer,
|
||||
SetConsoleScreenBufferSize, CONSOLE_TEXTMODE_BUFFER, COORD,
|
||||
},
|
||||
winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE},
|
||||
},
|
||||
};
|
||||
|
||||
use std::io::{Error, Result};
|
||||
use std::mem::size_of;
|
||||
|
||||
pub struct ScreenBuffer {
|
||||
handle: Handle,
|
||||
}
|
||||
|
||||
impl ScreenBuffer {
|
||||
/// Create an instance of `ScreenBuffer` where the `HANDLE`, used for the functions this type wraps, is the current output handle.
|
||||
pub fn current() -> Result<ScreenBuffer> {
|
||||
Ok(ScreenBuffer {
|
||||
handle: Handle::new(HandleType::CurrentOutputHandle)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create new console screen buffer.
|
||||
///
|
||||
/// Wraps the underlying function call: [CreateConsoleScreenBuffer]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/createconsolescreenbuffer]
|
||||
pub fn create() -> ScreenBuffer {
|
||||
let mut security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES {
|
||||
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
|
||||
lpSecurityDescriptor: NULL,
|
||||
bInheritHandle: TRUE,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let new_screen_buffer = CreateConsoleScreenBuffer(
|
||||
GENERIC_READ | // read/write access
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, // shared
|
||||
&mut security_attr, // default security attributes
|
||||
CONSOLE_TEXTMODE_BUFFER, // must be TEXTMODE
|
||||
NULL,
|
||||
);
|
||||
ScreenBuffer {
|
||||
handle: Handle::from(new_screen_buffer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This will make this `ScreenBuffer` the active one.
|
||||
///
|
||||
/// Wraps the underlying function call: [SetConsoleActiveScreenBuffer]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsoleactivescreenbuffer]
|
||||
pub fn show(&self) -> Result<()> {
|
||||
unsafe {
|
||||
if !is_true(SetConsoleActiveScreenBuffer(*self.handle)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the screen buffer information like terminal size, cursor position, buffer size.
|
||||
///
|
||||
/// Wraps the underlying function call: [GetConsoleScreenBufferInfo]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo]
|
||||
pub fn info(&self) -> Result<ScreenBufferInfo> {
|
||||
let mut csbi = ScreenBufferInfo::new();
|
||||
|
||||
unsafe {
|
||||
if !is_true(GetConsoleScreenBufferInfo(*self.handle, &mut csbi.0)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(csbi)
|
||||
}
|
||||
|
||||
/// Set the console screen buffer size to the given size.
|
||||
///
|
||||
/// Wraps the underlying function call: [SetConsoleScreenBufferSize]
|
||||
/// link: [https://docs.microsoft.com/en-us/windows/console/setconsolescreenbuffersize]
|
||||
pub fn set_size(&self, x: i16, y: i16) -> Result<()> {
|
||||
unsafe {
|
||||
if !is_true(SetConsoleScreenBufferSize(
|
||||
*self.handle,
|
||||
COORD { X: x, Y: y },
|
||||
)) {
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the underlining raw `HANDLE` used by this type to execute whit.
|
||||
pub fn get_handle(&self) -> &Handle {
|
||||
return &self.handle;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Handle> for ScreenBuffer {
|
||||
fn from(handle: Handle) -> Self {
|
||||
ScreenBuffer { handle }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HANDLE> for ScreenBuffer {
|
||||
fn from(handle: HANDLE) -> Self {
|
||||
ScreenBuffer {
|
||||
handle: Handle::from(handle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ScreenBuffer;
|
||||
|
||||
#[test]
|
||||
fn screen_buffer_info() {
|
||||
let buffer = ScreenBuffer::current().unwrap();
|
||||
let info = buffer.info().unwrap();
|
||||
info.terminal_size();
|
||||
info.terminal_window();
|
||||
info.attributes();
|
||||
info.cursor_pos();
|
||||
}
|
||||
}
|
42
crossterm_winapi/src/structs/coord.rs
Normal file
42
crossterm_winapi/src/structs/coord.rs
Normal file
@ -0,0 +1,42 @@
|
||||
//! This module provides a type that represents some location/coordination.
|
||||
//! For example, in WinAPi we have `COORD` which looks and feels inconvenient.
|
||||
//! This module provides also some trait implementations who will make parsing and working whit `COORD` easier.
|
||||
|
||||
use winapi::um::wincon::COORD;
|
||||
|
||||
/// This is type represents the position of something on a certain 'x' and 'y'.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Coord {
|
||||
/// the position on the x axis
|
||||
pub x: i16,
|
||||
/// the position on the y axis
|
||||
pub y: i16,
|
||||
}
|
||||
|
||||
impl Coord {
|
||||
/// Create a new size instance by passing in the width and height.
|
||||
pub fn new(x: i16, y: i16) -> Coord {
|
||||
Coord { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<COORD> for Coord {
|
||||
fn from(coord: COORD) -> Self {
|
||||
Coord::new(coord.X, coord.Y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Coord> for COORD {
|
||||
fn from(location: Coord) -> Self {
|
||||
COORD {
|
||||
X: location.x,
|
||||
Y: location.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(u16, u16)> for Coord {
|
||||
fn into(self) -> (u16, u16) {
|
||||
(self.x as u16, self.y as u16)
|
||||
}
|
||||
}
|
7
crossterm_winapi/src/structs/mod.rs
Normal file
7
crossterm_winapi/src/structs/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod coord;
|
||||
mod size;
|
||||
mod window_coords;
|
||||
|
||||
pub use self::coord::Coord;
|
||||
pub use self::size::Size;
|
||||
pub use self::window_coords::WindowPositions;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user