Introduced: Crossterm Workspace and feature flags. (#84)

* Introduced: crossterm workspace, feature flags.
This commit is contained in:
Timon 2019-01-27 21:16:14 +01:00 committed by GitHub
parent 2bfa7ffd5b
commit ad74f6b524
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
153 changed files with 4315 additions and 2338 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
target/
.idea/
**/*.rs.bk
Cargo.lock
Cargo.lock

View File

@ -1,22 +1,22 @@
# Set up the Rust toolchain.
language: rust
rust:
- stable
- nightly
- stable
- nightly
before_script:
- export PATH=$PATH:/home/travis/.cargo/bin
- rustup component add rustfmt-preview
- export PATH=$PATH:/home/travis/.cargo/bin
- rustup component add rustfmt-preview
os:
- linux
- osx
- windows
- linux
- osx
- windows
branches:
only:
- master
- master
script:
- cargo build
- cargo fmt -- --check
- cargo build
- cargo fmt -- --check

View File

@ -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,26 +10,43 @@ 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"
[[example]]
name = "command_bar"
path = "examples/program_examples/command_bar.rs"
path = "examples/program_examples/command_bar.rs"

21
LICENSE
View File

@ -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.

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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
View 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

View File

@ -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();
}

View File

@ -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(())
}

View File

@ -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)
}

View File

@ -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

View File

@ -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,
}
}
}

View File

@ -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;

View 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};

View 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;

View 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
}

View File

@ -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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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
View 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

View 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() {}

View File

@ -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() {}

View File

@ -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)
}

View File

@ -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

View File

@ -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;

View File

@ -2,6 +2,7 @@
use super::*;
use crossterm_utils::TerminalOutput;
use std::char;
use std::thread;
use winapi::um::winnt::INT;

View File

@ -0,0 +1,6 @@
extern crate crossterm_utils;
mod input;
mod sys;
pub use self::input::{input, AsyncReader, KeyEvent, TerminalInput};

View File

@ -0,0 +1,2 @@
#[cfg(unix)]
pub mod unix;

View 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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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
View 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

View 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.
}

View 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.
}

View 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};

View File

@ -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()),
)
.unwrap();
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)?;

View File

@ -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;

View File

@ -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(())

View File

@ -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>,

View 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<()>;
}

View File

@ -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(())
}
}

View 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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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
View 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

View 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();
}

View File

@ -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(())
}

View File

@ -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)
}

View File

@ -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),
}

View File

@ -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(())
}
}

View File

@ -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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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"

View 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

View 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()
}

View 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};

View 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};

View 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)
}
}

View 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)
}
}

View File

@ -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();
}
}
}

View File

@ -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<()>;
}

View File

@ -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)
}

View File

@ -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,
}
}
}

View File

@ -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
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

View 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
View 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

View File

@ -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<()>;

View File

@ -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)]

View File

@ -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,
}
}

View 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};

View File

@ -0,0 +1,4 @@
#[macro_export]
macro_rules! csi {
($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
}

View File

@ -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.

View File

@ -1,31 +1,26 @@
//! 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:
//! In Rust we can call `stdout()` to get a handle to the current default console handle.
//! 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.
//!
//! ```
//! write!(std::io::stdout(), "{}", "some text").
//! ```
//! 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.
//!
//! 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).
//!
//! 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.
//!
//! 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`.
//! 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,
}

View File

@ -0,0 +1 @@
pub mod winapi;

View 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;

View File

@ -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,
}
}
}

View File

@ -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<()> {

View File

@ -0,0 +1,5 @@
#[cfg(windows)]
pub mod winapi;
#[cfg(unix)]
pub mod unix;

View 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(())
}

View 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
View File

@ -0,0 +1,4 @@
target/
.idea/
**/*.rs.bk
Cargo.lock

View 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

View 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"] }

View 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.

View 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() {}

View 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() {}

View 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 */
}

View 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();
}

View 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),
}
}
}

View 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);
}
}

View 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)
}
}

View 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();
}
}

View 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;
}
}

View 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();
}
}

Some files were not shown because too many files have changed in this diff Show More