diff --git a/.gitignore b/.gitignore index d483145..6696b18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -target/ -.idea/ -.vscode/ +**/target/ +**/.idea/ +**/.vscode/ **/*.rs.bk -Cargo.lock +**/Cargo.lock diff --git a/.travis.yml b/.travis.yml index 4a119fa..978cc69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,3 @@ -# Set up the Rust toolchain. language: rust rust: @@ -28,5 +27,4 @@ script: - rustc --version - if [ "$TRAVIS_RUST_VERSION" = "stable" ]; then cargo fmt --all -- --check; fi - cargo build - - cargo test --all -- --nocapture --test-threads 1 - - scripts/test-examples.sh + - cargo test --all-features -- --nocapture --test-threads 1 diff --git a/docs/CHANGELOG.md b/CHANGELOG.md similarity index 95% rename from docs/CHANGELOG.md rename to CHANGELOG.md index 945be34..bc1023e 100644 --- a/docs/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,20 @@ -# Changes crossterm 0.11.0 +# Version 0.11.1 + +- Maintenance release +- All sub-crates were moved to their own repositories in the `crossterm-rs` organization + +# Version 0.11.0 + As a preparation for crossterm 0.1.0 we have moved crossterm to an organisation called 'crossterm-rs'. ### Code Quality + - Code Cleanup: [warning-cleanup], [crossterm_style-cleanup], [crossterm_screen-cleanup], [crossterm_terminal-cleanup], [crossterm_utils-cleanup], [2018-cleanup], [api-cleanup-1], [api-cleanup-2], [api-cleanup-3] - Examples: [example-cleanup_1], [example-cleanup_2], [example-fix], [commandbar-fix], [snake-game-improved] - Fixed all broken tests and added tests ### Important Changes + - Return written bytes: [return-written-bytes] - Added derives: `Debug` for `ObjectStyle` [debug-derive], Serialize/Deserialize for key events [serde] - Improved error handling: @@ -65,45 +73,52 @@ As a preparation for crossterm 0.1.0 we have moved crossterm to an organisation [added-key-event-enter]: https://github.com/crossterm-rs/crossterm/pull/236 [fixed-get-set-terminal-size]: https://github.com/crossterm-rs/crossterm/pull/242 -# Changes crossterm 0.10.1 +# Version 0.10.1 -# Changes crossterm 0.10.0 ~ yanked +# Version 0.10.0 ~ yanked - Implemented command API, to have better performance and more control over how and when commands are executed. [PR](https://github.com/crossterm-rs/crossterm/commit/1a60924abd462ab169b6706aab68f4cca31d7bc2), [issue](https://github.com/crossterm-rs/crossterm/issues/171) - Fixed showing, hiding cursor windows implementation - Removed some of the parsing logic from windows keys to ansi codes to key events [PR](https://github.com/crossterm-rs/crossterm/commit/762c3a9b8e3d1fba87acde237f8ed09e74cd9ecd) - Made terminal size 1-based [PR](https://github.com/crossterm-rs/crossterm/commit/d689d7e8ed46a335474b8262bd76f21feaaf0c50) - Added some derive implementation -# Changes crossterm 0.9.6 +# Version 0.9.6 + - Copy for KeyEvent - CTRL + Left, Down, Up, Right key support - SHIFT + Left, Down, Up, Right key support - Fixed UNIX cursor position bug [issue](https://github.com/crossterm-rs/crossterm/issues/140), [PR](https://github.com/crossterm-rs/crossterm/pull/152) -# Changes crossterm 0.9.5 +# Version 0.9.5 + - Prefetching buffer size for more efficient windows input reads. [PR](https://github.com/crossterm-rs/crossterm/pull/144) -# Changes crossterm 0.9.4 +# Version 0.9.4 + - Reset foreground and background color individually. [PR](https://github.com/crossterm-rs/crossterm/pull/138) - Backtap input support. [PR](https://github.com/crossterm-rs/crossterm/pull/129) - Corrected white/grey and added dark grey. - Fixed getting cursor position with raw screen enabled. [PR](https://github.com/crossterm-rs/crossterm/pull/134) - Removed one redundant stdout lock -# Changes crossterm 0.9.3 +# Version 0.9.3 + - Removed println from `SyncReader` -## Changes crossterm 0.9.2 +## Version 0.9.2 + - Terminal size linux was not 0-based - Windows mouse input event position was 0-based ans should be 1-based - Result, ErrorKind are made re-exported - Fixed some special key combination detections for UNIX systems - Made FreeBSD compile -## Changes crossterm 0.9.1 +## Version 0.9.1 + - Fixed libc compile error -## Changes crossterm 0.9.0 (yanked) +## Version 0.9.0 (yanked) + This release is all about moving to a stabilized API for 1.0. - Major refactor and cleanup. @@ -123,23 +138,28 @@ This release is all about moving to a stabilized API for 1.0. - Raw modes UNIX systems improved - Added `NoItalic` attribute -## Changes crossterm to 0.8.2 +## Version 0.8.2 + - Bug fix for sync reader UNIX. -## Changes crossterm to 0.8.1 +## Version 0.8.1 + - Added public re-exports for input. -# Changes crossterm 0.8.0 +# Version 0.8.0 + - Introduced KeyEvents - Introduced MouseEvents - Upgraded crossterm_winapi 0.2 -# Changes crossterm 0.7.0 +# Version 0.7.0 + - Introduced more `Attributes` - Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). - Removed `ColorType` since it was unnecessary. -# Changes crossterm 0.6.0 +# Version 0.6.0 + - Introduced feature flags; input, cursor, style, terminal, screen. - All modules are moved to their own crate. - Introduced crossterm workspace @@ -148,10 +168,12 @@ This release is all about moving to a stabilized API for 1.0. [PR 84](https://github.com/crossterm-rs/crossterm/pull/84) -# Changes crossterm 0.5.5 +# Version 0.5.5 + - Error module is made public [PR 78](https://github.com/crossterm-rs/crossterm/pull/78). -# Changes crossterm 0.5.4 +# Version 0.5.4 + - WinApi rewrite and correctly error handled [PR 67](https://github.com/crossterm-rs/crossterm/pull/67) - Windows attribute support [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) - Readline bug fix windows systems [PR 62](https://github.com/crossterm-rs/crossterm/pull/62) @@ -159,31 +181,37 @@ This release is all about moving to a stabilized API for 1.0. - General refactoring, all warnings removed. - Documentation improvement. -# Changes crossterm 0.5.1 +# Version 0.5.1 + - Documentation refactor. - Fixed broken API documentation [PR 53](https://github.com/crossterm-rs/crossterm/pull/53). -# Changes crossterm 0.5.0 +# Version 0.5.0 + - Added ability to pause the terminal [issue](https://github.com/crossterm-rs/crossterm/issues/39) - RGB support for Windows 10 systems - ANSI color value (255) color support - More convenient API, no need to care about `Screen` unless working with when working with alternate or raw screen [PR](https://github.com/crossterm-rs/crossterm/pull/44) - Implemented Display for styled object -# Changes crossterm to 0.4.3 +# Version 0.4.3 + - Fixed bug [issue 41](https://github.com/crossterm-rs/crossterm/issues/41) -# Changes crossterm to 0.4.2 +# Version 0.4.2 + - Added functionality to make a styled object writable to screen [issue 33](https://github.com/crossterm-rs/crossterm/issues/33) - Added unit tests. - Bugfix with getting terminal size unix. - Bugfix with returning written bytes [pull request 31](https://github.com/crossterm-rs/crossterm/pull/31) - removed methods calls: `as_any()` and `as_any_mut()` from `TerminalOutput` -# Bug fix crossterm to 0.4.1 +# Version 0.4.1 + - Fixed resizing of ansi terminal with and height where in the wrong order. -# Features / Fixes in crossterm 0.4.0 +# Version 0.4.0 + - Input support (read_line, read_char, read_async, read_until_async) - Styling module improved - Everything is multithreaded (`Send`, `Sync`) @@ -194,7 +222,7 @@ This release is all about moving to a stabilized API for 1.0. - Overall commend improvement. - Overall refactor of code. -# Features in crossterm 0.3.0 +# Version 0.3.0 This version has some braking changes check [upgrade manual](UPGRADE%20Manual.md) for more information about what is changed. I think you should not switch to version `0.3.0` if you aren't going to use the AlternateScreen feature. @@ -209,6 +237,7 @@ Some Features crossterm 0.3.0 - exit the current process. ## Alternate screen + This create supports alternate screen for both windows and unix systems. You can use *Nix style applications often utilize an alternate screen buffer, so that they can modify the entire contents of the buffer, without affecting the application that started them. @@ -219,6 +248,7 @@ Vim uses the entirety of the screen to edit the file, then returning to bash lea I Highly recommend you to check the `examples/program_examples/first_depth_search` for seeing this in action. ## Raw screen + This crate now supports raw screen for both windows and unix systems. What exactly is raw state: - No line buffering. @@ -232,6 +262,7 @@ What exactly is raw state: With these modes you can easier design the terminal screen. ## Some functionalities added + - Hiding and showing terminal cursor - Enable or disabling blinking of the cursor for unix systems (this is not widely supported) - Restoring the terminal to original modes. @@ -319,10 +350,11 @@ like demonstrated above, to get the functionalities of `cursor(), color(), termi You need to pass it the same `Context` as you have passed to the previous three called functions, If you don't use the same `Context` in `cursor(), color(), terminal()` than these modules will be using the main screen and you will not see anything at the alternate screen. If you use the [Crossterm](https://github.com/crossterm-rs/crossterm/blob/master/src/shared/crossterm.rs) type you can get the `Context` from it by calling the crossterm.get_context() whereafter you can create the AlternateScreen from it. -# Fixes in crossterm 0.2.2 +# Version 0.2.2 + - Bug see [issue 15](https://github.com/crossterm-rs/crossterm/issues/15) -# Fixes in crossterm 0.2.1 +# Version 0.2.1 - Default ANSI escape codes for windows machines, if windows does not support ANSI switch back to WinApi. - method grammar mistake fixed [Issue 3](https://github.com/crossterm-rs/crossterm/issues/3) @@ -330,7 +362,7 @@ If you don't use the same `Context` in `cursor(), color(), terminal()` than thes - Removed bin reference from crate [Issue 6](https://github.com/crossterm-rs/crossterm/issues/6) - Get position unix fixed [issue 8](https://github.com/crossterm-rs/crossterm/issues/8) -# Features crossterm 0.2 +# Version 0.2 - 256 color support. - Text Attributes like: bold, italic, underscore and crossed word ect. diff --git a/Cargo.toml b/Cargo.toml index 5634b20..61c8512 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crossterm" -version = "0.11.0" +version = "0.11.1" authors = ["T. Post"] description = "An crossplatform terminal library for manipulating terminals." repository = "https://github.com/crossterm-rs/crossterm" @@ -12,7 +12,7 @@ readme = "README.md" edition = "2018" [features] -default = ["cursor", "style","terminal","screen","input"] +default = ["cursor", "style", "terminal", "screen", "input"] cursor = ["crossterm_cursor"] style = ["crossterm_style"] @@ -20,29 +20,13 @@ 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" -] - -exclude = [ - "examples/program_examples" -] - [dependencies] -crossterm_screen = { path = "./crossterm_screen", version = "0.3.0" , optional = true } -crossterm_cursor = { path = "./crossterm_cursor", version = "0.3.0" , optional = true } -crossterm_terminal = { path = "./crossterm_terminal", version = "0.3.0", optional = true } -crossterm_style = { path = "./crossterm_style", version = "0.5.0" , optional = true } -crossterm_input = { path = "./crossterm_input", version = "0.4.0" , optional = true } -crossterm_utils = { path = "./crossterm_utils", version = "0.3.0" , optional = false } +crossterm_screen = { version = "0.3.1" , optional = true } +crossterm_cursor = { version = "0.3.1" , optional = true } +crossterm_terminal = { version = "0.3.1", optional = true } +crossterm_style = { version = "0.5.1" , optional = true } +crossterm_input = { version = "0.4.1" , optional = true } +crossterm_utils = { version = "0.3.1" , optional = false } [lib] name = "crossterm" diff --git a/README.md b/README.md index 4751057..350aa51 100644 --- a/README.md +++ b/README.md @@ -1,81 +1,66 @@

-# cross-platform terminal manipulating library. - [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z8QK6XU749JB2) ![Travis][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] [![Join us on Discord][s5]][l5] +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z8QK6XU749JB2) ![Travis][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] [![Join us on Discord][s5]][l5] -[s1]: https://img.shields.io/crates/v/crossterm.svg -[l1]: https://crates.io/crates/crossterm - -[s2]: https://img.shields.io/badge/license-MIT-blue.svg -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s6]: https://tokei.rs/b1/github/crossterm-rs/crossterm?category=code -[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master +# cross-platform terminal manipulating library. Have you ever been disappointed when a terminal library for rust was only written for UNIX systems? -Crossterm provides clearing, input handling, styling, cursor movement, and terminal actions for both Windows and UNIX systems. +Crossterm provides clearing, input handling, styling, cursor movement, and terminal actions for both +Windows and UNIX systems. Crossterm aims to be simple and easy to call in code. Through the simplicity of Crossterm, you do not have to worry about the platform you are working with. -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 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 consists of five modules that are provided behind [feature flags](https://crossterm-rs.github.io/crossterm/docs/feature_flags.html) so that you can define which features you'd like to have; by default, all features are enabled. -- [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 consists of five modules that are provided behind +[feature flags](https://crossterm-rs.github.io/crossterm/docs/feature_flags.html) so that you can define +which features you'd like to have; by default, all features are enabled. + +- [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) - [Features](#features) - [Examples](#examples) - - [Crossterm Type](#crossterm-type) - [Styled Text](#styled-text) - [Cursor](#cursor) - [Terminal](#terminal) - [Input Reading](#input-reading) - [Tested Terminals](#tested-terminals) -- [Notice](#notice) -- [Todo](#todo) - [Contributing](#contributing) - [Authors](#authors) - [License](#license) ## Getting Started -All [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) of how crossterm works can be found in the example directory. +All [examples](https://github.com/crossterm-rs/examples) of how crossterm works can be found in the example repository. Add the Crossterm package to your `Cargo.toml` file. ``` [dependencies] -crossterm = "^0.11" +crossterm = "0.11" ``` ### Useful Links -- [Book](https://crossterm-rs.github.io/crossterm/docs//) +- [Book](https://crossterm-rs.github.io/crossterm/docs/) - [Documentation](https://docs.rs/crossterm/) - [Crates.io](https://crates.io/crates/crossterm) -- [Program Examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/program_examples) -- [Examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) +- [Examples](https://github.com/crossterm-rs/examples) ## Features - Cross-platform -- Multithreaded (send, sync) +- Multi-threaded (send, sync) - Detailed Documentation - Few Dependencies - Cursor @@ -104,19 +89,25 @@ crossterm = "^0.11" - Read mouse input events (press, release, position, button) ## Examples -These are some basic examples demonstrating how to use this crate. See [examples](https://github.com/crossterm-rs/crossterm/blob/master/examples/) for more. + +These are some basic examples demonstrating how to use this crate. See the +[examples](https://github.com/crossterm-rs/examples) repository. ### Command API -My first recommendation is to use the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) because this might replace some of the existing API in the future. -Because it is more convenient, faster, and easier to use. +My first recommendation is to use the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) +because this might replace some of the existing API in the future. It is more convenient, faster, and easier to use. ### Styled Text + This module enables you to style the terminal text. -Good documentation can be found at the following places: [docs](https://docs.rs/crossterm_style/), [book](https://crossterm-rs.github.io/crossterm/docs/styling.html), [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/key_events.rs) +Good documentation can be found at the following places: [docs](https://docs.rs/crossterm_style/), +[book](https://crossterm-rs.github.io/crossterm/docs/styling.html) +and [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/key_events.rs). _style text with attributes_ + ```rust use crossterm::{Colored, Color, Colorize, Styler, Attribute}; @@ -129,6 +120,7 @@ println!("{}", styled_text); ``` _style text with colors_ + ```rust println!("{} Red foreground color", Colored::Fg(Color::Red)); println!("{} Blue background color", Colored::Bg(Color::Blue)); @@ -137,7 +129,9 @@ println!("{} Blue background color", Colored::Bg(Color::Blue)); let styled_text = "Bold Underlined".red().on_blue(); println!("{}", styled_text); ``` + _style text with RGB and ANSI Value_ + ```rust // custom rgb value (Windows 10 and UNIX systems) println!("{} some colored text", Colored::Fg(Color::Rgb { @@ -150,11 +144,12 @@ println!("{} some colored text", Colored::Fg(Color::Rgb { println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); ``` - ### Cursor + This module enables you to work with the terminal cursor. -Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_cursor/), [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/cursor.rs) +Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_cursor/) and +[examples](https://github.com/crossterm-rs/examples). ```rust use crossterm::cursor; @@ -194,9 +189,11 @@ cursor.blink(true) ``` ### Terminal + This module enables you to work with the terminal in general. -Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_terminal/), [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/terminal.rs). +Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_terminal/) and +[examples](https://github.com/crossterm-rs/examples). ```rust use crossterm::{terminal,ClearType}; @@ -233,9 +230,12 @@ terminal.write("Some text\n Some text on new line"); ``` ### Input Reading + This module enables you to read user input events. -Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_input/), [book](https://crossterm-rs.github.io/crossterm/docs/input.html), [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/key_events.rs) +Good documentation could be found on the following places: [docs](https://docs.rs/crossterm_input/), +[book](https://crossterm-rs.github.io/crossterm/docs/input.html) and +[examples](https://github.com/crossterm-rs/examples). _available imports_ ```rust @@ -245,6 +245,7 @@ use crossterm_input::{ ``` _Simple Readings_ + ```rust let mut input = input(); @@ -260,8 +261,10 @@ let mut input = input(); ``` _Read input events synchronously or asynchronously._ + ```rust -// make sure to enable raw mode, this will make sure key events won't be handled by the terminal it's self and allows crossterm to read the input and pass it back to you. +// make sure to enable raw mode, this will make sure key events won't be handled by the terminal it's +// self and allows crossterm to read the input and pass it back to you. let screen = RawScreen::into_raw_mode(); let mut input = input(); @@ -281,6 +284,7 @@ if let Some(key_event) = stdin.next() { ``` _Enable mouse input events._ + ```rust let input = input(); @@ -292,9 +296,13 @@ input.disable_mouse_mode().unwrap(); ``` ### Alternate and Raw Screen -These concepts are a little more complex and would take over the README, please check out the [docs](https://docs.rs/crossterm_screen/), [book](https://crossterm-rs.github.io/crossterm/docs/screen.html), and [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples). -## Used By +These concepts are a little more complex and would take over the README, please check out +the [docs](https://docs.rs/crossterm_screen/), [book](https://crossterm-rs.github.io/crossterm/docs/screen.html) +and [examples](https://github.com/crossterm-rs/examples). + +## Used by + - [Broot](https://dystroy.org/broot/) - [Cursive](https://github.com/gyscos/Cursive) - [TUI](https://github.com/fdehau/tui-rs) @@ -312,8 +320,9 @@ These concepts are a little more complex and would take over the README, please - (Arch, Manjaro) KDE Konsole - Linux Mint -This crate supports all Unix terminals and Windows terminals down to Windows 7; however, not all of the terminals have been tested. -If you have used this library for a terminal other than the above list without issues, then feel free to add it to the above list - I really would appreciate it! +This crate supports all Unix terminals and Windows terminals down to Windows 7; however, not all of the +terminals have been tested. If you have used this library for a terminal other than the above list without +issues, then feel free to add it to the above list - I really would appreciate it! ## Contributing @@ -332,4 +341,24 @@ Would you like crossterm to be even more gorgeous and beautiful? You can help wi ## License -This project, crossterm and all it's sub-modules: crossterm_screen, crossterm_cursor, crossterm_style, crossterm_input, crossterm_terminal, crossterm_winapi, crossterm_utils are licensed under the MIT License - see the [LICENSE.md](https://github.com/crossterm-rs/crossterm/blob/master/LICENSE) file for details +This project, crossterm and all it's sub-modules: crossterm_screen, crossterm_cursor, crossterm_style, +crossterm_input, crossterm_terminal, crossterm_winapi, crossterm_utils are licensed under the MIT License - see +the [LICENSE](https://github.com/crossterm-rs/crossterm/blob/master/LICENSE) file for details + +[s1]: https://img.shields.io/crates/v/crossterm.svg +[l1]: https://crates.io/crates/crossterm + +[s2]: https://img.shields.io/badge/license-MIT-blue.svg +[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/ + +[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord +[l5]: https://discord.gg/K4nyTDB + +[s6]: https://tokei.rs/b1/github/crossterm-rs/crossterm?category=code +[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master diff --git a/crossterm_cursor/.gitignore b/crossterm_cursor/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_cursor/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_cursor/CHANGELOG.md b/crossterm_cursor/CHANGELOG.md deleted file mode 100644 index b53af9e..0000000 --- a/crossterm_cursor/CHANGELOG.md +++ /dev/null @@ -1,15 +0,0 @@ -# Changes crossterm_cursor 0.3 -- `TerminalCursor::pos()` returns `crossterm::Result<(u16, u16)>` -- `TerminalCursor::move_*` returns `crossterm::Result` -- `TerminalCursor::reset_position()` to `restore_position()` -- All `i16` values for indexing: set/get cursor pos synced to `u16` values -- `Command::get_anis_code()` to `ansi_code()` -- `ExecutableCommand::queue` returns `crossterm::Result` -- `QueueableCommand::queue` returns `crossterm::Result` -- Command API takes mutable self instead of self - -# Changes crossterm_cursor 0.2 -- Removed `TerminalCursor::from_output()` - -# Changes crossterm_cursor 0.1 -- Moved out of `crossterm` 5.4 crate. \ No newline at end of file diff --git a/crossterm_cursor/Cargo.toml b/crossterm_cursor/Cargo.toml deleted file mode 100644 index ca93fcb..0000000 --- a/crossterm_cursor/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "crossterm_cursor" -version = "0.3.0" -authors = ["T. Post"] -description = "A cross-platform library for moving the terminal cursor." -repository = "https://github.com/crossterm-rs/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.8", features = ["wincon","winnt","minwindef"] } -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0"} - -[dependencies] -crossterm_utils = { path="../crossterm_utils", version = "0.3.0"} diff --git a/crossterm_cursor/LICENSE b/crossterm_cursor/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_cursor/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_cursor/README.md b/crossterm_cursor/README.md deleted file mode 100644 index d63754f..0000000 --- a/crossterm_cursor/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# Crossterm Cursor | cross-platform cursor movement. - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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://crossterm-rs.github.io/crossterm/docs/feature_flags.html). - -## Table of contents: -- [Getting started](#getting-started) -- [Useful links](#useful-links) -- [Features](#features) -- [Examples](#examples) -- [Tested Terminals](#tested-terminals) -- [Authors](#authors) -- [License](#license) - -## Getting Started - -All examples of how `crossterm_cursor` works can be found in the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) directory. - -Add the `crossterm_cursor` package to your `Cargo.toml` file. - -``` -[dependencies] -crossterm_cursor = "0.2" -``` -Import the `crossterm_cursor` modules you want to use. - -```rust -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 -- Multithreaded (send, sync) -- Detailed Documentation -- Few Dependencies -- Cursor - - Moving _n_ times (up, down, left, right) - - Position (set/get) - - Store cursor position and resetting to that later - - Hiding/Showing - - Blinking Cursor (only some terminals are supporting this) - -## Command API - -My first recommendation is to use the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) because this might replace some of the existing API in the future. -Because it is more convenient, faster, and easier to use. - -## Examples -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -```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.restore_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. - -## Authors - -* **Timon Post** - *Project Owner & creator* - -## License -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_cursor/src/cursor.rs b/crossterm_cursor/src/cursor.rs deleted file mode 100644 index 56d41bf..0000000 --- a/crossterm_cursor/src/cursor.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! 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 crossterm_utils::Result; - -pub use self::cursor::{ - cursor, BlinkOff, BlinkOn, Down, Goto, Hide, Left, ResetPos, Right, SavePos, Show, - TerminalCursor, Up, -}; - -mod cursor; - -mod ansi_cursor; -#[cfg(windows)] -mod winapi_cursor; - -///! 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 -///! the wishes to work on a specific platform. -///! -///! ## For example: -///! -///! This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), -///! so that cursor related actions can be performed on both UNIX and Windows systems. -trait ITerminalCursor: Sync + Send { - /// Goto location (`x`, `y`) in the current terminal window. - fn goto(&self, x: u16, y: u16) -> Result<()>; - /// Get the cursor location `(x, y)` in the current terminal window. - fn pos(&self) -> Result<(u16, u16)>; - /// Move cursor `n` times up - fn move_up(&self, count: u16) -> Result<()>; - /// Move the cursor `n` times to the right. - fn move_right(&self, count: u16) -> Result<()>; - /// Move the cursor `n` times down. - fn move_down(&self, count: u16) -> Result<()>; - /// Move the cursor `n` times left. - fn move_left(&self, count: u16) -> Result<()>; - /// Save cursor position so that its saved position can be recalled later. Note that this position - /// is stored program based not per instance of the cursor struct. - fn save_position(&self) -> Result<()>; - /// Return to saved cursor position - fn restore_position(&self) -> Result<()>; - /// Hide the terminal cursor. - fn hide(&self) -> Result<()>; - /// Show the terminal cursor - fn show(&self) -> Result<()>; - /// Enable or disable the blinking of the cursor. - fn blink(&self, blink: bool) -> Result<()>; -} diff --git a/crossterm_cursor/src/cursor/ansi_cursor.rs b/crossterm_cursor/src/cursor/ansi_cursor.rs deleted file mode 100644 index 68b7c54..0000000 --- a/crossterm_cursor/src/cursor/ansi_cursor.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! This is an ANSI specific implementation for cursor related action. -//! 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 crossterm_utils::{csi, write_cout, Result}; - -use crate::sys::{get_cursor_position, show_cursor}; - -use super::ITerminalCursor; - -pub fn get_goto_ansi(x: u16, y: u16) -> String { - format!(csi!("{};{}H"), y + 1, x + 1) -} - -pub fn get_move_up_ansi(count: u16) -> String { - format!(csi!("{}A"), count) -} - -pub fn get_move_right_ansi(count: u16) -> String { - format!(csi!("{}C"), count) -} - -pub fn get_move_down_ansi(count: u16) -> String { - format!(csi!("{}B"), count) -} - -pub fn get_move_left_ansi(count: u16) -> String { - format!(csi!("{}D"), count) -} - -pub static SAVE_POS_ANSI: &'static str = csi!("s"); -pub static RESTORE_POS_ANSI: &'static str = csi!("u"); -pub static HIDE_ANSI: &'static str = csi!("?25l"); -pub static SHOW_ANSI: &'static str = csi!("?25h"); -pub static BLINK_ON_ANSI: &'static str = csi!("?12h"); -pub static BLINK_OFF_ANSI: &'static str = csi!("?12l"); - -/// This struct is an ANSI implementation for cursor related actions. -pub struct AnsiCursor; - -impl AnsiCursor { - pub fn new() -> AnsiCursor { - AnsiCursor - } -} - -impl ITerminalCursor for AnsiCursor { - fn goto(&self, x: u16, y: u16) -> Result<()> { - write_cout!(get_goto_ansi(x, y))?; - Ok(()) - } - - fn pos(&self) -> Result<(u16, u16)> { - get_cursor_position() - } - - fn move_up(&self, count: u16) -> Result<()> { - write_cout!(get_move_up_ansi(count))?; - Ok(()) - } - - fn move_right(&self, count: u16) -> Result<()> { - write_cout!(get_move_right_ansi(count))?; - Ok(()) - } - - fn move_down(&self, count: u16) -> Result<()> { - write_cout!(get_move_down_ansi(count))?; - Ok(()) - } - - fn move_left(&self, count: u16) -> Result<()> { - write_cout!(get_move_left_ansi(count))?; - Ok(()) - } - - fn save_position(&self) -> Result<()> { - write_cout!(SAVE_POS_ANSI)?; - Ok(()) - } - - fn restore_position(&self) -> Result<()> { - write_cout!(RESTORE_POS_ANSI)?; - Ok(()) - } - - fn hide(&self) -> Result<()> { - show_cursor(false)?; - Ok(()) - } - - fn show(&self) -> Result<()> { - show_cursor(true)?; - Ok(()) - } - - fn blink(&self, blink: bool) -> Result<()> { - if blink { - write_cout!(BLINK_ON_ANSI)?; - } else { - write_cout!(BLINK_OFF_ANSI)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::{AnsiCursor, ITerminalCursor}; - - // TODO - Test is ingored, because it's stalled on Travis CI - #[test] - #[ignore] - fn test_ansi_save_restore_position() { - if try_enable_ansi() { - let cursor = AnsiCursor::new(); - - let (saved_x, saved_y) = cursor.pos().unwrap(); - - cursor.save_position().unwrap(); - cursor.goto(saved_x + 1, saved_y + 1).unwrap(); - cursor.restore_position().unwrap(); - - let (x, y) = cursor.pos().unwrap(); - - assert_eq!(x, saved_x); - assert_eq!(y, saved_y); - } - } - - // TODO - Test is ingored, because it's stalled on Travis CI - #[test] - #[ignore] - fn test_ansi_goto() { - if try_enable_ansi() { - let cursor = AnsiCursor::new(); - - let (saved_x, saved_y) = cursor.pos().unwrap(); - - cursor.goto(saved_x + 1, saved_y + 1).unwrap(); - assert_eq!(cursor.pos().unwrap(), (saved_x + 1, saved_y + 1)); - - cursor.goto(saved_x, saved_y).unwrap(); - assert_eq!(cursor.pos().unwrap(), (saved_x, saved_y)); - } - } - - fn try_enable_ansi() -> bool { - #[cfg(windows)] - { - if cfg!(target_os = "windows") { - use crossterm_utils::sys::winapi::ansi::set_virtual_terminal_processing; - - // 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(_) => return false, - } - } - } - - true - } -} diff --git a/crossterm_cursor/src/cursor/cursor.rs b/crossterm_cursor/src/cursor/cursor.rs deleted file mode 100644 index 16cd047..0000000 --- a/crossterm_cursor/src/cursor/cursor.rs +++ /dev/null @@ -1,342 +0,0 @@ -//! 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. - -#[cfg(windows)] -use crossterm_utils::supports_ansi; -use crossterm_utils::{impl_display, Command, Result}; - -use super::ansi_cursor::{self, AnsiCursor}; -#[cfg(windows)] -use super::winapi_cursor::WinApiCursor; -use super::ITerminalCursor; - -/// 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 and 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. -pub struct TerminalCursor { - #[cfg(windows)] - cursor: Box<(dyn ITerminalCursor + Sync + Send)>, - #[cfg(unix)] - cursor: AnsiCursor, -} - -impl TerminalCursor { - /// Create new `TerminalCursor` instance whereon cursor related actions can be performed. - pub fn new() -> TerminalCursor { - #[cfg(windows)] - let cursor = if supports_ansi() { - Box::from(AnsiCursor::new()) as Box<(dyn ITerminalCursor + Sync + Send)> - } else { - WinApiCursor::new() as Box<(dyn ITerminalCursor + Sync + Send)> - }; - - #[cfg(unix)] - let cursor = AnsiCursor::new(); - - TerminalCursor { cursor } - } - - /// Goto some position (x,y) in the terminal. - /// - /// # Remarks - /// position is 0-based, which means we start counting at 0. - pub fn goto(&self, x: u16, y: u16) -> Result<()> { - self.cursor.goto(x, y) - } - - /// Get current cursor position (x,y) in the terminal. - /// - /// # Remarks - /// position is 0-based, which means we start counting at 0. - pub fn pos(&self) -> Result<(u16, u16)> { - self.cursor.pos() - } - - /// Move the current cursor position `n` times up. - pub fn move_up(&mut self, count: u16) -> Result<&mut TerminalCursor> { - self.cursor.move_up(count)?; - Ok(self) - } - - /// Move the current cursor position `n` times right. - pub fn move_right(&mut self, count: u16) -> Result<&mut TerminalCursor> { - self.cursor.move_right(count)?; - Ok(self) - } - - /// Move the current cursor position `n` times down. - pub fn move_down(&mut self, count: u16) -> Result<&mut TerminalCursor> { - self.cursor.move_down(count)?; - Ok(self) - } - - /// Move the current cursor position `n` times left. - pub fn move_left(&mut self, count: u16) -> Result<&mut TerminalCursor> { - self.cursor.move_left(count)?; - Ok(self) - } - - /// Save cursor position for recall later. - /// - /// Note that this position is stored program based not per instance of the `Cursor` struct. - pub fn save_position(&self) -> Result<()> { - self.cursor.save_position() - } - - /// Return to saved cursor position - pub fn restore_position(&self) -> Result<()> { - self.cursor.restore_position() - } - - /// Hide de cursor in the console. - pub fn hide(&self) -> Result<()> { - self.cursor.hide() - } - - /// Show the cursor in the console. - pub fn show(&self) -> Result<()> { - self.cursor.show() - } - - /// 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. - pub fn blink(&self, blink: bool) -> Result<()> { - self.cursor.blink(blink) - } -} - -/// Get a `TerminalCursor` instance whereon cursor related actions can be performed. -pub fn cursor() -> TerminalCursor { - TerminalCursor::new() -} - -/// When executed, this command will move the cursor position to the given `x` and `y` in the terminal window. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Goto(pub u16, pub u16); - -impl Command for Goto { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::get_goto_ansi(self.0, self.1) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().goto(self.0, self.1) - } -} - -/// When executed, this command will move the current cursor position `n` times up. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Up(pub u16); - -impl Command for Up { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::get_move_up_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().move_up(self.0) - } -} - -/// When executed, this command will move the current cursor position `n` times down. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Down(pub u16); - -impl Command for Down { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::get_move_down_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().move_down(self.0) - } -} - -/// When executed, this command will move the current cursor position `n` times left. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Left(pub u16); - -impl Command for Left { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::get_move_left_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().move_left(self.0) - } -} - -/// When executed, this command will move the current cursor position `n` times right. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Right(pub u16); - -impl Command for Right { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::get_move_right_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().move_right(self.0) - } -} - -/// When executed, this command will save the cursor position for recall later. -/// -/// Note that this position is stored program based not per instance of the `Cursor` struct. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SavePos; - -impl Command for SavePos { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::SAVE_POS_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().save_position() - } -} - -/// When executed, this command will return the cursor position to the saved cursor position -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct ResetPos; - -impl Command for ResetPos { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::RESTORE_POS_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().restore_position() - } -} - -/// When executed, this command will hide de cursor in the console. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Hide; - -impl Command for Hide { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::HIDE_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().hide() - } -} - -/// When executed, this command will show de cursor in the console. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Show; - -impl Command for Show { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::SHOW_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiCursor::new().show() - } -} - -/// When executed, this command will enable cursor blinking. -/// -/// # Remarks -/// Not all terminals are supporting this functionality. Windows versions lower than windows 10 also are not supporting this version. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct BlinkOn; - -impl Command for BlinkOn { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::BLINK_ON_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -/// When executed, this command will disable cursor blinking. -/// -/// # Remarks -/// Not all terminals are supporting this functionality. Windows versions lower than windows 10 also are not supporting this version. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct BlinkOff; - -impl Command for BlinkOff { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_cursor::BLINK_OFF_ANSI - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - Ok(()) - } -} - -impl_display!(for Goto); -impl_display!(for Up); -impl_display!(for Down); -impl_display!(for Left); -impl_display!(for Right); -impl_display!(for SavePos); -impl_display!(for ResetPos); -impl_display!(for Hide); -impl_display!(for Show); -impl_display!(for BlinkOn); -impl_display!(for BlinkOff); diff --git a/crossterm_cursor/src/cursor/winapi_cursor.rs b/crossterm_cursor/src/cursor/winapi_cursor.rs deleted file mode 100644 index 4b649e5..0000000 --- a/crossterm_cursor/src/cursor/winapi_cursor.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! This is a WINAPI specific implementation for cursor related actions. -//! 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 crossterm_utils::Result; - -use crate::sys::winapi::{Cursor, Handle}; - -use super::ITerminalCursor; - -/// This struct is a windows implementation for cursor related actions. -pub struct WinApiCursor; - -impl WinApiCursor { - pub fn new() -> Box { - Box::from(WinApiCursor) - } -} - -impl ITerminalCursor for WinApiCursor { - fn goto(&self, x: u16, y: u16) -> Result<()> { - let cursor = Cursor::new()?; - cursor.goto(x as i16, y as i16)?; - Ok(()) - } - - fn pos(&self) -> Result<(u16, u16)> { - let cursor = Cursor::new()?; - Ok(cursor.position()?.into()) - } - - fn move_up(&self, count: u16) -> Result<()> { - let (xpos, ypos) = self.pos()?; - self.goto(xpos, ypos - count)?; - Ok(()) - } - - fn move_right(&self, count: u16) -> Result<()> { - let (xpos, ypos) = self.pos()?; - self.goto(xpos + count, ypos)?; - Ok(()) - } - - fn move_down(&self, count: u16) -> Result<()> { - let (xpos, ypos) = self.pos()?; - self.goto(xpos, ypos + count)?; - Ok(()) - } - - fn move_left(&self, count: u16) -> Result<()> { - let (xpos, ypos) = self.pos()?; - self.goto(xpos - count, ypos)?; - Ok(()) - } - - fn save_position(&self) -> Result<()> { - Cursor::save_cursor_pos()?; - Ok(()) - } - - fn restore_position(&self) -> Result<()> { - Cursor::restore_cursor_pos()?; - Ok(()) - } - - fn hide(&self) -> Result<()> { - Cursor::from(Handle::current_out_handle()?).set_visibility(false)?; - Ok(()) - } - - fn show(&self) -> Result<()> { - Cursor::from(Handle::current_out_handle()?).set_visibility(true)?; - Ok(()) - } - - fn blink(&self, _blink: bool) -> Result<()> { - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::{ITerminalCursor, WinApiCursor}; - - #[test] - fn test_winapi_goto() { - let cursor = WinApiCursor::new(); - - let (saved_x, saved_y) = cursor.pos().unwrap(); - - cursor.goto(saved_x + 1, saved_y + 1).unwrap(); - assert_eq!(cursor.pos().unwrap(), (saved_x + 1, saved_y + 1)); - - cursor.goto(saved_x, saved_y).unwrap(); - assert_eq!(cursor.pos().unwrap(), (saved_x, saved_y)); - } - - #[test] - fn test_winapi_save_and_restore() { - let cursor = WinApiCursor::new(); - - let (saved_x, saved_y) = cursor.pos().unwrap(); - - cursor.save_position().unwrap(); - cursor.goto(saved_x + 1, saved_y + 1).unwrap(); - cursor.restore_position().unwrap(); - - let (x, y) = cursor.pos().unwrap(); - - assert_eq!(x, saved_x); - assert_eq!(y, saved_y); - } -} diff --git a/crossterm_cursor/src/lib.rs b/crossterm_cursor/src/lib.rs deleted file mode 100644 index a267434..0000000 --- a/crossterm_cursor/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![deny(unused_imports)] - -pub use crossterm_utils::{ - execute, queue, Command, ErrorKind, ExecutableCommand, Output, QueueableCommand, Result, -}; - -pub use self::cursor::{ - cursor, BlinkOff, BlinkOn, Down, Goto, Hide, Left, ResetPos, Right, SavePos, Show, - TerminalCursor, Up, -}; - -mod cursor; -pub mod sys; diff --git a/crossterm_cursor/src/sys.rs b/crossterm_cursor/src/sys.rs deleted file mode 100644 index 91bd3be..0000000 --- a/crossterm_cursor/src/sys.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[cfg(unix)] -pub use self::unix::get_cursor_position; -#[cfg(unix)] -pub use self::unix::show_cursor; -#[cfg(windows)] -pub use self::winapi::get_cursor_position; -#[cfg(windows)] -pub use self::winapi::show_cursor; - -#[cfg(unix)] -pub mod unix; - -#[cfg(windows)] -pub mod winapi; diff --git a/crossterm_cursor/src/sys/unix.rs b/crossterm_cursor/src/sys/unix.rs deleted file mode 100644 index 672f748..0000000 --- a/crossterm_cursor/src/sys/unix.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::io::{self, BufRead, Write}; - -use crossterm_utils::{ - csi, - sys::unix::{self, RAW_MODE_ENABLED}, - write_cout, Result, -}; - -#[cfg(unix)] -pub fn get_cursor_position() -> Result<(u16, u16)> { - if unsafe { RAW_MODE_ENABLED } { - pos_raw() - } else { - pos() - } -} - -#[cfg(unix)] -pub fn show_cursor(show_cursor: bool) -> Result<()> { - if show_cursor { - write_cout!(csi!("?25h"))?; - } else { - write_cout!(csi!("?25l"))?; - } - Ok(()) -} - -pub fn pos() -> Result<(u16, u16)> { - unix::enable_raw_mode()?; - let pos = pos_raw(); - unix::disable_raw_mode()?; - pos -} - -pub fn pos_raw() -> Result<(u16, u16)> { - // Where is the cursor? - // Use `ESC [ 6 n`. - let mut stdout = io::stdout(); - let stdin = io::stdin(); - - // Write command - stdout.write_all(b"\x1B[6n")?; - stdout.flush()?; - - stdin.lock().read_until(b'[', &mut vec![])?; - - let mut rows = vec![]; - stdin.lock().read_until(b';', &mut rows)?; - - let mut cols = vec![]; - stdin.lock().read_until(b'R', &mut cols)?; - - // remove delimiter - rows.pop(); - cols.pop(); - - let rows = String::from_utf8(rows)?.parse::()?; - let cols = String::from_utf8(cols)?.parse::()?; - - Ok((cols - 1, rows - 1)) -} diff --git a/crossterm_cursor/src/sys/winapi.rs b/crossterm_cursor/src/sys/winapi.rs deleted file mode 100644 index 85ca541..0000000 --- a/crossterm_cursor/src/sys/winapi.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! This module handles some logic for cursor interaction in the windows console. - -use std::io; - -use winapi::{ - shared::minwindef::{FALSE, TRUE}, - um::wincon::{SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD}, - um::winnt::HANDLE, -}; - -use crossterm_utils::Result; -pub use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer}; - -#[cfg(windows)] -pub fn get_cursor_position() -> Result<(u16, u16)> { - let cursor = Cursor::new()?; - Ok(cursor.position()?.into()) -} - -#[cfg(windows)] -pub fn show_cursor(show_cursor: bool) -> Result<()> { - Cursor::from(Handle::current_out_handle()?).set_visibility(show_cursor) -} - -/// This stores the cursor pos, at program level. So it can be recalled later. -static mut SAVED_CURSOR_POS: (u16, u16) = (0, 0); - -pub struct Cursor { - screen_buffer: ScreenBuffer, -} - -impl Cursor { - pub fn new() -> Result { - Ok(Cursor { - screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?), - }) - } - - /// get the current cursor position. - pub fn position(&self) -> Result { - Ok(self.screen_buffer.info()?.cursor_pos()) - } - - /// Set the cursor position to the given x and y. Note that this is 0 based. - pub fn goto(&self, x: i16, y: i16) -> Result<()> { - if x < 0 || x >= ::max_value() { - Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Argument Out of Range Exception when setting cursor position to X: {}", - x - ), - ))?; - } - - if y < 0 || y >= ::max_value() { - Err(io::Error::new( - io::ErrorKind::Other, - format!( - "Argument Out of Range Exception when setting cursor position to Y: {}", - y - ), - ))?; - } - - let position = COORD { X: x, Y: y }; - - unsafe { - if !is_true(SetConsoleCursorPosition( - **self.screen_buffer.handle(), - position, - )) { - Err(io::Error::last_os_error())?; - } - } - Ok(()) - } - - /// change the cursor visibility. - pub fn set_visibility(&self, visable: bool) -> Result<()> { - let cursor_info = CONSOLE_CURSOR_INFO { - dwSize: 100, - bVisible: if visable { TRUE } else { FALSE }, - }; - - unsafe { - if !is_true(SetConsoleCursorInfo( - **self.screen_buffer.handle(), - &cursor_info, - )) { - Err(io::Error::last_os_error())?; - } - } - Ok(()) - } - - /// Reset to saved cursor position - pub fn restore_cursor_pos() -> Result<()> { - let cursor = Cursor::new()?; - - unsafe { - cursor.goto(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16)?; - } - - Ok(()) - } - - /// Save current cursor position to recall later. - pub fn save_cursor_pos() -> Result<()> { - let cursor = Cursor::new()?; - let position = cursor.position()?; - - unsafe { - SAVED_CURSOR_POS = (position.x as u16, position.y as u16); - } - - Ok(()) - } -} - -impl From for Cursor { - fn from(handle: Handle) -> Self { - Cursor { - screen_buffer: ScreenBuffer::from(handle), - } - } -} - -impl From for Cursor { - fn from(handle: HANDLE) -> Self { - Cursor { - screen_buffer: ScreenBuffer::from(handle), - } - } -} diff --git a/crossterm_input/.gitignore b/crossterm_input/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_input/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_input/CHANGELOG.md b/crossterm_input/CHANGELOG.md deleted file mode 100644 index 0dcb44e..0000000 --- a/crossterm_input/CHANGELOG.md +++ /dev/null @@ -1,39 +0,0 @@ -# Changes crossterm_input 0.4.0 -- `TerminalInput::read_line` returns `crossterm::Result` instead of `io::Result` -- `TerminalInput::read_char` returns `crossterm::Result` instead of `io::Result` -- `Command::get_anis_code()` to `ansi_code()` -- Added KeyEvent::Enter and KeyEvent::Tab: [added-key-event-enter], [added-key-event-tab] -- `ExecutableCommand::queue` returns `crossterm::Result` -- `QueueableCommand::queue` returns `crossterm::Result` -- Added derives: Serialize/Deserialize for key events [serde] -- Command API takes mutable self instead of self - -[added-key-event-tab]: https://github.com/crossterm-rs/crossterm/pull/239 -[added-key-event-enter]: https://github.com/crossterm-rs/crossterm/pull/236 -[serde]: https://github.com/crossterm-rs/crossterm/pull/190 - -# Changes crossterm_input 0.3.3 -- Removed println from `SyncReader` - -# Changes crossterm_input 0.3.2 -- Fixed some special key combination detections for UNIX systems -- Windows mouse input event position was 0-based and should be 1-based - -# Changes crossterm_input 0.3.1 -- Updated crossterm_utils - -# Changes crossterm_input 0.3 -- Removed `TerminalInput::from_output()` - -# Changes crossterm_input 0.2.1 -- Fixed SyncReade bug. - -# Changes crossterm_input 0.2.1 -- Introduced SyncReader - -# Changes crossterm_input 0.2 -- Introduced KeyEvents -- Introduced MouseEvents - -# Changes crossterm_input 0.1 -- Moved out of `crossterm` 5.4 crate. \ No newline at end of file diff --git a/crossterm_input/Cargo.toml b/crossterm_input/Cargo.toml deleted file mode 100644 index 2b0c68e..0000000 --- a/crossterm_input/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "crossterm_input" -version = "0.4.0" -authors = ["T. Post"] -description = "A cross-platform library for reading userinput." -repository = "https://github.com/crossterm-rs/crossterm" -documentation = "https://docs.rs/crossterm_input/" -license = "MIT" -keywords = ["input", "keys", "crossterm", "events", "terminal"] -exclude = ["target", "Cargo.lock"] -readme = "README.md" -edition = "2018" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.8", features = ["winnt", "winuser"] } -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0"} - -[target.'cfg(unix)'.dependencies] -libc = "0.2.51" - -[dependencies] -crossterm_utils = { path="../crossterm_utils", version = "0.3.0"} -crossterm_screen = {path="../crossterm_screen", version = "0.3.0"} -serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crossterm_input/LICENSE b/crossterm_input/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_input/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_input/README.md b/crossterm_input/README.md deleted file mode 100644 index 3966641..0000000 --- a/crossterm_input/README.md +++ /dev/null @@ -1,151 +0,0 @@ -# Crossterm Input | cross-platform input reading . - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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 used 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://crossterm-rs.github.io/crossterm/docs/feature_flags.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 - -All examples of how `crossterm_input` works can be found in the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) directory. - -Add the `crossterm_input` package to your `Cargo.toml` file. - -``` -[dependencies] -crossterm_input = "0.3" -``` -Import the `crossterm_input` modules you want to use. - -```rust -pub use crossterm_input::{input, AsyncReader, InputEvent, KeyEvent, MouseButton, MouseEvent, SyncReader, TerminalInput}; -``` - -### Useful Links - -- [Documentation](https://docs.rs/crossterm_input/) -- [Crates.io](https://crates.io/crates/crossterm_input) -- [Book](https://crossterm-rs.github.io/crossterm/docs/input.html) -- [Examples](./examples) - -## Features -These are the features of this crate: - -- Cross-platform -- Multithreaded (send, sync) -- Detailed Documentation -- Few Dependencies -- Input - - Read character - - Read line - - Read key input events (async / sync) - - Read mouse input events (press, release, position, button) - - RawScreen (from `crossterm_screen`) - -## Examples -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -_Simple Readings_ -```rust -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), - } -``` - -_Read input events synchronously or asynchronously._ -```rust -// make sure to enable raw mode, this will make sure key events won't be handled by the terminal it's self and allows crossterm to read the input and pass it back to you. -let screen = RawScreen::into_raw_mode(); - -let mut input = input(); - -// either read the input synchronously -let stdin = input.read_sync(); - -// or asynchronously -let stdin = input.read_async(); - -if let Some(key_event) = stdin.next() { - match key_event { - InputEvent::Keyboard(event: KeyEvent) => match event { /* check key event */ } - InputEvent::Mouse(event: MouseEvent) => match event { /* check mouse event */ } - } - } -``` - -_Enable mouse input events._ -```rust -let input = input(); - -// enable mouse events to be captured. -input.enable_mouse_mode().unwrap(); - -// disable mouse events to be captured. -input.disable_mouse_mode().unwrap(); -``` - -## 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. - -## Authors - -* **Timon Post** - *Project Owner & creator* -* **Dave Ho** - *Contributor* - -## License - -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_input/src/input.rs b/crossterm_input/src/input.rs deleted file mode 100644 index 4f7cf38..0000000 --- a/crossterm_input/src/input.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! A module that contains all the actions related to reading input from the terminal. -//! Like reading a line, reading a character and reading asynchronously. - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crossterm_utils::Result; - -pub use self::input::{input, TerminalInput}; -#[cfg(unix)] -pub use self::unix_input::{AsyncReader, SyncReader}; -#[cfg(windows)] -pub use self::windows_input::{AsyncReader, SyncReader}; - -mod input; - -#[cfg(unix)] -mod unix_input; -#[cfg(windows)] -mod windows_input; - -/// This trait defines the actions that can be performed with the terminal input. -/// This trait can be implemented so that a concrete implementation of the ITerminalInput can fulfill -/// the wishes to work on a specific platform. -/// -/// ## For example: -/// -/// This trait is implemented for Windows and UNIX systems. -/// Unix is using the 'TTY' and windows is using 'libc' C functions to read the input. -trait ITerminalInput { - /// Read one character from the user input - fn read_char(&self) -> Result; - /// Read the input asynchronously from the user. - fn read_async(&self) -> AsyncReader; - /// Read the input asynchronously until a certain character is hit. - fn read_until_async(&self, delimiter: u8) -> AsyncReader; - /// Read the input synchronously from the user. - fn read_sync(&self) -> SyncReader; - fn enable_mouse_mode(&self) -> Result<()>; - fn disable_mouse_mode(&self) -> Result<()>; -} - -/// Enum to specify which input event has occurred. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone)] -pub enum InputEvent { - /// A single key or a combination is pressed. - Keyboard(KeyEvent), - /// A mouse event occurred. - Mouse(MouseEvent), - /// A unsupported event has occurred. - Unsupported(Vec), - /// An unknown event has occurred. - Unknown, -} - -/// Enum to specify which mouse event has occurred. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] -pub enum MouseEvent { - /// A mouse press has occurred, this contains the pressed button and the position of the press. - Press(MouseButton, u16, u16), - /// A mouse button was released. - Release(u16, u16), - /// A mouse button was hold. - Hold(u16, u16), - /// An unknown mouse event has occurred. - Unknown, -} - -/// Enum to define mouse buttons. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, PartialOrd, PartialEq, Hash, Clone, Copy)] -pub enum MouseButton { - /// Left mouse button - Left, - /// Right mouse button - Right, - /// Middle mouse button - Middle, - /// Scroll up - WheelUp, - /// Scroll down - WheelDown, -} - -/// Enum with different key or key combinations. -#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum KeyEvent { - Backspace, - Enter, - Left, - Right, - Up, - Down, - Home, - End, - PageUp, - PageDown, - Tab, - BackTab, - Delete, - Insert, - F(u8), - Char(char), - Alt(char), - Ctrl(char), - Null, - Esc, - CtrlUp, - CtrlDown, - CtrlRight, - CtrlLeft, - ShiftUp, - ShiftDown, - ShiftRight, - ShiftLeft, -} diff --git a/crossterm_input/src/input/input.rs b/crossterm_input/src/input/input.rs deleted file mode 100644 index aa027c4..0000000 --- a/crossterm_input/src/input/input.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! A module that contains all the actions related to reading input from the terminal. -//! Like reading a line, reading a character and reading asynchronously. - -use std::io; - -use crossterm_utils::Result; - -#[cfg(unix)] -use super::unix_input::{AsyncReader, SyncReader, UnixInput}; -#[cfg(windows)] -use super::windows_input::{AsyncReader, SyncReader, WindowsInput}; -use super::ITerminalInput; - -/// Allows you to read user input. -/// -/// # Features: -/// -/// - Read character -/// - Read line -/// - Read async -/// - Read async until -/// - Read sync -/// - Wait for key event (terminal pause) -/// -/// Check `/examples/` in the library for more specific examples. -pub struct TerminalInput { - #[cfg(windows)] - input: WindowsInput, - #[cfg(unix)] - input: UnixInput, -} - -impl TerminalInput { - /// Create a new instance of `TerminalInput` whereon input related actions could be performed. - pub fn new() -> TerminalInput { - #[cfg(windows)] - let input = WindowsInput::new(); - - #[cfg(unix)] - let input = UnixInput::new(); - - TerminalInput { input } - } - - /// Read one line from the user input. - /// - /// # 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`, `read_async_until` or `read_sync`. - /// Not sure what 'raw mode' is, checkout the 'crossterm_screen' crate. - /// - /// # Example - /// ```ignore - /// let in = input(); - /// match in.read_line() { - /// Ok(s) => println!("string typed: {}", s), - /// Err(e) => println!("error: {}", e), - /// } - /// ``` - pub fn read_line(&self) -> Result { - let mut rv = String::new(); - io::stdin().read_line(&mut rv)?; - let len = rv.trim_end_matches(&['\r', '\n'][..]).len(); - rv.truncate(len); - Ok(rv) - } - - /// Read one character from the user input - /// - /// ```ignore - /// let in = input(); - /// match in.read_char() { - /// Ok(c) => println!("character pressed: {}", c), - /// Err(e) => println!("error: {}", e), - /// } - /// ``` - pub fn read_char(&self) -> Result { - self.input.read_char() - } - - /// Read the input asynchronously, which means that input events are gathered on the background and will be queued for you to read. - /// - /// If you want a blocking, or less resource consuming read to happen use `read_sync()`, this will leave a way all the thread and queueing and will be a blocking read. - /// - /// This is the same as `read_async()` but stops reading when a certain character is hit. - /// - /// # Remarks - /// - Readings won't be blocking calls. - /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi - /// `ReadConsoleW` will be used. - /// - Input events read from the user will be queued on a MPSC-channel. - /// - The reading thread will be cleaned up when it drops. - /// - Requires 'raw screen to be enabled'. - /// Not sure what this is? Please checkout the 'crossterm_screen' crate. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_async(&self) -> AsyncReader { - self.input.read_async() - } - - /// Read the input asynchronously until a certain delimiter (character as byte) is hit, which means that input events are gathered on the background and will be queued for you to read. - /// - /// If you want a blocking or less resource consuming read to happen, use `read_sync()`. This will leave alone the background thread and queues and will be a blocking read. - /// - /// This is the same as `read_async()` but stops reading when a certain character is hit. - /// - /// # Remarks - /// - Readings won't be blocking calls. - /// A thread will be fired to read input, on unix systems from TTY and on windows WinApi - /// `ReadConsoleW` will be used. - /// - Input events read from the user will be queued on a MPSC-channel. - /// - The reading thread will be cleaned up when it drops. - /// - Requires 'raw screen to be enabled'. - /// Not sure what this is? Please checkout the 'crossterm_screen' crate. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_until_async(&self, delimiter: u8) -> AsyncReader { - self.input.read_until_async(delimiter) - } - - /// Read the input synchronously from the user, which means that reading calls will block. - /// It also uses less resources than the `AsyncReader` because the background thread and queues are left alone. - /// - /// Consider using `read_async` if you don't want the reading call to block your program. - /// - /// # Remark - /// - Readings will be blocking calls. - /// - /// # Examples - /// Please checkout the example folder in the repository. - pub fn read_sync(&self) -> SyncReader { - self.input.read_sync() - } - - /// Enable mouse events to be captured. - /// - /// When enabling mouse input, you will be able to capture mouse movements, pressed buttons, and locations. - /// - /// # Remark - /// - Mouse events will be send over the reader created with `read_async`, `read_async_until`, `read_sync`. - pub fn enable_mouse_mode(&self) -> Result<()> { - self.input.enable_mouse_mode() - } - - /// Disable mouse events to be captured. - /// - /// When disabling mouse input, you won't be able to capture mouse movements, pressed buttons, and locations anymore. - pub fn disable_mouse_mode(&self) -> Result<()> { - self.input.disable_mouse_mode() - } -} - -/// Get a `TerminalInput` instance whereon input related actions can be performed. -pub fn input() -> TerminalInput { - TerminalInput::new() -} diff --git a/crossterm_input/src/input/unix_input.rs b/crossterm_input/src/input/unix_input.rs deleted file mode 100644 index 7b54e89..0000000 --- a/crossterm_input/src/input/unix_input.rs +++ /dev/null @@ -1,506 +0,0 @@ -//! This is a UNIX specific implementation for input related action. - -use std::char; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{self, Receiver, Sender}, - Arc, -}; -use std::{ - io::{self, Read}, - str, thread, -}; - -use crossterm_utils::{csi, write_cout, ErrorKind, Result}; - -use crate::sys::unix::{get_tty, read_char_raw}; - -use super::{ITerminalInput, InputEvent, KeyEvent, MouseButton, MouseEvent}; - -pub struct UnixInput; - -impl UnixInput { - pub fn new() -> UnixInput { - UnixInput {} - } -} - -impl ITerminalInput for UnixInput { - fn read_char(&self) -> Result { - read_char_raw() - } - - fn read_async(&self) -> AsyncReader { - AsyncReader::new(Box::new(move |event_tx, cancellation_token| { - for i in get_tty().unwrap().bytes() { - if event_tx.send(i.unwrap()).is_err() { - return; - } - - if cancellation_token.load(Ordering::SeqCst) { - return; - } - } - })) - } - - fn read_until_async(&self, delimiter: u8) -> AsyncReader { - AsyncReader::new(Box::new(move |event_tx, cancellation_token| { - for byte in get_tty().unwrap().bytes() { - let byte = byte.unwrap(); - let end_of_stream = byte == delimiter; - let send_error = event_tx.send(byte).is_err(); - - if end_of_stream || send_error || cancellation_token.load(Ordering::SeqCst) { - return; - } - } - })) - } - - fn read_sync(&self) -> SyncReader { - SyncReader { - source: Box::from(get_tty().unwrap()), - leftover: None, - } - } - - fn enable_mouse_mode(&self) -> Result<()> { - write_cout!(&format!( - "{}h{}h{}h{}h", - csi!("?1000"), - csi!("?1002"), - csi!("?1015"), - csi!("?1006") - ))?; - Ok(()) - } - - fn disable_mouse_mode(&self) -> Result<()> { - write_cout!(&format!( - "{}l{}l{}l{}l", - csi!("?1006"), - csi!("?1015"), - csi!("?1002"), - csi!("?1000") - ))?; - Ok(()) - } -} - -/// This type allows you to read the input asynchronously which means that input events are gathered on the background and will be queued for you to read. -/// -/// **[SyncReader](./LINK)** -/// If you want a blocking, or less resource consuming read to happen use `SyncReader`, this will leave a way all the thread and queueing and will be a blocking read. -/// -/// This type is an iterator, and could be used to iterate over input events. -/// -/// # Remarks -/// - Threads spawned will be disposed of as soon the `AsyncReader` goes out of scope. -/// - MPSC-channels are used to queue input events, this type implements an iterator of the rx side of the queue. -pub struct AsyncReader { - event_rx: Receiver, - shutdown: Arc, -} - -impl AsyncReader { - /// Construct a new instance of the `AsyncReader`. - /// The reading will immediately start when calling this function. - pub fn new(function: Box, &Arc) + Send>) -> AsyncReader { - let shutdown_handle = Arc::new(AtomicBool::new(false)); - - let (event_tx, event_rx) = mpsc::channel(); - let thread_shutdown = shutdown_handle.clone(); - - thread::spawn(move || loop { - function(&event_tx, &thread_shutdown); - }); - - AsyncReader { - event_rx, - shutdown: shutdown_handle, - } - } - - /// Stop the input event reading. - /// - /// You don't necessarily have to call this function because it will automatically be called when this reader goes out of scope. - /// - /// # Remarks - /// - Background thread will be closed. - /// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead. - pub fn stop(&mut self) { - self.shutdown.store(true, Ordering::SeqCst); - } -} - -impl Iterator for AsyncReader { - type Item = InputEvent; - - /// Check if there are input events to read. - /// - /// It will return `None` when nothing is there to read, `Some(InputEvent)` if there are events to read. - /// - /// # Remark - /// - This is **not** a blocking call. - fn next(&mut self) -> Option { - let mut iterator = self.event_rx.try_iter(); - - match iterator.next() { - Some(char_value) => { - if let Ok(char_value) = parse_event(char_value, &mut iterator) { - Some(char_value) - } else { - None - } - } - None => None, - } - } -} - -impl Drop for AsyncReader { - fn drop(&mut self) { - self.stop(); - } -} - -/// This type allows you to read input synchronously, which means that reading calls will block. -/// -/// This type is an iterator, and can be used to iterate over input events. -/// -/// If you don't want to block your calls use [AsyncReader](./LINK), which will read input on the background and queue it for you to read. -pub struct SyncReader { - source: Box, - leftover: Option, -} - -impl Iterator for SyncReader { - type Item = InputEvent; - /// Read input from the user. - /// - /// If there are no keys pressed, this will be a blocking call until there is one. - /// This will return `None` in case of a failure and `Some(InputEvent)` in case of an occurred input event. - fn next(&mut self) -> Option { - // TODO: Currently errors are consumed and converted to a `None`. Maybe we shouldn't be doing this? - let source = &mut self.source; - - if let Some(c) = self.leftover { - // we have a leftover byte, use it - self.leftover = None; - if let Ok(e) = parse_event(c, &mut source.bytes().flatten()) { - return Some(e); - } else { - return None; - } - } - - // Here we read two bytes at a time. We need to distinguish between single ESC key presses, - // and escape sequences (which start with ESC or a x1B byte). The idea is that if this is - // an escape sequence, we will read multiple bytes (the first byte being ESC) but if this - // is a single ESC keypress, we will only read a single byte. - let mut buf = [0u8; 2]; - - let res = match source.read(&mut buf) { - Ok(0) => return None, - Ok(1) => match buf[0] { - b'\x1B' => return Some(InputEvent::Keyboard(KeyEvent::Esc)), - c => { - if let Ok(e) = parse_event(c, &mut source.bytes().flatten()) { - return Some(e); - } else { - return None; - } - } - }, - Ok(2) => { - let option_iter = &mut Some(buf[1]).into_iter(); - let iter = option_iter.map(|c| Ok(c)).chain(source.bytes()); - if let Ok(e) = parse_event(buf[0], &mut iter.flatten()) { - self.leftover = option_iter.next(); - Some(e) - } else { - None - } - } - Ok(_) => unreachable!(), - Err(_) => return None, /* maybe we should not throw away the error?*/ - }; - - res - } -} - -/// Parse an Event from `item` and possibly subsequent bytes through `iter`. -pub(crate) fn parse_event(item: u8, iter: &mut I) -> Result -where - I: Iterator, -{ - let error = ErrorKind::IoError(io::Error::new( - io::ErrorKind::Other, - "Could not parse an event", - )); - let input_event = match item { - b'\x1B' => { - let a = iter.next(); - // This is an escape character, leading a control sequence. - match a { - Some(b'O') => { - match iter.next() { - // F1-F4 - Some(val @ b'P'..=b'S') => { - InputEvent::Keyboard(KeyEvent::F(1 + val - b'P')) - } - _ => return Err(error), - } - } - Some(b'[') => { - // This is a CSI sequence. - parse_csi(iter) - } - Some(b'\x1B') => InputEvent::Keyboard(KeyEvent::Esc), - Some(c) => { - let ch = parse_utf8_char(c, iter); - InputEvent::Keyboard(KeyEvent::Alt(ch?)) - } - None => InputEvent::Keyboard(KeyEvent::Esc), - } - } - b'\r' | b'\n' => InputEvent::Keyboard(KeyEvent::Enter), - b'\t' => InputEvent::Keyboard(KeyEvent::Tab), - b'\x7F' => InputEvent::Keyboard(KeyEvent::Backspace), - c @ b'\x01'..=b'\x1A' => { - InputEvent::Keyboard(KeyEvent::Ctrl((c as u8 - 0x1 + b'a') as char)) - } - c @ b'\x1C'..=b'\x1F' => { - InputEvent::Keyboard(KeyEvent::Ctrl((c as u8 - 0x1C + b'4') as char)) - } - b'\0' => InputEvent::Keyboard(KeyEvent::Null), - c => { - let ch = parse_utf8_char(c, iter); - InputEvent::Keyboard(KeyEvent::Char(ch?)) - } - }; - - Ok(input_event) -} - -/// Parses a CSI sequence, just after reading ^[ -/// Returns Event::Unknown if an unrecognized sequence is found. -/// Most of this parsing code is been taken over from 'termion`. -fn parse_csi(iter: &mut I) -> InputEvent -where - I: Iterator, -{ - match iter.next() { - Some(b'[') => match iter.next() { - // NOTE (@imdaveho): cannot find when this occurs; - // having another '[' after ESC[ not a likely scenario - Some(val @ b'A'..=b'E') => InputEvent::Keyboard(KeyEvent::F(1 + val - b'A')), - _ => InputEvent::Unknown, - }, - Some(b'D') => InputEvent::Keyboard(KeyEvent::Left), - Some(b'C') => InputEvent::Keyboard(KeyEvent::Right), - Some(b'A') => InputEvent::Keyboard(KeyEvent::Up), - Some(b'B') => InputEvent::Keyboard(KeyEvent::Down), - Some(b'H') => InputEvent::Keyboard(KeyEvent::Home), - Some(b'F') => InputEvent::Keyboard(KeyEvent::End), - Some(b'Z') => InputEvent::Keyboard(KeyEvent::BackTab), - Some(b'M') => { - // X10 emulation mouse encoding: ESC [ CB Cx Cy (6 characters only). - // NOTE (@imdaveho): cannot find documentation on this - let mut next = || iter.next().unwrap(); - - let cb = next() as i8 - 32; - // (1, 1) are the coords for upper left. - let cx = next().saturating_sub(32) as u16; - let cy = next().saturating_sub(32) as u16; - - InputEvent::Mouse(match cb & 0b11 { - 0 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelUp, cx, cy) - } else { - MouseEvent::Press(MouseButton::Left, cx, cy) - } - } - 1 => { - if cb & 0x40 != 0 { - MouseEvent::Press(MouseButton::WheelDown, cx, cy) - } else { - MouseEvent::Press(MouseButton::Middle, cx, cy) - } - } - 2 => MouseEvent::Press(MouseButton::Right, cx, cy), - 3 => MouseEvent::Release(cx, cy), - _ => MouseEvent::Unknown, - }) - } - Some(b'<') => { - // xterm mouse handling: - // ESC [ < Cb ; Cx ; Cy (;) (M or m) - let mut buf = Vec::new(); - let mut c = iter.next().unwrap(); - while match c { - b'm' | b'M' => false, - _ => true, - } { - buf.push(c); - c = iter.next().unwrap(); - } - let str_buf = String::from_utf8(buf).unwrap(); - let nums = &mut str_buf.split(';'); - - let cb = nums.next().unwrap().parse::().unwrap(); - let cx = nums.next().unwrap().parse::().unwrap(); - let cy = nums.next().unwrap().parse::().unwrap(); - - match cb { - 0..=2 | 64..=65 => { - let button = match cb { - 0 => MouseButton::Left, - 1 => MouseButton::Middle, - 2 => MouseButton::Right, - 64 => MouseButton::WheelUp, - 65 => MouseButton::WheelDown, - _ => unreachable!(), - }; - match c { - b'M' => InputEvent::Mouse(MouseEvent::Press(button, cx, cy)), - b'm' => InputEvent::Mouse(MouseEvent::Release(cx, cy)), - _ => InputEvent::Unknown, - } - } - 32 => InputEvent::Mouse(MouseEvent::Hold(cx, cy)), - 3 => InputEvent::Mouse(MouseEvent::Release(cx, cy)), - _ => InputEvent::Unknown, - } - } - Some(c @ b'0'..=b'9') => { - // Numbered escape code. - let mut buf = Vec::new(); - buf.push(c); - let mut character = iter.next().unwrap(); - - // The final byte of a CSI sequence can be in the range 64-126, so - // let's keep reading anything else. - while character < 64 || character > 126 { - buf.push(character); - character = iter.next().unwrap(); - } - - match character { - // rxvt mouse encoding: - // ESC [ Cb ; Cx ; Cy ; M - b'M' => { - let str_buf = String::from_utf8(buf).unwrap(); - - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); - - let cb = nums[0]; - let cx = nums[1]; - let cy = nums[2]; - - let event = match cb { - 32 => MouseEvent::Press(MouseButton::Left, cx, cy), - 33 => MouseEvent::Press(MouseButton::Middle, cx, cy), - 34 => MouseEvent::Press(MouseButton::Right, cx, cy), - 35 => MouseEvent::Release(cx, cy), - 64 => MouseEvent::Hold(cx, cy), - 96 | 97 => MouseEvent::Press(MouseButton::WheelUp, cx, cy), - _ => MouseEvent::Unknown, - }; - - InputEvent::Mouse(event) - } - // Special key code. - b'~' => { - let str_buf = String::from_utf8(buf).unwrap(); - - // This CSI sequence can be a list of semicolon-separated numbers. - let nums: Vec = str_buf.split(';').map(|n| n.parse().unwrap()).collect(); - - if nums.is_empty() { - return InputEvent::Unknown; - } - - // TODO: handle multiple values for key modifiers (ex: values [3, 2] means Shift+Delete) - if nums.len() > 1 { - return InputEvent::Unknown; - } - - match nums[0] { - 1 | 7 => InputEvent::Keyboard(KeyEvent::Home), - 2 => InputEvent::Keyboard(KeyEvent::Insert), - 3 => InputEvent::Keyboard(KeyEvent::Delete), - 4 | 8 => InputEvent::Keyboard(KeyEvent::End), - 5 => InputEvent::Keyboard(KeyEvent::PageUp), - 6 => InputEvent::Keyboard(KeyEvent::PageDown), - v @ 11..=15 => InputEvent::Keyboard(KeyEvent::F(v - 10)), - v @ 17..=21 => InputEvent::Keyboard(KeyEvent::F(v - 11)), - v @ 23..=24 => InputEvent::Keyboard(KeyEvent::F(v - 12)), - _ => InputEvent::Unknown, - } - } - e => match (buf.last().unwrap(), e) { - (53, 65) => InputEvent::Keyboard(KeyEvent::CtrlUp), - (53, 66) => InputEvent::Keyboard(KeyEvent::CtrlDown), - (53, 67) => InputEvent::Keyboard(KeyEvent::CtrlRight), - (53, 68) => InputEvent::Keyboard(KeyEvent::CtrlLeft), - (50, 65) => InputEvent::Keyboard(KeyEvent::ShiftUp), - (50, 66) => InputEvent::Keyboard(KeyEvent::ShiftDown), - (50, 67) => InputEvent::Keyboard(KeyEvent::ShiftRight), - (50, 68) => InputEvent::Keyboard(KeyEvent::ShiftLeft), - _ => InputEvent::Unknown, - }, - } - } - _ => InputEvent::Unknown, - } -} - -/// Parse `c` as either a single byte ASCII char or a variable size UTF-8 char. -fn parse_utf8_char(c: u8, iter: &mut I) -> Result -where - I: Iterator, -{ - let error = Err(ErrorKind::IoError(io::Error::new( - io::ErrorKind::Other, - "Input character is not valid UTF-8", - ))); - - if c.is_ascii() { - Ok(c as char) - } else { - let mut bytes = Vec::new(); - bytes.push(c); - - while let Some(next) = iter.next() { - bytes.push(next); - if let Ok(st) = str::from_utf8(&bytes) { - return Ok(st.chars().next().unwrap()); - } - if bytes.len() >= 4 { - return error; - } - } - - return error; - } -} - -#[cfg(test)] -mod tests { - use super::parse_utf8_char; - - #[test] - fn test_parse_utf8() { - let st = "abcéŷ¤£€ù%323"; - let ref mut bytes = st.bytes(); - let chars = st.chars(); - for c in chars { - let b = bytes.next().unwrap(); - assert_eq!(c, parse_utf8_char(b, bytes).unwrap()); - } - } -} diff --git a/crossterm_input/src/input/windows_input.rs b/crossterm_input/src/input/windows_input.rs deleted file mode 100644 index 41a71d6..0000000 --- a/crossterm_input/src/input/windows_input.rs +++ /dev/null @@ -1,486 +0,0 @@ -//! This is a WINDOWS specific implementation for input related action. - -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{self, Receiver, Sender}, - Arc, -}; -use std::time::Duration; -use std::{char, io, thread}; - -use winapi::um::{ - wincon::{ - LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED, - }, - winnt::INT, - winuser::{ - VK_BACK, VK_CONTROL, VK_DELETE, VK_DOWN, VK_END, VK_ESCAPE, VK_F1, VK_F10, VK_F11, VK_F12, - VK_F2, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_HOME, VK_INSERT, VK_LEFT, - VK_MENU, VK_NEXT, VK_PRIOR, VK_RETURN, VK_RIGHT, VK_SHIFT, VK_UP, - }, -}; - -use crossterm_utils::Result; -use crossterm_winapi::{ - ButtonState, Console, ConsoleMode, EventFlags, Handle, InputEventType, KeyEventRecord, - MouseEvent, -}; - -use super::{ITerminalInput, InputEvent, KeyEvent, MouseButton}; - -pub struct WindowsInput; - -impl WindowsInput { - pub fn new() -> WindowsInput { - WindowsInput - } -} - -const ENABLE_MOUSE_MODE: u32 = 0x0010 | 0x0080 | 0x0008; - -// NOTE (@imdaveho): this global var is terrible -> move it elsewhere... -static mut ORIG_MODE: u32 = 0; - -impl ITerminalInput for WindowsInput { - fn read_char(&self) -> Result { - // _getwch is without echo and _getwche is with echo - let pressed_char = unsafe { _getwche() }; - - // we could return error but maybe option to keep listening until valid character is inputted. - if pressed_char == 0 || pressed_char == 0xe0 { - Err(io::Error::new( - io::ErrorKind::Other, - "Given input char is not a valid char, mostly occurs when pressing special keys", - ))?; - } - - let ch = char::from_u32(pressed_char as u32).ok_or_else(|| { - io::Error::new(io::ErrorKind::Other, "Could not parse given input to char") - })?; - - Ok(ch) - } - - fn read_async(&self) -> AsyncReader { - AsyncReader::new(Box::new(move |event_tx, cancellation_token| loop { - for i in read_input_events().unwrap().1 { - if event_tx.send(i).is_err() { - return; - } - } - - if cancellation_token.load(Ordering::SeqCst) { - return; - } - - thread::sleep(Duration::from_millis(1)); - })) - } - - fn read_until_async(&self, delimiter: u8) -> AsyncReader { - AsyncReader::new(Box::new(move |event_tx, cancellation_token| loop { - for event in read_input_events().unwrap().1 { - if let InputEvent::Keyboard(KeyEvent::Char(key)) = event { - if (key as u8) == delimiter { - return; - } - } - - if cancellation_token.load(Ordering::SeqCst) { - return; - } else { - if event_tx.send(event).is_err() { - return; - } - } - - thread::sleep(Duration::from_millis(1)); - } - })) - } - - fn read_sync(&self) -> SyncReader { - SyncReader - } - - fn enable_mouse_mode(&self) -> Result<()> { - let mode = ConsoleMode::from(Handle::current_in_handle()?); - - unsafe { - ORIG_MODE = mode.mode()?; - mode.set_mode(ENABLE_MOUSE_MODE)?; - } - Ok(()) - } - - fn disable_mouse_mode(&self) -> Result<()> { - let mode = ConsoleMode::from(Handle::current_in_handle()?); - mode.set_mode(unsafe { ORIG_MODE })?; - Ok(()) - } -} - -/// This type allows you to read input synchronously, which means that reading call will be blocking ones. -/// -/// This type is an iterator, and could be used to iterate over input events. -/// -/// If you don't want to block your calls use [AsyncReader](./LINK), which will read input on the background and queue it for you to read. -pub struct SyncReader; - -impl Iterator for SyncReader { - type Item = InputEvent; - - /// Read input from the user. - /// - /// If there are no keys pressed this will be a blocking call until there are. - /// This will return `None` in case of a failure and `Some(InputEvent) in case of an occurred input event.` - fn next(&mut self) -> Option { - read_single_event().unwrap() - } -} - -/// This type allows you to read the input asynchronously which means that input events are gathered on the background and will be queued for you to read. -/// -/// **[SyncReader](./LINK)** -/// If you want a blocking, or less resource consuming read to happen use `SyncReader`, this will leave a way all the thread and queueing and will be a blocking read. -/// -/// This type is an iterator, and could be used to iterate over input events. -/// -/// # Remarks -/// - Threads spawned will be disposed of as soon the `AsyncReader` goes out of scope. -/// - MPSC-channels are used to queue input events, this type implements an iterator of the rx side of the queue. -pub struct AsyncReader { - event_rx: Receiver, - shutdown: Arc, -} - -impl AsyncReader { - /// Construct a new instance of the `AsyncReader`. - /// The reading will immediately start when calling this function. - pub fn new(function: Box, &Arc) + Send>) -> AsyncReader { - let shutdown_handle = Arc::new(AtomicBool::new(false)); - - let (event_tx, event_rx) = mpsc::channel(); - let thread_shutdown = shutdown_handle.clone(); - - thread::spawn(move || loop { - function(&event_tx, &thread_shutdown); - }); - - AsyncReader { - event_rx, - shutdown: shutdown_handle, - } - } - - /// Stop the input event reading. - /// - /// You don't necessarily have to call this function because it will automatically be called when this reader goes out of scope. - /// - /// # Remarks - /// - Background thread will be closed. - /// - This will consume the handle you won't be able to restart the reading with this handle, create a new `AsyncReader` instead. - pub fn stop(&mut self) { - self.shutdown.store(true, Ordering::SeqCst); - } -} - -impl Drop for AsyncReader { - fn drop(&mut self) { - self.stop(); - } -} - -impl Iterator for AsyncReader { - type Item = InputEvent; - - /// Check if there are input events to read. - /// - /// It will return `None` when nothing is there to read, `Some(InputEvent)` if there are events to read. - /// - /// # Remark - /// - This is **not** a blocking call. - /// - When calling this method to fast after each other the reader might not have read a full byte sequence of some pressed key. - /// Make sure that you have some delay of a few ms when calling this method. - fn next(&mut self) -> Option { - let mut iterator = self.event_rx.try_iter(); - iterator.next() - } -} - -extern "C" { - fn _getwche() -> INT; -} - -fn read_single_event() -> Result> { - let console = Console::from(Handle::current_in_handle()?); - - let input = match console.read_single_input_event()? { - Some(event) => event, - None => return Ok(None), - }; - - match input.event_type { - InputEventType::KeyEvent => { - handle_key_event(unsafe { KeyEventRecord::from(*input.event.KeyEvent()) }) - } - InputEventType::MouseEvent => { - handle_mouse_event(unsafe { MouseEvent::from(*input.event.MouseEvent()) }) - } - // NOTE (@imdaveho): ignore below - InputEventType::WindowBufferSizeEvent => return Ok(None), // TODO implement terminal resize event - InputEventType::FocusEvent => Ok(None), - InputEventType::MenuEvent => Ok(None), - } -} - -/// partially inspired by: https://github.com/retep998/wio-rs/blob/master/src/console.rs#L130 -fn read_input_events() -> Result<(u32, Vec)> { - let console = Console::from(Handle::current_in_handle()?); - - let result = console.read_console_input()?; - - let mut input_events = Vec::with_capacity(result.0 as usize); - - for input in result.1 { - match input.event_type { - InputEventType::KeyEvent => { - if let Ok(Some(event)) = - handle_key_event(unsafe { KeyEventRecord::from(*input.event.KeyEvent()) }) - { - input_events.push(event) - } - } - InputEventType::MouseEvent => { - if let Ok(Some(event)) = - handle_mouse_event(unsafe { MouseEvent::from(*input.event.MouseEvent()) }) - { - input_events.push(event) - } - } - // NOTE (@imdaveho): ignore below - InputEventType::WindowBufferSizeEvent => (), // TODO implement terminal resize event - InputEventType::FocusEvent => (), - InputEventType::MenuEvent => (), - } - } - - return Ok((result.0, input_events)); -} - -fn handle_mouse_event(mouse_event: MouseEvent) -> Result> { - if let Some(event) = parse_mouse_event_record(&mouse_event) { - return Ok(Some(InputEvent::Mouse(event))); - } - Ok(None) -} - -fn handle_key_event(key_event: KeyEventRecord) -> Result> { - if key_event.key_down { - if let Some(event) = parse_key_event_record(&key_event) { - return Ok(Some(InputEvent::Keyboard(event))); - } - } - - return Ok(None); -} - -fn parse_key_event_record(key_event: &KeyEventRecord) -> Option { - let key_code = key_event.virtual_key_code as i32; - match key_code { - VK_SHIFT | VK_CONTROL | VK_MENU => None, - VK_BACK => Some(KeyEvent::Backspace), - VK_ESCAPE => Some(KeyEvent::Esc), - VK_RETURN => Some(KeyEvent::Enter), - VK_F1 | VK_F2 | VK_F3 | VK_F4 | VK_F5 | VK_F6 | VK_F7 | VK_F8 | VK_F9 | VK_F10 | VK_F11 - | VK_F12 => Some(KeyEvent::F((key_event.virtual_key_code - 111) as u8)), - VK_LEFT | VK_UP | VK_RIGHT | VK_DOWN => { - // Modifier Keys (Ctrl, Shift) Support - let key_state = &key_event.control_key_state; - let ctrl_pressed = key_state.has_state(RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED); - let shift_pressed = key_state.has_state(SHIFT_PRESSED); - - let event = match key_code { - VK_LEFT => { - if ctrl_pressed { - Some(KeyEvent::CtrlLeft) - } else if shift_pressed { - Some(KeyEvent::ShiftLeft) - } else { - Some(KeyEvent::Left) - } - } - VK_UP => { - if ctrl_pressed { - Some(KeyEvent::CtrlUp) - } else if shift_pressed { - Some(KeyEvent::ShiftUp) - } else { - Some(KeyEvent::Up) - } - } - VK_RIGHT => { - if ctrl_pressed { - Some(KeyEvent::CtrlRight) - } else if shift_pressed { - Some(KeyEvent::ShiftRight) - } else { - Some(KeyEvent::Right) - } - } - VK_DOWN => { - if ctrl_pressed { - Some(KeyEvent::CtrlDown) - } else if shift_pressed { - Some(KeyEvent::ShiftDown) - } else { - Some(KeyEvent::Down) - } - } - _ => None, - }; - - event - } - VK_PRIOR | VK_NEXT => { - if key_code == VK_PRIOR { - Some(KeyEvent::PageUp) - } else if key_code == VK_NEXT { - Some(KeyEvent::PageDown) - } else { - None - } - } - VK_END | VK_HOME => { - if key_code == VK_HOME { - Some(KeyEvent::Home) - } else if key_code == VK_END { - Some(KeyEvent::End) - } else { - None - } - } - VK_DELETE => Some(KeyEvent::Delete), - VK_INSERT => Some(KeyEvent::Insert), - _ => { - // Modifier Keys (Ctrl, Alt, Shift) Support - let character_raw = { (unsafe { *key_event.u_char.UnicodeChar() } as u16) }; - - if character_raw < 255 { - let character = character_raw as u8 as char; - - let key_state = &key_event.control_key_state; - - if key_state.has_state(LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) { - // If the ALT key is held down, pressing the A key produces ALT+A, which the system does not treat as a character at all, but rather as a system command. - // The pressed command is stored in `virtual_key_code`. - let command = key_event.virtual_key_code as u8 as char; - - if (command).is_alphabetic() { - Some(KeyEvent::Alt(command)) - } else { - None - } - } else if key_state.has_state(LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) { - match character_raw as u8 { - c @ b'\x01'..=b'\x1A' => { - Some(KeyEvent::Ctrl((c as u8 - 0x1 + b'a') as char)) - } - c @ b'\x1C'..=b'\x1F' => { - Some(KeyEvent::Ctrl((c as u8 - 0x1C + b'4') as char)) - } - _ => None, - } - } else if key_state.has_state(SHIFT_PRESSED) { - // Shift + key press, essentially the same as single key press - // Separating to be explicit about the Shift press. - if character == '\t' { - Some(KeyEvent::BackTab) - } else { - Some(KeyEvent::Tab) - } - } else { - Some(KeyEvent::Char(character)) - } - } else { - None - } - } - } -} - -fn parse_mouse_event_record(event: &MouseEvent) -> Option { - // NOTE (@imdaveho): xterm emulation takes the digits of the coords and passes them - // individually as bytes into a buffer; the below cxbs and cybs replicates that and - // mimicks the behavior; additionally, in xterm, mouse move is only handled when a - // mouse button is held down (ie. mouse drag) - - let xpos = event.mouse_position.x + 1; - let ypos = event.mouse_position.y + 1; - - // TODO (@imdaveho): check if linux only provides coords for visible terminal window vs the total buffer - - match event.event_flags { - EventFlags::PressOrRelease => { - // Single click - match event.button_state { - ButtonState::Release => Some(super::MouseEvent::Release(xpos as u16, ypos as u16)), - ButtonState::FromLeft1stButtonPressed => { - // left click - Some(super::MouseEvent::Press( - MouseButton::Left, - xpos as u16, - ypos as u16, - )) - } - ButtonState::RightmostButtonPressed => { - // right click - Some(super::MouseEvent::Press( - MouseButton::Right, - xpos as u16, - ypos as u16, - )) - } - ButtonState::FromLeft2ndButtonPressed => { - // middle click - Some(super::MouseEvent::Press( - MouseButton::Middle, - xpos as u16, - ypos as u16, - )) - } - _ => None, - } - } - EventFlags::MouseMoved => { - // Click + Move - // NOTE (@imdaveho) only register when mouse is not released - if event.button_state != ButtonState::Release { - Some(super::MouseEvent::Hold(xpos as u16, ypos as u16)) - } else { - None - } - } - EventFlags::MouseWheeled => { - // Vertical scroll - // NOTE (@imdaveho) from https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str - // if `button_state` is negative then the wheel was rotated backward, toward the user. - if event.button_state != ButtonState::Negative { - Some(super::MouseEvent::Press( - MouseButton::WheelUp, - xpos as u16, - ypos as u16, - )) - } else { - Some(super::MouseEvent::Press( - MouseButton::WheelDown, - xpos as u16, - ypos as u16, - )) - } - } - EventFlags::DoubleClick => None, // NOTE (@imdaveho): double click not supported by unix terminals - EventFlags::MouseHwheeled => None, // NOTE (@imdaveho): horizontal scroll not supported by unix terminals - // TODO: Handle Ctrl + Mouse, Alt + Mouse, etc. - } -} diff --git a/crossterm_input/src/lib.rs b/crossterm_input/src/lib.rs deleted file mode 100644 index 059c3c3..0000000 --- a/crossterm_input/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![deny(unused_imports)] - -pub use crossterm_screen::{IntoRawMode, RawScreen}; - -pub use self::input::{ - input, AsyncReader, InputEvent, KeyEvent, MouseButton, MouseEvent, SyncReader, TerminalInput, -}; - -mod input; -mod sys; diff --git a/crossterm_input/src/sys.rs b/crossterm_input/src/sys.rs deleted file mode 100644 index b13064b..0000000 --- a/crossterm_input/src/sys.rs +++ /dev/null @@ -1,2 +0,0 @@ -#[cfg(unix)] -pub mod unix; diff --git a/crossterm_input/src/sys/unix.rs b/crossterm_input/src/sys/unix.rs deleted file mode 100644 index bae349b..0000000 --- a/crossterm_input/src/sys/unix.rs +++ /dev/null @@ -1,60 +0,0 @@ -use std::os::unix::io::AsRawFd; -use std::{fs, io}; - -use crossterm_utils::Result; - -/// Get the TTY device. -/// -/// This allows for getting stdio representing _only_ the TTY, and not other streams. -pub fn get_tty() -> Result { - let file = fs::OpenOptions::new() - .read(true) - .write(true) - .open("/dev/tty")?; - - Ok(file) -} - -fn get_tty_fd() -> Result { - let fd = unsafe { - if libc::isatty(libc::STDIN_FILENO) == 1 { - libc::STDIN_FILENO - } else { - let tty_f = fs::File::open("/dev/tty")?; - tty_f.as_raw_fd() - } - }; - Ok(fd) -} - -pub fn read_char_raw() -> Result { - let mut buf = [0u8; 20]; - - let fd = get_tty_fd()?; - - // 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 { - 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 - } - }?; - - Ok(rv) -} diff --git a/crossterm_screen/.gitignore b/crossterm_screen/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_screen/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_screen/CHANGELOG.md b/crossterm_screen/CHANGELOG.md deleted file mode 100644 index 5da5eaa..0000000 --- a/crossterm_screen/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# Changes crossterm_screen 0.3.0 - -- `RawScreen::into_raw_mode` returns `crossterm::Result` instead of `io::Result` -- `RawScreen::disable_raw_mode` returns `crossterm::Result` instead of `io::Result` -- `AlternateScreen::to_alternate` returns `crossterm::Result` instead of `io::Result` -- `AsyncReader::stop_reading()` to `stop()` -- `RawScreen::disable_raw_mode_on_drop` to `keep_raw_mode_on_drop` \ No newline at end of file diff --git a/crossterm_screen/Cargo.toml b/crossterm_screen/Cargo.toml deleted file mode 100644 index 5651b6a..0000000 --- a/crossterm_screen/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "crossterm_screen" -version = "0.3.0" -authors = ["T. Post"] -description = "A cross-platform library for raw and alternate screen." -repository = "https://github.com/crossterm-rs/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", version = "0.3.0"} - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.8", features = ["minwindef", "wincon"] } -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0" } diff --git a/crossterm_screen/LICENSE b/crossterm_screen/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_screen/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_screen/README.md b/crossterm_screen/README.md deleted file mode 100644 index e43f574..0000000 --- a/crossterm_screen/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# Crossterm Screen | cross-platform alternate, raw screen. - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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://crossterm-rs.github.io/crossterm/docs/feature_flags.html). - -In case you are wondering what 'alternate' or 'raw' screen is, you could checkout the [book](https://crossterm-rs.github.io/crossterm/docs/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 - -All examples of how `crossterm_input` works can be found in the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) directory. -And you might consider reading the [book](https://crossterm-rs.github.io/crossterm/docs/screen.html) which has a dedicated section on alternate and raw modes. - -Add the `crossterm_screen` package to your `Cargo.toml` file. - -``` -[dependencies] -crossterm_screen = "0.2" -``` - -And import the `crossterm_screen` modules you want to use. - -```rust -pub use crossterm_screen::{AlternateScreen, RawScreen}; -``` - -### Useful Links - -- [Documentation](https://docs.rs/crossterm_screen/) -- [Crates.io](https://crates.io/crates/crossterm_screen) -- [Book](https://crossterm-rs.github.io/crossterm/docs/screen.html) -- [Examples](./examples) - -## Features -These are the features of this crate: - -- Cross-platform -- Multithreaded (send, sync) -- Detailed Documentation -- Few Dependencies -- Alternate screen -- Raw screen - -Planned features: -- make is possible to switch between multiple buffers. - -## Examples -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -## 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. - -## Authors -* **Timon Post** - *Project Owner & creator* - -## License -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_screen/src/lib.rs b/crossterm_screen/src/lib.rs deleted file mode 100644 index 2298100..0000000 --- a/crossterm_screen/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![deny(unused_imports)] - -//! A module which provides some functionalities to work with the terminal screen. -//! Like allowing you to switch between the main and alternate screen or putting the terminal into raw mode. -pub use self::screen::{AlternateScreen, IntoRawMode, RawScreen}; - -mod screen; -mod sys; diff --git a/crossterm_screen/src/screen.rs b/crossterm_screen/src/screen.rs deleted file mode 100644 index e0dadf8..0000000 --- a/crossterm_screen/src/screen.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! 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. - -pub use self::alternate::AlternateScreen; -pub use self::raw::{IntoRawMode, RawScreen}; - -mod alternate; -mod raw; diff --git a/crossterm_screen/src/screen/alternate.rs b/crossterm_screen/src/screen/alternate.rs deleted file mode 100644 index 4296fc4..0000000 --- a/crossterm_screen/src/screen/alternate.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! This module contains all the logic for switching between alternate screen and main screen. -//! -//! *Nix style applications often utilize an alternate screen buffer, so that they can modify the entire contents of the buffer, without affecting the application that started them. -//! The alternate buffer is exactly the dimensions of the window, without any scrollback region. -//! 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. - -#[cfg(windows)] -use crossterm_utils::supports_ansi; -use crossterm_utils::Result; - -#[cfg(windows)] -use crate::sys::winapi::ToAlternateScreenCommand; -use crate::sys::{self, IAlternateScreenCommand}; - -use super::RawScreen; - -/// With this type you will be able to switch to the alternate screen and then back to the main screen. -/// Check also the Screen type for switching to alternate mode. -/// -/// Although this type is available for you to use I would recommend using `Screen` instead. -pub struct AlternateScreen { - #[cfg(windows)] - command: Box<(dyn IAlternateScreenCommand + Sync + Send)>, - #[cfg(unix)] - command: sys::ToAlternateScreenCommand, - _raw_screen: Option, -} - -impl AlternateScreen { - /// Switch to the alternate screen. This function will return an `AlternateScreen` instance if everything went well. This type will give you control over the `AlternateScreen`. - /// - /// The bool specifies whether the screen should be in raw mode or not. - /// - /// # What is Alternate screen? - /// *Nix style applications often utilize an alternate screen buffer, so that they can modify the entire contents of the buffer without affecting the application that started them. - /// The alternate buffer dimensions are exactly the same as the window, without any scrollback region. - /// 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. - pub fn to_alternate(raw_mode: bool) -> Result { - #[cfg(windows)] - let command = if supports_ansi() { - Box::from(sys::ToAlternateScreenCommand::new()) - as Box<(dyn IAlternateScreenCommand + Sync + Send)> - } else { - Box::from(ToAlternateScreenCommand::new()) - as Box<(dyn IAlternateScreenCommand + Sync + Send)> - }; - - #[cfg(unix)] - let command = sys::ToAlternateScreenCommand::new(); - - command.enable()?; - - if raw_mode { - let raw_screen = RawScreen::into_raw_mode()?; - return Ok(AlternateScreen { - command, - _raw_screen: Some(raw_screen), - }); - } - - Ok(AlternateScreen { - command, - _raw_screen: None, - }) - } - - /// Switch the alternate screen back to the main screen. - pub fn to_main(&self) -> Result<()> { - self.command.disable() - } -} - -impl Drop for AlternateScreen { - /// This will switch back to the main screen on drop. - fn drop(&mut self) { - let _ = self.to_main(); - } -} diff --git a/crossterm_screen/src/screen/raw.rs b/crossterm_screen/src/screen/raw.rs deleted file mode 100644 index 3e7d5c2..0000000 --- a/crossterm_screen/src/screen/raw.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! This module is used for enabling and disabling raw mode for the terminal. -//! -//! What exactly is raw state: -//! - No line buffering. -//! Normally the terminals uses line buffering. This means that the input will be send to the terminal line by line. -//! With raw mode the input will be send one byte at a time. -//! - Input -//! All input has to be written manually by the programmer. -//! - Characters -//! The characters are not processed by the terminal driver, but are sent straight through. -//! Special character have no meaning, like backspace will not be interpret as backspace but instead will be directly send to the terminal. -//! - Escape characters -//! Note that in raw modes `\n` `\r` will move to the new line but the cursor will be at the same position as before on the new line therefor use `\n\r` to start at the new line at the first cell. -//! -//! With these modes you can easier design the terminal screen. - -use std::io::{Stdout, Write}; - -use crossterm_utils::Result; - -use crate::sys; - -/// A wrapper for the raw terminal state, which can be used to write to. -/// -/// Please note that if this type drops, the raw screen will be undone. To prevent this behaviour call `disable_drop`. -pub struct RawScreen { - disable_raw_mode_on_drop: bool, -} - -impl RawScreen { - /// Put terminal in raw mode. - pub fn into_raw_mode() -> Result { - #[cfg(unix)] - let mut command = sys::unix::RawModeCommand::new(); - #[cfg(windows)] - let mut command = sys::winapi::RawModeCommand::new(); - - command.enable()?; - - Ok(RawScreen { - disable_raw_mode_on_drop: true, - }) - } - - /// Put terminal back in original modes. - pub fn disable_raw_mode() -> Result<()> { - #[cfg(unix)] - let mut command = sys::unix::RawModeCommand::new(); - #[cfg(windows)] - let command = sys::winapi::RawModeCommand::new(); - - command.disable()?; - Ok(()) - } - - /// Keeps the raw mode when the `RawMode` value is dropped. - pub fn keep_raw_mode_on_drop(&mut self) { - self.disable_raw_mode_on_drop = false; - } -} - -/// Types which can be converted into "raw mode". -/// -/// # Why is this type defined on writers and not readers? -/// -/// TTYs has their state controlled by the writer, not the reader. You use the writer to clear the -/// screen, move the cursor and so on, so naturally you use the writer to change the mode as well. -pub trait IntoRawMode: Write + Sized { - /// Switch to raw mode. - /// - /// Raw mode means that stdin won't be printed (it will instead have to be written manually by - /// the program). Furthermore, the input isn't canonicalised or buffered (that is, you can - /// read from stdin one byte of a time). The output is neither modified in any way. - fn into_raw_mode(self) -> Result; -} - -impl IntoRawMode for Stdout { - fn into_raw_mode(self) -> Result { - RawScreen::into_raw_mode()?; - // this make's sure that raw screen will be disabled when it goes out of scope. - Ok(RawScreen { - disable_raw_mode_on_drop: true, - }) - } -} - -impl Drop for RawScreen { - fn drop(&mut self) { - if self.disable_raw_mode_on_drop { - let _ = RawScreen::disable_raw_mode(); - } - } -} diff --git a/crossterm_screen/src/sys.rs b/crossterm_screen/src/sys.rs deleted file mode 100644 index f4d7ec1..0000000 --- a/crossterm_screen/src/sys.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crossterm_utils::{csi, write_cout, Result}; - -#[cfg(unix)] -pub mod unix; - -#[cfg(windows)] -pub mod winapi; - -/// This command is used for switching to the alternate screen and back to the main screen. -pub struct ToAlternateScreenCommand; - -impl ToAlternateScreenCommand { - pub fn new() -> ToAlternateScreenCommand { - ToAlternateScreenCommand - } -} - -impl IAlternateScreenCommand for ToAlternateScreenCommand { - /// enable alternate screen. - fn enable(&self) -> Result<()> { - write_cout!(csi!("?1049h"))?; - Ok(()) - } - - /// disable alternate screen. - fn disable(&self) -> Result<()> { - write_cout!(csi!("?1049l"))?; - Ok(()) - } -} - -// This trait provides an interface for switching to the alternate screen and back. -pub trait IAlternateScreenCommand: Sync + Send { - fn enable(&self) -> Result<()>; - fn disable(&self) -> Result<()>; -} diff --git a/crossterm_screen/src/sys/unix.rs b/crossterm_screen/src/sys/unix.rs deleted file mode 100644 index d305efd..0000000 --- a/crossterm_screen/src/sys/unix.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crossterm_utils::Result; - -/// This command is used for enabling and disabling raw mode for the terminal. -pub struct RawModeCommand; - -impl RawModeCommand { - pub fn new() -> Self { - RawModeCommand - } - - /// Enables raw mode. - pub fn enable(&mut self) -> Result<()> { - crossterm_utils::sys::unix::enable_raw_mode()?; - Ok(()) - } - - /// Disables raw mode. - pub fn disable(&mut self) -> Result<()> { - crossterm_utils::sys::unix::disable_raw_mode()?; - Ok(()) - } -} diff --git a/crossterm_screen/src/sys/winapi.rs b/crossterm_screen/src/sys/winapi.rs deleted file mode 100644 index 58e19bd..0000000 --- a/crossterm_screen/src/sys/winapi.rs +++ /dev/null @@ -1,76 +0,0 @@ -use winapi::shared::minwindef::DWORD; -use winapi::um::wincon; - -use crossterm_utils::Result; -use crossterm_winapi::{ConsoleMode, Handle, ScreenBuffer}; - -use super::IAlternateScreenCommand; - -use self::wincon::{ENABLE_LINE_INPUT, ENABLE_WRAP_AT_EOL_OUTPUT}; - -/// 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, -} - -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) -> 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) -> 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 the alternate screen and back to the 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) -> Result<()> { - let alternate_screen = ScreenBuffer::create(); - alternate_screen.show()?; - Ok(()) - } - - fn disable(&self) -> Result<()> { - let screen_buffer = ScreenBuffer::from(Handle::output_handle()?); - screen_buffer.show()?; - Ok(()) - } -} diff --git a/crossterm_style/.gitignore b/crossterm_style/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_style/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_style/CHANGELOG.md b/crossterm_style/CHANGELOG.md deleted file mode 100644 index 0003688..0000000 --- a/crossterm_style/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -# Changes crossterm_style 0.4 -- `get_available_color_count` returns no result -- `ExecutableCommand::queue` returns `crossterm::Result` -- `QueueableCommand::queue` returns `crossterm::Result` -- `available_color_count` to `available_color_count()` -- Added derives: `Debug` for `ObjectStyle` [debug-derive] -- Command API takes mutable self instead of self - -# Changes crossterm_style 0.3 -- Removed `TerminalColor::from_output()` -- Added `NoItalic` attribute - -# Changes crossterm_style 0.2 -- Introduced more `Attributes` -- Introduced easier ways to style text [issue 87](https://github.com/crossterm-rs/crossterm/issues/87). -- Removed `ColorType` since it was unnecessary. - -# Changes crossterm_style 0.1 -- Moved out of `crossterm` 5.4 crate. \ No newline at end of file diff --git a/crossterm_style/Cargo.toml b/crossterm_style/Cargo.toml deleted file mode 100644 index 14a146c..0000000 --- a/crossterm_style/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "crossterm_style" -version = "0.5.0" -authors = ["T. Post"] -description = "A cross-platform library styling the terminal output." -repository = "https://github.com/crossterm-rs/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.8", features = ["wincon"] } -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0"} - -[dependencies] -crossterm_utils = { path="../crossterm_utils", version = "0.3.0"} -serde = { version = "1.0.0", features = ["derive"], optional = true } diff --git a/crossterm_style/LICENSE b/crossterm_style/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_style/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_style/README.md b/crossterm_style/README.md deleted file mode 100644 index f6ac483..0000000 --- a/crossterm_style/README.md +++ /dev/null @@ -1,144 +0,0 @@ -# Crossterm Style | cross-platform styling. - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.svg?branch=master - -This crate allows you to style the 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 the 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://crossterm-rs.github.io/crossterm/docs/feature_flags.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 - -All examples of how `crossterm_style` works can be found in the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) directory. - -Add the `crossterm_style` package to your `Cargo.toml` file. - -``` -[dependencies] -crossterm_style = "0.3" -``` - -And import the `crossterm_style` modules you want to use. - -```rust -pub use crossterm_style::{color, style, Attribute, Color, ColorType, ObjectStyle, StyledObject, TerminalColor, Colorize, Styler}; -``` - -### Useful Links - -- [Documentation](https://docs.rs/crossterm_input/) -- [Crates.io](https://crates.io/crates/crossterm_input) -- [Book](https://crossterm-rs.github.io/crossterm/docs/styling.html) -- [Examples](./examples) - -## Features -These are the features of this crate: - -- Cross-platform -- Multithreaded (send, sync) -- Detailed Documentation -- Few Dependencies -- Styled output - - Foreground Color (16 base colors) - - Background Color (16 base colors) - - 256 (ANSI) Color Support (Windows 10 and UNIX Only) - - RGB Color Support (Windows 10 and UNIX only) - - Text Attributes: bold, italic, underscore and crossed word and [more](https://crossterm-rs.github.io/crossterm/docs/styling.html#attributes) (Windows 10 and UNIX only) - -## Examples -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -_style text with attributes_ -```rust -use crossterm_style::{Colored, Color, Colorize, Styler, Attribute}; - -// pass any `Attribute` value to the formatting braces. -println!("{} Underlined {} No Underline", Attribute::Underlined, Attribute::NoUnderline); - -// you could also call different attribute methods on a `&str` and keep on chaining if needed. -let styled_text = "Bold Underlined".bold().underlined(); -println!("{}", styled_text); - -// old-way but still usable -let styled_text = style("Bold Underlined").bold().underlined(); -``` - -_style text with colors_ -```rust -use crossterm_style::{Colored, Color, Colorize}; - -println!("{} Red foreground color", Colored::Fg(Color::Red)); -println!("{} Blue background color", Colored::Bg(Color::Blue)); - -// you can also call different coloring methods on a `&str`. -let styled_text = "Bold Underlined".red().on_blue(); -println!("{}", styled_text); - -// old-way but still usable -let styled_text = style("Bold Underlined").with(Color::Red).on(Color::Blue); -``` -_style text with RGB and ANSI Value_ -```rust -// custom rgb value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(Color::Rgb { - r: 10, - g: 10, - b: 10 -})); - -// custom ansi color value (Windows 10 and UNIX systems) -println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); -``` - -## 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. - -## Authors -* **Timon Post** - *Project Owner & creator* - -## License -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_style/src/ansi_color.rs b/crossterm_style/src/ansi_color.rs deleted file mode 100644 index 65bda2a..0000000 --- a/crossterm_style/src/ansi_color.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! This is a ANSI specific implementation for styling related action. -//! This module is used for Windows 10 terminals and Unix terminals by default. - -use crossterm_utils::{csi, write_cout, Result}; - -use crate::{Attribute, Color, Colored, ITerminalColor}; - -pub fn get_set_fg_ansi(fg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Fg(fg_color)),) -} - -pub fn get_set_bg_ansi(bg_color: Color) -> String { - format!(csi!("{}m"), color_value(Colored::Bg(bg_color)),) -} - -pub fn get_set_attr_ansi(attribute: Attribute) -> String { - format!(csi!("{}m"), attribute as i16,) -} - -pub static RESET_ANSI: &'static str = csi!("0m"); - -/// This struct is an ANSI escape code implementation for color related actions. -pub struct AnsiColor; - -impl AnsiColor { - pub fn new() -> AnsiColor { - AnsiColor - } -} - -impl ITerminalColor for AnsiColor { - fn set_fg(&self, fg_color: Color) -> Result<()> { - write_cout!(get_set_fg_ansi(fg_color))?; - Ok(()) - } - - fn set_bg(&self, bg_color: Color) -> Result<()> { - write_cout!(get_set_bg_ansi(bg_color))?; - Ok(()) - } - - fn reset(&self) -> Result<()> { - write_cout!(RESET_ANSI)?; - Ok(()) - } -} - -fn color_value(colored: Colored) -> String { - let mut ansi_value = String::new(); - - let color; - - match colored { - Colored::Fg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("39"); - return ansi_value; - } else { - ansi_value.push_str("38;"); - color = new_color; - } - } - Colored::Bg(new_color) => { - if new_color == Color::Reset { - ansi_value.push_str("49"); - return ansi_value; - } else { - ansi_value.push_str("48;"); - color = new_color; - } - } - } - - let rgb_val: String; - - let color_val = match color { - Color::Black => "5;0", - Color::DarkGrey => "5;8", - Color::Red => "5;9", - Color::DarkRed => "5;1", - Color::Green => "5;10", - Color::DarkGreen => "5;2", - Color::Yellow => "5;11", - Color::DarkYellow => "5;3", - Color::Blue => "5;12", - Color::DarkBlue => "5;4", - Color::Magenta => "5;13", - Color::DarkMagenta => "5;5", - Color::Cyan => "5;14", - Color::DarkCyan => "5;6", - Color::White => "5;15", - Color::Grey => "5;7", - Color::Rgb { r, g, b } => { - rgb_val = format!("2;{};{};{}", r, g, b); - rgb_val.as_str() - } - Color::AnsiValue(val) => { - rgb_val = format!("5;{}", val); - rgb_val.as_str() - } - _ => "", - }; - - ansi_value.push_str(color_val); - ansi_value -} diff --git a/crossterm_style/src/color.rs b/crossterm_style/src/color.rs deleted file mode 100644 index 7bccfa0..0000000 --- a/crossterm_style/src/color.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! A module that contains all the actions related to the styling of the terminal. -//! Like applying attributes to text and changing the foreground and background. - -use std::clone::Clone; -use std::env; -use std::fmt::Display; - -#[cfg(windows)] -use crossterm_utils::supports_ansi; -use crossterm_utils::{impl_display, Command, Result}; - -use super::ansi_color::{self, AnsiColor}; -use super::enums::{Attribute, Color}; -use super::styledobject::StyledObject; -#[cfg(windows)] -use super::winapi_color::WinApiColor; -use super::ITerminalColor; - -/// Allows you to style the terminal. -/// -/// # Features: -/// -/// - 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. -pub struct TerminalColor { - #[cfg(windows)] - color: Box<(dyn ITerminalColor + Sync + Send)>, - #[cfg(unix)] - color: AnsiColor, -} - -impl TerminalColor { - /// Create new instance whereon color related actions can be performed. - pub fn new() -> TerminalColor { - #[cfg(windows)] - let color = if supports_ansi() { - Box::from(AnsiColor::new()) as Box<(dyn ITerminalColor + Sync + Send)> - } else { - WinApiColor::new() as Box<(dyn ITerminalColor + Sync + Send)> - }; - - #[cfg(unix)] - let color = AnsiColor::new(); - - TerminalColor { color } - } - - /// Set the foreground color to the given color. - pub fn set_fg(&self, color: Color) -> Result<()> { - self.color.set_fg(color) - } - - /// Set the background color to the given color. - pub fn set_bg(&self, color: Color) -> Result<()> { - self.color.set_bg(color) - } - - /// Reset the terminal colors and attributes to default. - pub fn reset(&self) -> Result<()> { - self.color.reset() - } - - /// Get available color count. - /// - /// # Remarks - /// - /// This does not always provide a good result. - pub fn available_color_count(&self) -> u16 { - env::var("TERM") - .map(|x| if x.contains("256color") { 256 } else { 8 }) - .unwrap_or(8) - } -} - -/// Get a `TerminalColor` implementation whereon color related actions can be performed. -pub fn color() -> TerminalColor { - TerminalColor::new() -} - -/// When executed, this command will set the foreground color of the terminal to the given color. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetFg(pub Color); - -impl Command for SetFg { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_fg_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiColor::new().set_fg(self.0) - } -} - -/// When executed, this command will set the background color of the terminal to the given color. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetBg(pub Color); - -impl Command for SetBg { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_bg_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiColor::new().set_fg(self.0) - } -} - -/// When executed, this command will set the given attribute to the terminal. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetAttr(pub Attribute); - -impl Command for SetAttr { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - ansi_color::get_set_attr_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -/// When executed, this command will print the styled font to the terminal. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct PrintStyledFont(pub StyledObject); - -impl Command for PrintStyledFont -where - D: Display + Clone, -{ - type AnsiType = StyledObject; - - fn ansi_code(&self) -> Self::AnsiType { - self.0.clone() - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - // attributes are not supported by WinAPI. - Ok(()) - } -} - -impl_display!(for SetFg); -impl_display!(for SetBg); -impl_display!(for SetAttr); -impl_display!(for PrintStyledFont); -impl_display!(for PrintStyledFont<&'static str>); diff --git a/crossterm_style/src/enums.rs b/crossterm_style/src/enums.rs deleted file mode 100644 index e790c64..0000000 --- a/crossterm_style/src/enums.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use self::{attribute::Attribute, color::Color, colored::Colored}; - -mod attribute; -mod color; -mod colored; diff --git a/crossterm_style/src/enums/attribute.rs b/crossterm_style/src/enums/attribute.rs deleted file mode 100644 index 408020d..0000000 --- a/crossterm_style/src/enums/attribute.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::fmt::Display; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crossterm_utils::csi; - -/// Enum with the different attributes to style your test. -/// -/// There are few things to note: -/// - Not all attributes are supported, some of them are only supported on Windows some only on Unix, -/// and some are only very rarely supported. -/// - I got those attributes, descriptions, supportability from here: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters -/// - Take note of the fact that when running your program cross-platform that some attributes might not work because of their support. -/// - When an attribute is not supported nothing will happen with the terminal state. -/// -/// # Example -/// You can use an attribute in a write statement to apply the attribute to the terminal output. -/// -/// ```ignore -/// println!( -/// "{} Underlined {} No Underline", -/// Attribute::Underlined, -/// Attribute::NoUnderline -/// ); -/// ``` -/// -/// You can also call attribute functions on a `&'static str`: -/// ```ignore -/// use Colorizer; -/// -/// println!("{}", style("Bold text").bold()); -/// println!("{}", style("Underlined text").underlined()); -/// println!("{}", style("Negative text").negative()); -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Attribute { - /// All attributes off - /// [info]: This will reset all current set attributes. - /// [Supportability]: Windows, UNIX. - Reset = 0, - /// Increased Intensity - /// [info]: This will increase the text sensitivity also known as bold. - /// [Supportability]: Windows, UNIX. - Bold = 1, - /// Decreased Intensity - /// [info]: This will decrease the text sensitivity also known as bold. - /// [Supportability]: Windows, UNIX. - Dim = 2, - /// Italic Text - /// [info]: This will make the text italic. - /// [Supportability]: Not widely supported, sometimes treated as inverse. - Italic = 3, - /// This will draw a line under the text. - /// [info]: An line under a word, especially in order to show its importance. - /// [Supportability]: Windows, UNIX - Underlined = 4, - /// Slow Blinking Text - /// [info]: Blink Less than 150 per minute. - /// [Supportability]: UNIX - SlowBlink = 5, - /// Slow Blinking Text - /// [info]: MS-DOS ANSI.SYS; 150+ per minute; - /// [Supportability]: Not widely supported - RapidBlink = 6, - /// Swap foreground and background colors - /// [info]: swap foreground and background colors - /// [Supportability]: Windows, UNIX - Reverse = 7, - /// Hide text - /// [info]: - /// - This will make the text hidden. - /// - Also known as 'Conceal' - /// [Supportability]: Windows, UNIX - Hidden = 8, - /// Cross-out text - /// [info]: Characters legible, but marked for deletion. - /// [Supportability]: UNIX - CrossedOut = 9, - /// The Fraktur is a typeface belonging to the group of Gothic typefaces. - /// [info]: https://nl.wikipedia.org/wiki/Fraktur - /// [Supportability]: Rarely supported - Fraktur = 20, - /// This will turn off the bold attribute. - /// [info]: - /// - Double-underline per ECMA-48. - /// - WikiPedia: https://en.wikipedia.org/wiki/Talk:ANSI_escape_code#SGR_21%E2%80%94%60Bold_off%60_not_widely_supported - /// - Opposite of `Bold`(1) - /// [Supportability]: not widely supported - NoBold = 21, - /// Normal color or intensity - /// Neither bold nor faint - NormalIntensity = 22, - /// This will turn off the italic attribute. - /// [info]: - /// - Not italic, not Fraktur - /// - Opposite of `Italic`(3) - /// [Supportability]: Windows, UNIX - NoItalic = 23, - /// This will turn off the underline attribute. - /// [info]: - /// - Not singly or doubly underlined will be turned off. - /// - Opposite of `Underlined.`(4) - /// [Supportability]: Windows, UNIX - NoUnderline = 24, - /// This will turn off the blinking attribute - /// [info]: Opposite of `Slow and Rapid blink.`(5,6) - /// [Supportability]: Unknown - NoBlink = 25, - /// This will turn off the reverse attribute. - /// [info]: Opposite of `Reverse`(7) - /// [Supportability]: Windows, unknown - NoInverse = 27, - /// This will make the text visible. - /// [info]: Opposite of `Hidden`(8) - /// [Supportability]: Unknown - NoHidden = 28, - /// This will turn off the crossed out attribute. - /// [info]: Opposite of `CrossedOut`(9) - /// [Supportability]: Not widely supported - NotCrossedOut = 29, - /// Framed text. - /// [Supportability]: Not widely supported - Framed = 51, - /// This will turn on the encircled attribute. - Encircled = 52, - /// This will draw a line at the top of the text. - /// [info]: Implementation defined (according to standard) - /// [Supportability]: Unknown - OverLined = 53, - /// This will turn off the framed or encircled attribute. - NotFramedOrEncircled = 54, - /// This will turn off the overLined attribute. - /// [info]: Opposite of `OverLined`(7) - /// [Supportability]: Windows, unknown - NotOverLined = 55, - - #[doc(hidden)] - __Nonexhaustive, -} - -impl Display for Attribute { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { - write!(f, "{}", format!(csi!("{}m"), *self as i16))?; - Ok(()) - } -} diff --git a/crossterm_style/src/enums/color.rs b/crossterm_style/src/enums/color.rs deleted file mode 100644 index d17303d..0000000 --- a/crossterm_style/src/enums/color.rs +++ /dev/null @@ -1,111 +0,0 @@ -use std::convert::AsRef; -use std::str::FromStr; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Enum with the different colors to color your test and terminal. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Color { - // This resets the color. - Reset, - - Black, - DarkGrey, - - Red, - DarkRed, - - Green, - DarkGreen, - - Yellow, - DarkYellow, - - Blue, - DarkBlue, - - Magenta, - DarkMagenta, - - Cyan, - DarkCyan, - - White, - Grey, - /// Color representing RGB-colors; - /// r = red - /// g = green - /// b = blue - Rgb { - r: u8, - g: u8, - b: u8, - }, - AnsiValue(u8), -} - -impl FromStr for Color { - type Err = (); - - /// Creates a `Color` from the string representation. - /// - /// # Remarks - /// - /// * `Color::White` is returned in case of an unknown color. - /// * This function does not return `Err` and you can safely unwrap. - fn from_str(src: &str) -> ::std::result::Result { - let src = src.to_lowercase(); - - match src.as_ref() { - "black" => Ok(Color::Black), - "dark_grey" => Ok(Color::DarkGrey), - "red" => Ok(Color::Red), - "dark_red" => Ok(Color::DarkRed), - "green" => Ok(Color::Green), - "dark_green" => Ok(Color::DarkGreen), - "yellow" => Ok(Color::Yellow), - "dark_yellow" => Ok(Color::DarkYellow), - "blue" => Ok(Color::Blue), - "dark_blue" => Ok(Color::DarkBlue), - "magenta" => Ok(Color::Magenta), - "dark_magenta" => Ok(Color::DarkMagenta), - "cyan" => Ok(Color::Cyan), - "dark_cyan" => Ok(Color::DarkCyan), - "white" => Ok(Color::White), - "grey" => Ok(Color::Grey), - _ => Ok(Color::White), - } - } -} - -#[cfg(test)] -mod tests { - use super::Color; - - #[test] - fn test_known_color_conversion() { - assert_eq!("black".parse(), Ok(Color::Black)); - assert_eq!("dark_grey".parse(), Ok(Color::DarkGrey)); - assert_eq!("red".parse(), Ok(Color::Red)); - assert_eq!("dark_red".parse(), Ok(Color::DarkRed)); - assert_eq!("green".parse(), Ok(Color::Green)); - assert_eq!("dark_green".parse(), Ok(Color::DarkGreen)); - assert_eq!("yellow".parse(), Ok(Color::Yellow)); - assert_eq!("dark_yellow".parse(), Ok(Color::DarkYellow)); - assert_eq!("blue".parse(), Ok(Color::Blue)); - assert_eq!("dark_blue".parse(), Ok(Color::DarkBlue)); - assert_eq!("magenta".parse(), Ok(Color::Magenta)); - assert_eq!("dark_magenta".parse(), Ok(Color::DarkMagenta)); - assert_eq!("cyan".parse(), Ok(Color::Cyan)); - assert_eq!("dark_cyan".parse(), Ok(Color::DarkCyan)); - assert_eq!("white".parse(), Ok(Color::White)); - assert_eq!("grey".parse(), Ok(Color::Grey)); - } - - #[test] - fn test_unknown_color_conversion_yields_white() { - assert_eq!("foo".parse(), Ok(Color::White)); - } -} diff --git a/crossterm_style/src/enums/colored.rs b/crossterm_style/src/enums/colored.rs deleted file mode 100644 index 1073225..0000000 --- a/crossterm_style/src/enums/colored.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::fmt::Display; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crate::color::color; -use crate::enums::Color; - -/// Could be used to color the foreground or background color. -/// -/// `Colored::Fg` represents the foreground color. -/// `Color::Bg` represents the background color. -/// -/// # Example -/// -/// You can use `Colored` in a write statement to apply the attribute to the terminal output. -/// -/// ```ignore -/// println!("{} Red foreground color", Colored::Fg(Color::Red)); -/// println!("{} Blue background color", Colored::Bg(Color::Blue)); -/// ``` -/// -/// You can also call coloring functions on a `&'static str`: -/// ```ignore -/// let styled_text = "Red forground color on blue background.".red().on_blue(); -/// println!("{}", styled_text); -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub enum Colored { - Fg(Color), - Bg(Color), -} - -impl Display for Colored { - fn fmt(&self, _f: &mut ::std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { - let colored_terminal = color(); - - match *self { - Colored::Fg(color) => colored_terminal - .set_fg(color) - .map_err(|_| std::fmt::Error)?, - Colored::Bg(color) => colored_terminal - .set_bg(color) - .map_err(|_| std::fmt::Error)?, - } - - Ok(()) - } -} diff --git a/crossterm_style/src/lib.rs b/crossterm_style/src/lib.rs deleted file mode 100644 index cf8fbb5..0000000 --- a/crossterm_style/src/lib.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! A module that contains all the actions related to the styling of the terminal. -//! Like applying attributes to text and changing the foreground and background. -#![deny(unused_imports)] - -use std::fmt::Display; - -pub use crossterm_utils::{execute, queue, Command, ExecutableCommand, QueueableCommand, Result}; - -pub use self::color::{color, PrintStyledFont, SetAttr, SetBg, SetFg, TerminalColor}; -pub use self::enums::{Attribute, Color, Colored}; -pub use self::objectstyle::ObjectStyle; -pub use self::styledobject::StyledObject; -pub use self::traits::{Colorize, Styler}; - -#[macro_use] -mod macros; -mod color; -mod enums; -pub mod objectstyle; -pub mod styledobject; -mod traits; - -mod ansi_color; -#[cfg(windows)] -mod winapi_color; - -/// This trait defines the actions that can be performed with terminal colors. -/// This trait can be implemented so that a concrete implementation of the ITerminalColor can fulfill -/// the wishes to work on a specific platform. -/// -/// ## For example: -/// -/// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), -/// so that color-related actions can be performed on both UNIX and Windows systems. -trait ITerminalColor { - /// Set the foreground color to the given color. - fn set_fg(&self, fg_color: Color) -> Result<()>; - /// Set the background color to the given color. - fn set_bg(&self, fg_color: Color) -> Result<()>; - /// Reset the terminal color to default. - fn reset(&self) -> Result<()>; -} - -/// This could be used to style a type that implements `Display` with colors and attributes. -/// -/// # Example -/// ```ignore -/// // get a styled object which could be painted to the terminal. -/// let styled_object = style("Some Blue colored text on black background") -/// .with(Color::Blue) -/// .on(Color::Black); -/// -/// // print the styled text * times to the current screen. -/// for i in 1..10 -/// { -/// println!("{}", styled_object); -/// } -/// ``` -/// -/// # Important Remark -/// -/// - Please checkout the documentation for `Colorizer` or `Styler`. -/// Those types will make it a bit easier to style a string. -pub fn style<'a, D: 'a>(val: D) -> StyledObject -where - D: Display + Clone, -{ - ObjectStyle::new().apply_to(val) -} - -impl Colorize<&'static str> for &'static str { - // foreground colors - def_str_color!(fg_color: black => Color::Black); - def_str_color!(fg_color: dark_grey => Color::DarkGrey); - def_str_color!(fg_color: red => Color::Red); - def_str_color!(fg_color: dark_red => Color::DarkRed); - def_str_color!(fg_color: green => Color::Green); - def_str_color!(fg_color: dark_green => Color::DarkGreen); - def_str_color!(fg_color: yellow => Color::Yellow); - def_str_color!(fg_color: dark_yellow => Color::DarkYellow); - def_str_color!(fg_color: blue => Color::Blue); - def_str_color!(fg_color: dark_blue => Color::DarkBlue); - def_str_color!(fg_color: magenta => Color::Magenta); - def_str_color!(fg_color: dark_magenta => Color::DarkMagenta); - def_str_color!(fg_color: cyan => Color::Cyan); - def_str_color!(fg_color: dark_cyan => Color::DarkCyan); - def_str_color!(fg_color: white => Color::White); - def_str_color!(fg_color: grey => Color::Grey); - - // background colors - def_str_color!(bg_color: on_black => Color::Black); - def_str_color!(bg_color: on_dark_grey => Color::DarkGrey); - def_str_color!(bg_color: on_red => Color::Red); - def_str_color!(bg_color: on_dark_red => Color::DarkRed); - def_str_color!(bg_color: on_green => Color::Green); - def_str_color!(bg_color: on_dark_green => Color::DarkGreen); - def_str_color!(bg_color: on_yellow => Color::Yellow); - def_str_color!(bg_color: on_dark_yellow => Color::DarkYellow); - def_str_color!(bg_color: on_blue => Color::Blue); - def_str_color!(bg_color: on_dark_blue => Color::DarkBlue); - def_str_color!(bg_color: on_magenta => Color::Magenta); - def_str_color!(bg_color: on_dark_magenta => Color::DarkMagenta); - def_str_color!(bg_color: on_cyan => Color::Cyan); - def_str_color!(bg_color: on_dark_cyan => Color::DarkCyan); - def_str_color!(bg_color: on_white => Color::White); - def_str_color!(bg_color: on_grey => Color::Grey); -} - -impl Styler<&'static str> for &'static str { - def_str_attr!(reset => Attribute::Reset); - def_str_attr!(bold => Attribute::Bold); - def_str_attr!(underlined => Attribute::Underlined); - def_str_attr!(reverse => Attribute::Reverse); - def_str_attr!(dim => Attribute::Dim); - def_str_attr!(italic => Attribute::Italic); - def_str_attr!(negative => Attribute::Reverse); - def_str_attr!(slow_blink => Attribute::SlowBlink); - def_str_attr!(rapid_blink => Attribute::RapidBlink); - def_str_attr!(hidden => Attribute::Hidden); - def_str_attr!(crossed_out => Attribute::CrossedOut); -} diff --git a/crossterm_style/src/macros.rs b/crossterm_style/src/macros.rs deleted file mode 100644 index 2138534..0000000 --- a/crossterm_style/src/macros.rs +++ /dev/null @@ -1,46 +0,0 @@ -macro_rules! def_attr { - ($name:ident => $attr:path) => { - fn $name(self) -> StyledObject { - self.attr($attr) - } - }; -} - -macro_rules! def_color { - ($side:ident: $name:ident => $color:path) => { - fn $name(self) -> StyledObject { - StyledObject { - object_style: ObjectStyle { - $side: Some($color), - ..self.object_style - }, - ..self - } - } - }; -} - -macro_rules! def_str_color { - ($side:ident: $name:ident => $color:path) => { - fn $name(self) -> StyledObject< &'static str> { - StyledObject { - object_style: ObjectStyle { - $side: Some($color), - ..Default::default() - }, - content: self - } - } - }; -} - -macro_rules! def_str_attr { - ($name:ident => $color:path) => { - fn $name(self) -> StyledObject<&'static str> { - StyledObject { - object_style: Default::default(), - content: self, - } - } - } -} diff --git a/crossterm_style/src/objectstyle.rs b/crossterm_style/src/objectstyle.rs deleted file mode 100644 index 4fb8608..0000000 --- a/crossterm_style/src/objectstyle.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! This module contains the `object style` that can be applied to an `styled object`. - -use std::fmt::Display; - -use super::{Attribute, Color, StyledObject}; - -/// Struct that contains the style properties that can be applied to a displayable object. -#[derive(Debug, Clone, Default)] -pub struct ObjectStyle { - pub fg_color: Option, - pub bg_color: Option, - pub attrs: Vec, -} - -impl ObjectStyle { - /// Apply a `StyledObject` to the passed displayable object. - pub fn apply_to(&self, val: D) -> StyledObject { - StyledObject { - object_style: self.clone(), - content: val, - } - } - - /// Get a new instance of `ObjectStyle` - pub fn new() -> ObjectStyle { - ObjectStyle::default() - } - - /// Set the background color of `ObjectStyle` to the passed color. - pub fn bg(mut self, color: Color) -> ObjectStyle { - self.bg_color = Some(color); - self - } - - /// Set the foreground color of `ObjectStyle` to the passed color. - pub fn fg(mut self, color: Color) -> ObjectStyle { - self.fg_color = Some(color); - self - } - - /// Add an `Attribute` to the current text. Like italic or bold. - pub fn add_attr(&mut self, attr: Attribute) { - self.attrs.push(attr); - } -} diff --git a/crossterm_style/src/styledobject.rs b/crossterm_style/src/styledobject.rs deleted file mode 100644 index f0b3ec7..0000000 --- a/crossterm_style/src/styledobject.rs +++ /dev/null @@ -1,131 +0,0 @@ -//! This module contains the logic to style an object that contains some 'content' which can be styled. - -use std::fmt::{self, Display, Formatter}; -use std::result; - -use crossterm_utils::{csi, queue}; - -use super::{color, Attribute, Color, Colorize, ObjectStyle, SetBg, SetFg, Styler}; - -/// Contains both the style and the content which can be styled. -#[derive(Clone)] -pub struct StyledObject { - pub object_style: ObjectStyle, - pub content: D, -} - -impl<'a, D: Display + 'a + Clone> StyledObject { - /// Set the foreground of the styled object to the passed `Color`. - /// - /// # Remarks - /// - /// 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 { - self.object_style = self.object_style.fg(foreground_color); - self - } - - /// Set the background of the styled object to the passed `Color`. - /// - /// # Remarks - /// - /// 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 { - self.object_style = self.object_style.bg(background_color); - self - } - - /// Set the attribute of an styled object to the passed `Attribute`. - /// - /// # Remarks - /// - /// 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 { - self.object_style.add_attr(attr); - self - } -} - -impl Display for StyledObject { - fn fmt(&self, f: &mut Formatter) -> result::Result<(), fmt::Error> { - let colored_terminal = color(); - let mut reset = false; - - if let Some(bg) = self.object_style.bg_color { - queue!(f, SetBg(bg)).map_err(|_| fmt::Error)?; - reset = true; - } - if let Some(fg) = self.object_style.fg_color { - queue!(f, SetFg(fg)).map_err(|_| fmt::Error)?; - reset = true; - } - - for attr in self.object_style.attrs.iter() { - fmt::Display::fmt(&format!(csi!("{}m"), *attr as i16), f)?; - reset = true; - } - - fmt::Display::fmt(&self.content, f)?; - - if reset { - colored_terminal.reset().map_err(|_| fmt::Error)?; - } - - Ok(()) - } -} - -impl Colorize for StyledObject { - // foreground colors - def_color!(fg_color: black => Color::Black); - def_color!(fg_color: dark_grey => Color::DarkGrey); - def_color!(fg_color: red => Color::Red); - def_color!(fg_color: dark_red => Color::DarkRed); - def_color!(fg_color: green => Color::Green); - def_color!(fg_color: dark_green => Color::DarkGreen); - def_color!(fg_color: yellow => Color::Yellow); - def_color!(fg_color: dark_yellow => Color::DarkYellow); - def_color!(fg_color: blue => Color::Blue); - def_color!(fg_color: dark_blue => Color::DarkBlue); - def_color!(fg_color: magenta => Color::Magenta); - def_color!(fg_color: dark_magenta => Color::DarkMagenta); - def_color!(fg_color: cyan => Color::Cyan); - def_color!(fg_color: dark_cyan => Color::DarkCyan); - def_color!(fg_color: white => Color::White); - def_color!(fg_color: grey => Color::Grey); - - // background colors - def_color!(bg_color: on_black => Color::Black); - def_color!(bg_color: on_dark_grey => Color::DarkGrey); - def_color!(bg_color: on_red => Color::Red); - def_color!(bg_color: on_dark_red => Color::DarkRed); - def_color!(bg_color: on_green => Color::Green); - def_color!(bg_color: on_dark_green => Color::DarkGreen); - def_color!(bg_color: on_yellow => Color::Yellow); - def_color!(bg_color: on_dark_yellow => Color::DarkYellow); - def_color!(bg_color: on_blue => Color::Blue); - def_color!(bg_color: on_dark_blue => Color::DarkBlue); - def_color!(bg_color: on_magenta => Color::Magenta); - def_color!(bg_color: on_dark_magenta => Color::DarkMagenta); - def_color!(bg_color: on_cyan => Color::Cyan); - def_color!(bg_color: on_dark_cyan => Color::DarkCyan); - def_color!(bg_color: on_white => Color::White); - def_color!(bg_color: on_grey => Color::Grey); -} - -impl Styler for StyledObject { - def_attr!(reset => Attribute::Reset); - def_attr!(bold => Attribute::Bold); - def_attr!(underlined => Attribute::Underlined); - def_attr!(reverse => Attribute::Reverse); - def_attr!(dim => Attribute::Dim); - def_attr!(italic => Attribute::Italic); - def_attr!(negative => Attribute::Reverse); - def_attr!(slow_blink => Attribute::SlowBlink); - def_attr!(rapid_blink => Attribute::RapidBlink); - def_attr!(hidden => Attribute::Hidden); - def_attr!(crossed_out => Attribute::CrossedOut); -} diff --git a/crossterm_style/src/traits.rs b/crossterm_style/src/traits.rs deleted file mode 100644 index 6867463..0000000 --- a/crossterm_style/src/traits.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::fmt::Display; - -use crate::StyledObject; - -/// Provides a set of methods to color any type implementing `Display` with attributes. -/// -/// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them. -/// -/// ```rust -/// use crossterm_style::Colorize; -/// -/// let styled_text = "Red forground color on blue background.".red().on_blue(); -/// println!("{}", styled_text); -/// ``` -pub trait Colorize { - fn black(self) -> StyledObject; - fn dark_grey(self) -> StyledObject; - fn red(self) -> StyledObject; - fn dark_red(self) -> StyledObject; - fn green(self) -> StyledObject; - fn dark_green(self) -> StyledObject; - fn yellow(self) -> StyledObject; - fn dark_yellow(self) -> StyledObject; - fn blue(self) -> StyledObject; - fn dark_blue(self) -> StyledObject; - fn magenta(self) -> StyledObject; - fn dark_magenta(self) -> StyledObject; - fn cyan(self) -> StyledObject; - fn dark_cyan(self) -> StyledObject; - fn white(self) -> StyledObject; - fn grey(self) -> StyledObject; - - fn on_black(self) -> StyledObject; - fn on_dark_grey(self) -> StyledObject; - fn on_red(self) -> StyledObject; - fn on_dark_red(self) -> StyledObject; - fn on_green(self) -> StyledObject; - fn on_dark_green(self) -> StyledObject; - fn on_yellow(self) -> StyledObject; - fn on_dark_yellow(self) -> StyledObject; - fn on_blue(self) -> StyledObject; - fn on_dark_blue(self) -> StyledObject; - fn on_magenta(self) -> StyledObject; - fn on_dark_magenta(self) -> StyledObject; - fn on_cyan(self) -> StyledObject; - fn on_dark_cyan(self) -> StyledObject; - fn on_white(self) -> StyledObject; - fn on_grey(self) -> StyledObject; -} - -/// Provides a set of methods to style any type implementing `Display` with attributes. -/// -/// This trait is implemented for `&static str` and `StyledObject` and thus the methods of this trait could be called on them. -/// -/// # Example -/// -/// ```rust -/// use crossterm_style::Styler; -/// -/// println!("{}", "Bold text".bold()); -/// println!("{}", "Underlined text".underlined()); -/// println!("{}", "Negative text".negative()); -/// ``` -pub trait Styler { - fn reset(self) -> StyledObject; - fn bold(self) -> StyledObject; - fn underlined(self) -> StyledObject; - fn reverse(self) -> StyledObject; - fn dim(self) -> StyledObject; - fn italic(self) -> StyledObject; - fn negative(self) -> StyledObject; - fn slow_blink(self) -> StyledObject; - fn rapid_blink(self) -> StyledObject; - fn hidden(self) -> StyledObject; - fn crossed_out(self) -> StyledObject; -} diff --git a/crossterm_style/src/winapi_color.rs b/crossterm_style/src/winapi_color.rs deleted file mode 100644 index 9da9419..0000000 --- a/crossterm_style/src/winapi_color.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! This is a `WinApi` specific implementation for styling related action. -//! This module is used for non supporting `ANSI` Windows terminals. - -use std::sync::Once; - -use winapi::um::wincon; - -use crossterm_utils::Result; -use crossterm_winapi::{Console, Handle, HandleType, ScreenBuffer}; - -use crate::{Color, Colored, ITerminalColor}; - -const FG_GREEN: u16 = wincon::FOREGROUND_GREEN; -const FG_RED: u16 = wincon::FOREGROUND_RED; -const FG_BLUE: u16 = wincon::FOREGROUND_BLUE; -const FG_INTENSITY: u16 = wincon::FOREGROUND_INTENSITY; - -const BG_GREEN: u16 = wincon::BACKGROUND_GREEN; -const BG_RED: u16 = wincon::BACKGROUND_RED; -const BG_BLUE: u16 = wincon::BACKGROUND_BLUE; -const BG_INTENSITY: u16 = wincon::BACKGROUND_INTENSITY; - -/// This struct is a WinApi implementation for color related actions. -pub struct WinApiColor; - -impl WinApiColor { - pub fn new() -> Box { - Box::from(WinApiColor) - } -} - -impl ITerminalColor for WinApiColor { - fn set_fg(&self, fg_color: Color) -> Result<()> { - // init the original color in case it is not set. - init_console_color()?; - - let color_value = color_value(Colored::Fg(fg_color)); - - 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 mut color: u16; - let attrs = csbi.attributes(); - let bg_color = attrs & 0x0070; - color = color_value.parse::()? | 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 & wincon::BACKGROUND_INTENSITY as u16) != 0 { - color = color | wincon::BACKGROUND_INTENSITY as u16; - } - - Console::from(**screen_buffer.handle()).set_text_attribute(color)?; - - Ok(()) - } - - fn set_bg(&self, bg_color: Color) -> Result<()> { - // init the original color in case it is not set. - init_console_color()?; - - let color_value = color_value(Colored::Bg(bg_color)); - - 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 mut color: u16; - let attrs = csbi.attributes(); - let fg_color = attrs & 0x0007; - color = fg_color | color_value.parse::()?; - - // Foreground intensity is a separate value in attrs, - // So we need to check if this was applied to the current fg color. - if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 { - color = color | wincon::FOREGROUND_INTENSITY as u16; - } - - Console::from(**screen_buffer.handle()).set_text_attribute(color)?; - - Ok(()) - } - - fn reset(&self) -> Result<()> { - // init the original color in case it is not set. - let original_color = original_console_color(); - Console::from(Handle::new(HandleType::CurrentOutputHandle)?) - .set_text_attribute(original_color)?; - - Ok(()) - } -} - -/// This will get the winapi color value from the Color and ColorType struct -fn color_value(color: Colored) -> String { - let winapi_color: u16; - - match color { - Colored::Fg(color) => { - winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => FG_INTENSITY, - Color::Red => FG_INTENSITY | FG_RED, - Color::DarkRed => FG_RED, - Color::Green => FG_INTENSITY | FG_GREEN, - Color::DarkGreen => FG_GREEN, - Color::Yellow => FG_INTENSITY | FG_GREEN | FG_RED, - Color::DarkYellow => FG_GREEN | FG_RED, - Color::Blue => FG_INTENSITY | FG_BLUE, - Color::DarkBlue => FG_BLUE, - Color::Magenta => FG_INTENSITY | FG_RED | FG_BLUE, - Color::DarkMagenta => FG_RED | FG_BLUE, - Color::Cyan => FG_INTENSITY | FG_GREEN | FG_BLUE, - Color::DarkCyan => FG_GREEN | FG_BLUE, - Color::White => FG_RED | FG_GREEN | FG_BLUE, - Color::Grey => FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE, - - Color::Reset => { - // init the original color in case it is not set. - let mut original_color = original_console_color(); - - const REMOVE_BG_MASK: u16 = BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE; - // remove all background values from the original color, we don't want to reset those. - original_color &= !(REMOVE_BG_MASK); - - original_color - } - - /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, - }; - } - Colored::Bg(color) => { - winapi_color = match color { - Color::Black => 0, - Color::DarkGrey => BG_INTENSITY, - Color::Red => BG_INTENSITY | BG_RED, - Color::DarkRed => BG_RED, - Color::Green => BG_INTENSITY | BG_GREEN, - Color::DarkGreen => BG_GREEN, - Color::Yellow => BG_INTENSITY | BG_GREEN | BG_RED, - Color::DarkYellow => BG_GREEN | BG_RED, - Color::Blue => BG_INTENSITY | BG_BLUE, - Color::DarkBlue => BG_BLUE, - Color::Magenta => BG_INTENSITY | BG_RED | BG_BLUE, - Color::DarkMagenta => BG_RED | BG_BLUE, - Color::Cyan => BG_INTENSITY | BG_GREEN | BG_BLUE, - Color::DarkCyan => BG_GREEN | BG_BLUE, - Color::White => BG_INTENSITY | BG_RED | BG_GREEN | BG_BLUE, - Color::Grey => BG_RED | BG_GREEN | BG_BLUE, - - Color::Reset => { - // init the original color in case it is not set. - let mut original_color = original_console_color(); - - const REMOVE_FG_MASK: u16 = FG_INTENSITY | FG_RED | FG_GREEN | FG_BLUE; - // remove all foreground values from the original color, we don't want to reset those. - original_color &= !(REMOVE_FG_MASK); - original_color - } - /* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/ - Color::Rgb { r: _, g: _, b: _ } => 0, - Color::AnsiValue(_val) => 0, - }; - } - }; - - winapi_color.to_string() -} - -fn init_console_color() -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - - let attr = screen_buffer.info()?.attributes(); - - GET_ORIGINAL_CONSOLE_COLOR.call_once(|| { - unsafe { ORIGINAL_CONSOLE_COLOR = attr }; - }); - Ok(()) -} - -fn original_console_color() -> u16 { - return unsafe { ORIGINAL_CONSOLE_COLOR }; -} - -static GET_ORIGINAL_CONSOLE_COLOR: Once = Once::new(); -static mut ORIGINAL_CONSOLE_COLOR: u16 = 0; diff --git a/crossterm_terminal/.gitignore b/crossterm_terminal/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_terminal/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_terminal/CHANGELOG.md b/crossterm_terminal/CHANGELOG.md deleted file mode 100644 index 48faf0e..0000000 --- a/crossterm_terminal/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changes crossterm_terminal 0.3.0 -- `Terminal::terminal_size` to `Terminal::size` -- `Terminal::size()` returns `Result<(u16, u16)>` -- Return written bytes: [return-written-bytes] -- Synced all `i16` values for indexing: set size, get size, scrolling to `u16` values -- Synced set/get terminal size behaviour: [fixed-get-set-terminal-size] -- `ExecutableCommand::queue` returns `crossterm::Result` -- `QueueableCommand::queue` returns `crossterm::Result` -- Command API takes mutable self instead of self - -[return-written-bytes]: https://github.com/crossterm-rs/crossterm/pull/212 -[fixed-get-set-terminal-size]: https://github.com/crossterm-rs/crossterm/pull/242 - -# Changes crossterm_terminal 0.2.2 -- Terminal size Linux was not 0-based. -- Made FreeBSD compile - -# Changes crossterm_terminal 0.2 -- Removed `Terminal:from_output()` - -# Changes crossterm_terminal 0.1 -- Moved out of `crossterm` 5.4 crate. \ No newline at end of file diff --git a/crossterm_terminal/Cargo.toml b/crossterm_terminal/Cargo.toml deleted file mode 100644 index d72b0fa..0000000 --- a/crossterm_terminal/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "crossterm_terminal" -version = "0.3.0" -authors = ["T. Post"] -description = "A cross-platform library for doing terminal related actions." -repository = "https://github.com/crossterm-rs/crossterm" -documentation = "https://docs.rs/crossterm_terminal/" -license = "MIT" -keywords = ["terminal", "clear", "console", "crossterm", "size"] -exclude = ["target", "Cargo.lock"] -readme = "README.md" -edition = "2018" - -[target.'cfg(windows)'.dependencies] -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0"} - -[target.'cfg(unix)'.dependencies] -libc = "0.2.51" - -[dependencies] -crossterm_utils = { path="../crossterm_utils", version = "0.3.0"} -crossterm_cursor = { path="../crossterm_cursor", version = "0.3.0"} -serde = { version = "1.0.0", features = ["derive"], optional = true } diff --git a/crossterm_terminal/LICENSE b/crossterm_terminal/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_terminal/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_terminal/README.md b/crossterm_terminal/README.md deleted file mode 100644 index 8ad7cd2..0000000 --- a/crossterm_terminal/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# Crossterm Terminal | cross-platform terminal actions. - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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://crossterm-rs.github.io/crossterm/docs/feature_flags.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 - -All examples of how `crossterm_terminal` works can be found in the [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) directory. - -Add the `crossterm_terminal` package to your `Cargo.toml` file. - -``` -[dependencies] -crossterm_terminal = "0.2" - -``` -And import the `crossterm_terminal` modules you want to use. - -```rust -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 -- Multithreaded (send, sync) -- Detailed Documentation -- Few Dependencies -- Terminal - - Clearing (all lines, current line, from cursor down and up, until new line) - - Scrolling (up, down) - - Terminal Size (get/set) - - Exit Current Process - -## Command API - -My first recommendation is to use the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) because this might replace some of the existing API in the future. -Because it is more convenient, faster, and easier to use. - -## Examples - -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -```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.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. - -## Authors -* **Timon Post** - *Project Owner & creator* - -## License -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_terminal/src/lib.rs b/crossterm_terminal/src/lib.rs deleted file mode 100644 index c8582bb..0000000 --- a/crossterm_terminal/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![deny(unused_imports)] - -pub use crossterm_utils::{execute, queue, Command, ExecutableCommand, QueueableCommand, Result}; - -pub use self::terminal::{terminal, Clear, ClearType, ScrollDown, ScrollUp, SetSize, Terminal}; - -mod sys; -mod terminal; diff --git a/crossterm_terminal/src/sys.rs b/crossterm_terminal/src/sys.rs deleted file mode 100644 index 26b1553..0000000 --- a/crossterm_terminal/src/sys.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[cfg(unix)] -pub use self::unix::{exit, get_terminal_size}; -#[cfg(windows)] -pub use self::winapi::{exit, get_terminal_size}; - -#[cfg(windows)] -pub mod winapi; - -#[cfg(unix)] -pub mod unix; diff --git a/crossterm_terminal/src/sys/unix.rs b/crossterm_terminal/src/sys/unix.rs deleted file mode 100644 index b8169b4..0000000 --- a/crossterm_terminal/src/sys/unix.rs +++ /dev/null @@ -1,25 +0,0 @@ -use libc::{ioctl, winsize, STDOUT_FILENO, TIOCGWINSZ}; - -use crossterm_utils::Result; - -pub fn exit() { - ::std::process::exit(0); -} - -/// Get the current terminal size. -pub fn get_terminal_size() -> Result<(u16, u16)> { - // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc - let mut size = winsize { - ws_row: 0, - ws_col: 0, - ws_xpixel: 0, - ws_ypixel: 0, - }; - let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ.into(), &mut size) }; - - if r == 0 { - Ok((size.ws_col, size.ws_row)) - } else { - Err(std::io::Error::last_os_error().into()) - } -} diff --git a/crossterm_terminal/src/sys/winapi.rs b/crossterm_terminal/src/sys/winapi.rs deleted file mode 100644 index 18dbf15..0000000 --- a/crossterm_terminal/src/sys/winapi.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crossterm_utils::Result; -use crossterm_winapi::ScreenBuffer; - -/// Exit the current process. -pub fn exit() { - ::std::process::exit(256); -} - -#[cfg(windows)] -pub fn get_terminal_size() -> Result<(u16, u16)> { - let terminal_size = ScreenBuffer::current()?.info()?.terminal_size(); - // windows starts counting at 0, unix at 1, add one to replicated unix behaviour. - Ok(( - (terminal_size.width + 1) as u16, - (terminal_size.height + 1) as u16, - )) -} diff --git a/crossterm_terminal/src/terminal.rs b/crossterm_terminal/src/terminal.rs deleted file mode 100644 index 2d3465d..0000000 --- a/crossterm_terminal/src/terminal.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! A module that contains all the actions related to the terminal. like clearing, resizing, pausing and scrolling the terminal. -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -use crossterm_utils::Result; - -use self::ansi_terminal::AnsiTerminal; -pub use self::terminal::{terminal, Clear, ScrollDown, ScrollUp, SetSize, Terminal}; -#[cfg(windows)] -use self::winapi_terminal::WinApiTerminal; - -mod terminal; - -mod ansi_terminal; -#[cfg(windows)] -mod winapi_terminal; - -/// Enum with the different values to clear the terminal. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] -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, -} - -/// This trait defines the actions that can be performed with the terminal color. -/// This trait can be implemented so that an concrete implementation of the ITerminalColor can fulfill. -/// the wishes to work on an specific platform. -/// -/// ## For example: -/// -/// This trait is implemented for `WinApi` (Windows specific) and `ANSI` (Unix specific), -/// so that terminal related actions can be performed on both Unix and Windows systems. -trait ITerminal { - /// Clear the current cursor by specifying the clear type - fn clear(&self, clear_type: ClearType) -> Result<()>; - /// Get the terminal size (x,y) - fn size(&self) -> Result<(u16, u16)>; - /// Scroll `n` lines up in the current terminal. - fn scroll_up(&self, count: u16) -> Result<()>; - /// Scroll `n` lines down in the current terminal. - fn scroll_down(&self, count: u16) -> Result<()>; - /// Resize terminal to the given width and height. - fn set_size(&self, width: u16, height: u16) -> Result<()>; -} diff --git a/crossterm_terminal/src/terminal/ansi_terminal.rs b/crossterm_terminal/src/terminal/ansi_terminal.rs deleted file mode 100644 index 06adcf9..0000000 --- a/crossterm_terminal/src/terminal/ansi_terminal.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! 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 crossterm_cursor::TerminalCursor; -use crossterm_utils::{csi, write_cout, Result}; - -use crate::sys::get_terminal_size; - -use super::{ClearType, ITerminal}; - -pub static CLEAR_ALL: &'static str = csi!("2J"); -pub static CLEAR_FROM_CURSOR_DOWN: &'static str = csi!("J"); -pub static CLEAR_FROM_CURSOR_UP: &'static str = csi!("1J"); -pub static CLEAR_FROM_CURRENT_LINE: &'static str = csi!("2K"); -pub static CLEAR_UNTIL_NEW_LINE: &'static str = csi!("K"); - -pub fn get_scroll_up_ansi(count: u16) -> String { - format!(csi!("{}S"), count) -} - -pub fn get_scroll_down_ansi(count: u16) -> String { - format!(csi!("{}T"), count) -} - -pub fn get_set_size_ansi(width: u16, height: u16) -> String { - format!(csi!("8;{};{}t"), height, width) -} - -/// This struct is an ansi escape code implementation for terminal related actions. -pub struct AnsiTerminal; - -impl AnsiTerminal { - pub fn new() -> AnsiTerminal { - AnsiTerminal - } -} - -impl ITerminal for AnsiTerminal { - fn clear(&self, clear_type: ClearType) -> Result<()> { - match clear_type { - ClearType::All => { - write_cout!(CLEAR_ALL)?; - TerminalCursor::new().goto(0, 0)?; - } - ClearType::FromCursorDown => { - write_cout!(CLEAR_FROM_CURSOR_DOWN)?; - } - ClearType::FromCursorUp => { - write_cout!(CLEAR_FROM_CURSOR_UP)?; - } - ClearType::CurrentLine => { - write_cout!(CLEAR_FROM_CURRENT_LINE)?; - } - ClearType::UntilNewLine => { - write_cout!(CLEAR_UNTIL_NEW_LINE)?; - } - }; - Ok(()) - } - - fn size(&self) -> Result<(u16, u16)> { - get_terminal_size() - } - - fn scroll_up(&self, count: u16) -> Result<()> { - write_cout!(get_scroll_up_ansi(count))?; - Ok(()) - } - - fn scroll_down(&self, count: u16) -> Result<()> { - write_cout!(get_scroll_down_ansi(count))?; - Ok(()) - } - - fn set_size(&self, width: u16, height: u16) -> Result<()> { - write_cout!(get_set_size_ansi(width, height))?; - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use std::{thread, time}; - - use super::{AnsiTerminal, ITerminal}; - - /* ======================== ANSI =========================== */ - #[test] - // TODO - Test is disabled, because it's failing on Travis CI - #[ignore] - fn test_resize_ansi() { - if try_enable_ansi() { - let terminal = AnsiTerminal::new(); - - let (width, height) = terminal.size().unwrap(); - - terminal.set_size(35, 35).unwrap(); - // see issue: https://github.com/eminence/terminal-size/issues/11 - thread::sleep(time::Duration::from_millis(30)); - assert_eq!((35, 35), terminal.size().unwrap()); - - // reset to previous size - terminal.set_size(width, height).unwrap(); - // see issue: https://github.com/eminence/terminal-size/issues/11 - thread::sleep(time::Duration::from_millis(30)); - assert_eq!((width, height), terminal.size().unwrap()); - } - } - - fn try_enable_ansi() -> bool { - #[cfg(windows)] - { - if cfg!(target_os = "windows") { - use crossterm_utils::sys::winapi::ansi::set_virtual_terminal_processing; - - // 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(_) => return false, - } - } - } - - true - } - -} diff --git a/crossterm_terminal/src/terminal/terminal.rs b/crossterm_terminal/src/terminal/terminal.rs deleted file mode 100644 index 18d85dd..0000000 --- a/crossterm_terminal/src/terminal/terminal.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! 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 std::fmt; - -#[cfg(windows)] -use crossterm_utils::supports_ansi; -use crossterm_utils::{impl_display, write_cout, Command, Result}; - -#[cfg(windows)] -use super::WinApiTerminal; -use super::{AnsiTerminal, ClearType, ITerminal}; - -/// Allows you to preform actions on the terminal. -/// -/// # Features: -/// -/// - 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 -/// -/// Check `/examples/` in the library for more specific examples. -pub struct Terminal { - #[cfg(windows)] - terminal: Box<(dyn ITerminal + Sync + Send)>, - #[cfg(unix)] - terminal: AnsiTerminal, -} - -impl Terminal { - /// Create new terminal instance whereon terminal related actions can be performed. - pub fn new() -> Terminal { - #[cfg(windows)] - let terminal = if supports_ansi() { - Box::from(AnsiTerminal::new()) as Box<(dyn ITerminal + Sync + Send)> - } else { - WinApiTerminal::new() as Box<(dyn ITerminal + Sync + Send)> - }; - - #[cfg(unix)] - let terminal = AnsiTerminal::new(); - - Terminal { terminal } - } - - /// Clear the current cursor by specifying the `ClearType`. - /// - /// # Example - /// ```rust - /// # use crossterm_terminal as crossterm; - /// # use crossterm_terminal::terminal; - /// let mut term = terminal(); - /// - /// // clear all cells in terminal. - /// term.clear(crossterm::ClearType::All); - /// // clear all cells from the cursor position downwards in terminal. - /// term.clear(crossterm::ClearType::FromCursorDown); - /// // clear all cells from the cursor position upwards in terminal. - /// term.clear(crossterm::ClearType::FromCursorUp); - /// // clear current line cells in terminal. - /// term.clear(crossterm::ClearType::CurrentLine); - /// // clear all cells from cursor position until new line in terminal. - /// term.clear(crossterm::ClearType::UntilNewLine); - /// ``` - pub fn clear(&self, clear_type: ClearType) -> Result<()> { - self.terminal.clear(clear_type) - } - - /// Get the terminal size `(x,y)`. - pub fn size(&self) -> Result<(u16, u16)> { - self.terminal.size() - } - - /// Scroll `n` lines up in the current terminal. - /// - /// # Parameter - /// - `count`: the number of rows should be shifted up. - pub fn scroll_up(&self, count: u16) -> Result<()> { - self.terminal.scroll_up(count) - } - - /// Scroll `n` lines down in the current terminal. - /// - /// # Parameter - /// - `count`: the number of rows should be shifted down. - pub fn scroll_down(&self, count: u16) -> Result<()> { - self.terminal.scroll_down(count) - } - - /// Set the terminal size. Note that not all terminals can be set to a very small scale. - /// - /// ```rust - /// # use crossterm_terminal::terminal; - /// let mut term = terminal(); - /// - /// // Set of the size to X: 10 and Y: 10 - /// let size = term.set_size(10,10); - /// ``` - pub fn set_size(&self, width: u16, height: u16) -> Result<()> { - self.terminal.set_size(width, height) - } - - // TODO - Marked as no_run, because it's failing on Travis CI - /// Exit the current process. - /// - /// ```no_run - /// # use crossterm_terminal::terminal; - /// let mut term = terminal(); - /// - /// let size = term.exit(); - /// ``` - pub fn exit(&self) { - crate::sys::exit(); - } - - /// Write any displayable content to the current terminal screen. - /// - /// ```rust - /// # use crossterm_terminal::terminal; - /// let mut term = terminal(); - /// - /// let size = term.write("Some text \n Some text on new line"); - /// ``` - /// - /// This will also flush the standard output. - pub fn write(&self, value: D) -> Result { - write_cout!(format!("{}", value)) - } -} - -/// Get a `Terminal` instance whereon terminal related actions can be performed. -pub fn terminal() -> Terminal { - Terminal::new() -} - -/// When executed, this command will scroll up the terminal buffer by the given number of times. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct ScrollUp(pub u16); - -impl Command for ScrollUp { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - super::ansi_terminal::get_scroll_up_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiTerminal::new().scroll_up(self.0) - } -} - -/// When executed, this command will scroll down the terminal buffer by the given number of times. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct ScrollDown(pub u16); - -impl Command for ScrollDown { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - super::ansi_terminal::get_scroll_down_ansi(self.0) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiTerminal::new().scroll_down(self.0) - } -} - -/// When executed, this command will clear the terminal buffer based on the type provided. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Clear(pub ClearType); - -impl Command for Clear { - type AnsiType = &'static str; - - fn ansi_code(&self) -> Self::AnsiType { - match self.0 { - ClearType::All => { - return super::ansi_terminal::CLEAR_ALL; - } - ClearType::FromCursorDown => { - return super::ansi_terminal::CLEAR_FROM_CURSOR_DOWN; - } - ClearType::FromCursorUp => { - return super::ansi_terminal::CLEAR_FROM_CURSOR_UP; - } - ClearType::CurrentLine => return super::ansi_terminal::CLEAR_FROM_CURRENT_LINE, - ClearType::UntilNewLine => return super::ansi_terminal::CLEAR_UNTIL_NEW_LINE, - } - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiTerminal::new().clear(self.0.clone()) - } -} - -/// When executed, this command will set the terminal sie to the given (`width` and `height`) -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct SetSize(pub u16, pub u16); - -impl Command for SetSize { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - super::ansi_terminal::get_set_size_ansi(self.0, self.1) - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - WinApiTerminal::new().set_size(self.0, self.1) - } -} - -impl_display!(for ScrollUp); -impl_display!(for ScrollDown); -impl_display!(for SetSize); -impl_display!(for Clear); diff --git a/crossterm_terminal/src/terminal/winapi_terminal.rs b/crossterm_terminal/src/terminal/winapi_terminal.rs deleted file mode 100644 index 42ca353..0000000 --- a/crossterm_terminal/src/terminal/winapi_terminal.rs +++ /dev/null @@ -1,297 +0,0 @@ -//! This is a `WINAPI` specific implementation for terminal related action. -//! This module is used for non supporting `ANSI` windows terminals. -//! -//! Windows versions lower then windows 10 are not supporting ANSI codes. Those versions will use this implementation instead. - -use crossterm_cursor::sys::winapi::Cursor; -use crossterm_utils::{ErrorKind, Result}; -use crossterm_winapi::{Console, Coord, Handle, ScreenBuffer, Size}; - -use crate::sys::winapi::get_terminal_size; - -use super::{ClearType, ITerminal}; - -/// This struct is a winapi implementation for terminal related actions. -pub struct WinApiTerminal; - -impl WinApiTerminal { - pub fn new() -> Box { - Box::from(WinApiTerminal {}) - } -} - -impl ITerminal for WinApiTerminal { - fn clear(&self, clear_type: ClearType) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - - let pos = csbi.cursor_pos(); - let buffer_size = csbi.buffer_size(); - let current_attribute = csbi.attributes(); - - match clear_type { - ClearType::All => { - clear_entire_screen(buffer_size, current_attribute)?; - } - ClearType::FromCursorDown => clear_after_cursor(pos, buffer_size, current_attribute)?, - ClearType::FromCursorUp => clear_before_cursor(pos, buffer_size, current_attribute)?, - ClearType::CurrentLine => clear_current_line(pos, buffer_size, current_attribute)?, - ClearType::UntilNewLine => clear_until_line(pos, buffer_size, current_attribute)?, - }; - Ok(()) - } - - fn size(&self) -> Result<(u16, u16)> { - get_terminal_size() - } - - fn scroll_up(&self, count: u16) -> Result<()> { - let csbi = ScreenBuffer::current()?; - let mut window = csbi.info()?.terminal_window(); - - // Check whether the window is too close to the screen buffer top - let count = count as i16; - if window.top >= count { - window.top -= count; // move top down - window.bottom = count; // move bottom down - - Console::new()?.set_console_info(false, window)?; - } - Ok(()) - } - - fn scroll_down(&self, count: u16) -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - let csbi = screen_buffer.info()?; - let mut window = csbi.terminal_window(); - let buffer_size = csbi.buffer_size(); - - // Check whether the window is too close to the screen buffer top - let count = count as i16; - if window.bottom < buffer_size.height - count { - window.top += count; // move top down - window.bottom += count; // move bottom down - - Console::new()?.set_console_info(false, window)?; - } - Ok(()) - } - - /// Set the current terminal size - fn set_size(&self, width: u16, height: u16) -> Result<()> { - if width <= 0 { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Cannot set the terminal width lower than 1", - ))); - } - - if height <= 0 { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Cannot set the terminal height lower then 1", - ))); - } - - // Get the position of the current console window - let screen_buffer = ScreenBuffer::current()?; - let console = Console::from(**screen_buffer.handle()); - let csbi = screen_buffer.info()?; - - let current_size = csbi.buffer_size(); - let window = csbi.terminal_window(); - - let mut new_size = Size::new(current_size.width, current_size.height); - - // If the buffer is smaller than this new window size, resize the - // buffer to be large enough. Include window position. - let mut resize_buffer = false; - - let width = width as i16; - if current_size.width < window.left + width { - if window.left >= i16::max_value() - width { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Argument out of range when setting terminal width.", - ))); - } - - new_size.width = window.left + width; - resize_buffer = true; - } - let height = height as i16; - if current_size.height < window.top + height { - if window.top >= i16::max_value() - height { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Argument out of range when setting terminal height.", - ))); - } - - new_size.height = window.top + height; - resize_buffer = true; - } - - if resize_buffer { - if let Err(_) = screen_buffer.set_size(new_size.width - 1, new_size.height - 1) { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Something went wrong when setting screen buffer size.", - ))); - } - } - - let mut window = window.clone(); - // Preserve the position, but change the size. - window.bottom = window.top + height - 1; - window.right = window.left + width - 1; - console.set_console_info(true, window)?; - - // If we resized the buffer, un-resize it. - if resize_buffer { - if let Err(_) = screen_buffer.set_size(current_size.width - 1, current_size.height - 1) - { - return Err(ErrorKind::ResizingTerminalFailure(String::from( - "Something went wrong when setting screen buffer size.", - ))); - } - } - - let bounds = console.largest_window_size(); - - if width > bounds.x { - return Err(ErrorKind::ResizingTerminalFailure(format!( - "Argument width: {} out of range when setting terminal width.", - width - ))); - } - if height > bounds.y { - return Err(ErrorKind::ResizingTerminalFailure(format!( - "Argument height: {} out of range when setting terminal height", - width - ))); - } - - Ok(()) - } -} - -pub fn clear_after_cursor( - location: Coord, - buffer_size: Size, - current_attribute: u16, -) -> Result<()> { - let (mut x, mut y) = (location.x, location.y); - - // if cursor position is at the outer right position - if x as i16 > buffer_size.width { - y += 1; - x = 0; - } - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; - - clear(start_location, cells_to_write, current_attribute) -} - -pub fn clear_before_cursor( - location: Coord, - buffer_size: Size, - current_attribute: u16, -) -> Result<()> { - let (xpos, ypos) = (location.x, location.y); - - // one cell after cursor position - let x = 0; - // one at row of cursor position - let y = 0; - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = (buffer_size.width as u32 * ypos as u32) + (xpos as u32 + 1); - - // clear everything before cursor position - clear(start_location, cells_to_write, current_attribute) -} - -pub fn clear_entire_screen(buffer_size: Size, current_attribute: u16) -> Result<()> { - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32; - - // location where to start clearing - let start_location = Coord::new(0, 0); - - // clear the entire screen - clear(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at cell 0,0 - let cursor = Cursor::new()?; - cursor.goto(0, 0)?; - Ok(()) -} - -pub fn clear_current_line( - location: Coord, - buffer_size: Size, - current_attribute: u16, -) -> Result<()> { - // location where to start clearing - let start_location = Coord::new(0, location.y); - - // get sum cells before cursor - let cells_to_write = buffer_size.width as u32; - - // clear the whole current line - clear(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at cell 1 on current row - let cursor = Cursor::new()?; - cursor.goto(0, location.y)?; - Ok(()) -} - -pub fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16) -> Result<()> { - let (x, y) = (location.x, location.y); - - // location where to start clearing - let start_location = Coord::new(x, y); - - // get sum cells before cursor - let cells_to_write = (buffer_size.width - x as i16) as u32; - - // clear until the current line - clear(start_location, cells_to_write, current_attribute)?; - - // put the cursor back at original cursor position before we did the clearing - let cursor = Cursor::new()?; - cursor.goto(x, y)?; - Ok(()) -} - -fn clear(start_location: Coord, cells_to_write: u32, current_attribute: u16) -> Result<()> { - let console = Console::from(Handle::current_out_handle()?); - console.fill_whit_character(start_location, cells_to_write, ' ')?; - console.fill_whit_attribute(start_location, cells_to_write, current_attribute)?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::{ITerminal, WinApiTerminal}; - - #[test] - fn test_resize_winapi() { - let terminal = WinApiTerminal::new(); - - let (width, height) = terminal.size().unwrap(); - - terminal.set_size(30, 30).unwrap(); - assert_eq!((30, 30), terminal.size().unwrap()); - - // reset to previous size - terminal.set_size(width, height).unwrap(); - assert_eq!((width, height), terminal.size().unwrap()); - } -} diff --git a/crossterm_utils/.gitignore b/crossterm_utils/.gitignore deleted file mode 100644 index 53eaa21..0000000 --- a/crossterm_utils/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -**/*.rs.bk diff --git a/crossterm_utils/Cargo.toml b/crossterm_utils/Cargo.toml deleted file mode 100644 index 06d57cc..0000000 --- a/crossterm_utils/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "crossterm_utils" -version = "0.3.0" -authors = ["T. Post"] -description = "Common logic used by the crossterm crates." -repository = "https://github.com/crossterm-rs/crossterm" -documentation = "https://docs.rs/crossterm_utils/" -license = "MIT" -keywords = ["terminal", "abstractions", "crossterm", "windows", "screen_buffer"] -exclude = ["target", "Cargo.lock"] -readme = "README.md" -edition = "2018" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.8", features = ["wincon"] } -crossterm_winapi = { path="../crossterm_winapi", version = "0.2.0"} - -[target.'cfg(unix)'.dependencies] -libc = "0.2.51" diff --git a/crossterm_utils/LICENSE b/crossterm_utils/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_utils/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_utils/README.md b/crossterm_utils/README.md deleted file mode 100644 index 44caa7e..0000000 --- a/crossterm_utils/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Crossterm Utils | crossterm common used code. - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] [![Join us on Discord][s5]][l5] - -[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/ - -[s5]: https://img.shields.io/discord/560857607196377088.svg?logo=discord -[l5]: https://discord.gg/K4nyTDB. - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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. - -## Authors - -* **Timon Post** - *Project Owner & creator* - -## License - -This project is licensed under the MIT License - see the [LICENSE.md](./LICENSE) file for details diff --git a/crossterm_utils/src/command.rs b/crossterm_utils/src/command.rs deleted file mode 100644 index b9e357d..0000000 --- a/crossterm_utils/src/command.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::fmt::Display; -use std::io::Write; - -use crate::{execute, impl_display, queue, write_cout, Result}; - -/// A command is an action that can be performed on the terminal. -/// -/// crossterm already delivers a number of commands. -/// There is no need to implement them yourself. -/// Also, you don't have to execute the commands yourself by calling a function. -/// For more information see the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) -pub trait Command { - type AnsiType: Display; - - /// Returns the ANSI code representation of this command. - /// You can manipulate the terminal behaviour by writing an ANSI escape code to the terminal. - /// You are able to use ANSI escape codes only for windows 10 and UNIX systems. - /// - /// **This method is mainly used internally by crossterm!** - fn ansi_code(&self) -> Self::AnsiType; - - /// Execute this command. - /// - /// On operating systems that do not support ANSI escape codes ( < Windows 10) we need to call WinApi to execute this command. - /// - /// **This method is mainly used internally by crossterm!** - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()>; -} - -/// A trait that defines behaviour for a command that can be used to be executed at a later time point. -/// This can be used in order to get more performance. -pub trait QueueableCommand: Sized { - /// Queues the given command for later execution. - fn queue(&mut self, command: impl Command) -> Result<&mut Self>; -} - -/// A trait that defines behaviour for a command that will be executed immediately. -pub trait ExecutableCommand: Sized { - /// Execute the given command directly. - fn execute(&mut self, command: impl Command) -> Result<&mut Self>; -} - -impl QueueableCommand for T -where - A: Display, - T: Write, -{ - /// Queue the given command for later execution. - /// - /// Queued commands will be executed in the following cases: - /// - When you manually call `flush` on the given writer. - /// - When the buffer is to full, then the terminal will flush for you. - /// - Incase of `stdout` each line, because `stdout` is line buffered. - /// - /// Check the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) for more information and all available commands. - /// - /// # Parameters - /// - [Command](./trait.Command.html) - /// - /// The command that you want to queue for later execution. - /// - /// # Remarks - /// - In the case of UNIX and windows 10, ANSI codes are written to the given 'writer'. - /// - In case of Windows versions lower than 10, a direct WinApi call will be made. - /// This is happening because windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer. - /// Because of that there is no difference between `execute` and `queue` for those windows versions. - /// - Queuing might sound that there is some scheduling going on, however, this means that we write to the stdout without flushing which will cause commands to be stored in the buffer without them being written to the terminal. - fn queue(&mut self, command: impl Command) -> Result<&mut Self> { - queue!(self, command)?; - Ok(self) - } -} - -impl ExecutableCommand for T -where - A: Display, - T: Write, -{ - /// Execute the given command directly. - /// This function will `write` the ANSI escape code to this type and call `flush`. - /// - /// In case you have many executions after on and another you can use `queue(command)` to get some better performance. - /// The `queue` function will not call `flush`. - /// - /// Check the [command API](https://crossterm-rs.github.io/crossterm/docs/command.html) for more information and all available commands. - /// - /// # Remarks - /// - In the case of UNIX and windows 10, ANSI codes are written to the given 'writer'. - /// - In case of Windows versions lower than 10, a direct WinApi call will be made. - /// This is happening because Windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer. - /// Because of that there is no difference between `execute` and `queue` for those windows versions. - fn execute(&mut self, command: impl Command) -> Result<&mut Self> { - execute!(self, command)?; - Ok(self) - } -} - -/// When executed, this command will output the given string to the terminal. -/// -/// See `crossterm/examples/command.rs` for more information on how to execute commands. -pub struct Output(pub String); - -impl Command for Output { - type AnsiType = String; - - fn ansi_code(&self) -> Self::AnsiType { - return self.0.clone(); - } - - #[cfg(windows)] - fn execute_winapi(&self) -> Result<()> { - print!("{}", self.0); - Ok(()) - } -} - -impl_display!(for Output); diff --git a/crossterm_utils/src/error.rs b/crossterm_utils/src/error.rs deleted file mode 100644 index 6ce4289..0000000 --- a/crossterm_utils/src/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Module containing error handling logic. - -use std::{ - fmt::{self, Display, Formatter}, - io, -}; - -use crate::impl_from; - -/// The `crossterm` result type. -pub type Result = std::result::Result; - -/// Wrapper for all errors that can occur in `crossterm`. -#[derive(Debug)] -pub enum ErrorKind { - IoError(io::Error), - FmtError(fmt::Error), - Utf8Error(std::string::FromUtf8Error), - ParseIntError(std::num::ParseIntError), - ResizingTerminalFailure(String), - - #[doc(hidden)] - __Nonexhaustive, -} - -impl std::error::Error for ErrorKind { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - ErrorKind::IoError(e) => Some(e), - ErrorKind::FmtError(e) => Some(e), - ErrorKind::Utf8Error(e) => Some(e), - ErrorKind::ParseIntError(e) => Some(e), - _ => None, - } - } -} - -impl Display for ErrorKind { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - match *self { - ErrorKind::IoError(_) => write!(fmt, "IO-error occurred"), - ErrorKind::ResizingTerminalFailure(_) => write!(fmt, "Cannot resize the terminal"), - _ => write!(fmt, "Some error has occurred"), - } - } -} - -impl_from!(io::Error, ErrorKind::IoError); -impl_from!(fmt::Error, ErrorKind::FmtError); -impl_from!(std::string::FromUtf8Error, ErrorKind::Utf8Error); -impl_from!(std::num::ParseIntError, ErrorKind::ParseIntError); diff --git a/crossterm_utils/src/functions.rs b/crossterm_utils/src/functions.rs deleted file mode 100644 index c2656c2..0000000 --- a/crossterm_utils/src/functions.rs +++ /dev/null @@ -1,42 +0,0 @@ -#[cfg(windows)] -use crate::sys::winapi::ansi::set_virtual_terminal_processing; - -#[cfg(windows)] -pub fn supports_ansi() -> bool { - // 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. - - if is_specific_term() { - return true; - } - - // if it is not listed we should try with WinApi to check if we do support ANSI-codes. - set_virtual_terminal_processing(true) - .map(|_| true) - .unwrap_or(false) -} - -// 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 -#[cfg(windows)] -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, - } -} diff --git a/crossterm_utils/src/lib.rs b/crossterm_utils/src/lib.rs deleted file mode 100644 index 8d47582..0000000 --- a/crossterm_utils/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![deny(unused_imports)] - -pub use self::command::{Command, ExecutableCommand, Output, QueueableCommand}; -pub use self::error::{ErrorKind, Result}; -#[cfg(windows)] -pub use self::functions::supports_ansi; - -mod command; -pub mod error; -mod functions; -pub mod macros; -pub mod sys; diff --git a/crossterm_utils/src/macros.rs b/crossterm_utils/src/macros.rs deleted file mode 100644 index ae8e6e9..0000000 --- a/crossterm_utils/src/macros.rs +++ /dev/null @@ -1,203 +0,0 @@ -/// Append a the first few characters of an ANSI escape code to the given string. -#[macro_export] -macro_rules! csi { - ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) }; -} - -/// Write a string to standard output whereafter the stdout will be flushed. -#[macro_export] -macro_rules! write_cout { - ($write:expr, $string:expr) => {{ - use $crate::ErrorKind; - - let fmt = format!("{}", $string); - let bytes = fmt.as_bytes(); - - $write - .write_all(bytes) - .and_then(|_| $write.flush().map(|_| bytes.len())) - .map_err(ErrorKind::IoError) - }}; - ($string:expr) => {{ - // Bring Write into the scope and ignore unused imports if it's - // already imported by the user - #[allow(unused_imports)] - use std::io::Write; - write_cout!(::std::io::stdout(), $string) - }}; -} - -/// Queue one or more command(s) for execution in the near future. -/// -/// Queued commands will be executed in the following cases: -/// - When you manually call `flush` on the given writer. -/// - When the buffer is to full, then the terminal will flush for you. -/// - Incase of `stdout` each line, because `stdout` is line buffered. -/// -/// Check [here](https://crossterm-rs.github.io/crossterm/docs/command.html) for more information and all availible commands. -/// -/// # Parameters -/// - [std::io::Writer](https://doc.rust-lang.org/std/io/trait.Write.html) -/// -/// Crossterm will write the ANSI escape codes to this given writer (No flush will be done). -/// - [Command](./trait.Command.html) -/// -/// Give one or more commands that you want to queue for execution -/// -/// # Example -/// ```rust -/// -/// use std::io::{Write, stdout}; -/// -/// use crossterm_utils::{queue, Output}; -/// -/// let mut stdout = stdout(); -/// -/// // will be executed when flush is called -/// queue!(stdout, Output("foo".to_string())); -/// -/// // some other code (no execution happening here) ... -/// -/// // when calling flush on stdout, all commands will be written to the stdout and therefor executed. -/// stdout.flush(); -/// ``` -/// -/// # Remarks -/// - In the case of UNIX and windows 10, ANSI codes are written to the given 'writer'. -/// - In case of Windows versions lower than 10, a direct WinApi call will be made. -/// This is happening because windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer. -/// Because of that there is no difference between `execute` and `queue` for those windows versions. -/// - Queuing might sound that there is some scheduling going on, however, this means that we write to the stdout without flushing which will cause commands to be stored in the buffer without them being written to the terminal. -#[macro_export] -macro_rules! queue { - ($write:expr, $($command:expr), *) => {{ - // Silent warning when the macro is used inside the `command` module - #[allow(unused_imports)] - use $crate::Command; - let mut error = None; - - $( - #[cfg(windows)] - { - if $crate::supports_ansi() { - match write!($write, "{}", $command.ansi_code()) { - Err(e) => { - error = Some(Err($crate::ErrorKind::from(e))); - } - _ => {} - }; - } else { - match $command.execute_winapi() { - Err(e) => { - error = Some(Err($crate::ErrorKind::from(e))); - } - _ => {} - }; - }; - } - #[cfg(unix)] - match write!($write, "{}", $command.ansi_code()) { - Err(e) => { - error = Some(Err($crate::ErrorKind::from(e))); - } - _ => {} - }; - )* - - if let Some(error) = error { - error - } else { - Ok(()) - } - }} -} - -/// Execute one or more command(s) -/// -/// Check [here](https://crossterm-rs.github.io/crossterm/docs/command.html) for more information and all availible commands. -/// -/// # Parameters -/// - [std::io::Writer](https://doc.rust-lang.org/std/io/trait.Write.html) -/// -/// Crossterm will write the ANSI escape codes to this given. (A flush will be done) -/// - [Command](./trait.Command.html) -/// -/// Give one or more commands that you want to execute -/// -/// # Example -/// ```rust -/// use std::io::Write; -/// -/// use crossterm_utils::{execute, Output}; -/// -/// // will be executed directly -/// execute!(std::io::stdout(), Output("foo".to_string())); -/// -/// // will be executed directly -/// execute!(std::io::stdout(), Output("foo".to_string()), Output("bar".to_string())); -/// ``` -/// -/// # Remarks -/// - In the case of UNIX and windows 10, ANSI codes are written to the given 'writer'. -/// - In case of Windows versions lower than 10, a direct WinApi call will be made. -/// This is happening because Windows versions lower then 10 do not support ANSI codes, and thus they can't be written to the given buffer. -/// Because of that there is no difference between `execute` and `queue` for those windows versions. -#[macro_export] -macro_rules! execute { - ($write:expr, $($command:expr), *) => {{ - // Silent warning when the macro is used inside the `command` module - #[allow(unused_imports)] - use $crate::{Command, write_cout}; - let mut error = None; - - $( - #[cfg(windows)] - { - if $crate::supports_ansi() { - if let Err(e) = write_cout!($write, $command.ansi_code()) { - error = Some($crate::ErrorKind::from(e)); - }; - } else { - if let Err(e) = $command.execute_winapi() { - error = Some($crate::ErrorKind::from(e)); - }; - }; - } - #[cfg(unix)] - { - if let Err(e) = write_cout!($write, $command.ansi_code()) { - error = Some($crate::ErrorKind::from(e)); - } - } - )* - - if let Some(error) = error { - Err(error) - } else { - Ok(()) - } - }} -} - -#[macro_export] -macro_rules! impl_display { - (for $($t:ty),+) => { - $(impl ::std::fmt::Display for $t { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> { - use $crate::Command; - write!(f, "{}", self.ansi_code()) - } - })* - } -} - -#[macro_export] -macro_rules! impl_from { - ($from:path, $to:expr) => { - impl From<$from> for ErrorKind { - fn from(e: $from) -> Self { - $to(e) - } - } - }; -} diff --git a/crossterm_utils/src/sys.rs b/crossterm_utils/src/sys.rs deleted file mode 100644 index 5ea3379..0000000 --- a/crossterm_utils/src/sys.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(windows)] -pub mod winapi; - -#[cfg(unix)] -pub mod unix; diff --git a/crossterm_utils/src/sys/unix.rs b/crossterm_utils/src/sys/unix.rs deleted file mode 100644 index 0c376fc..0000000 --- a/crossterm_utils/src/sys/unix.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! This module contains all `unix` specific terminal related logic. - -use std::{io, mem}; - -pub use libc::{c_int, termios as Termios}; - -use crate::{ErrorKind, Result}; - -static mut ORIGINAL_TERMINAL_MODE: Option = None; -pub static mut RAW_MODE_ENABLED: bool = false; - -fn wrap_with_result(t: i32) -> Result<()> { - if t == -1 { - Err(ErrorKind::IoError(io::Error::last_os_error())) - } else { - Ok(()) - } -} - -/// Transform the given mode into an raw mode (non-canonical) mode. -pub fn raw_terminal_attr(termios: &mut Termios) { - extern "C" { - pub fn cfmakeraw(termptr: *mut Termios); - } - unsafe { cfmakeraw(termios) } -} - -pub fn get_terminal_attr() -> Result { - extern "C" { - pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int; - } - unsafe { - let mut termios = mem::zeroed(); - wrap_with_result(tcgetattr(0, &mut termios))?; - Ok(termios) - } -} - -pub fn set_terminal_attr(termios: &Termios) -> Result<()> { - extern "C" { - pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int; - } - wrap_with_result(unsafe { tcsetattr(0, 0, termios) }) -} - -pub fn enable_raw_mode() -> Result<()> { - let mut ios = get_terminal_attr()?; - let prev_ios = ios; - - unsafe { - if ORIGINAL_TERMINAL_MODE.is_none() { - ORIGINAL_TERMINAL_MODE = Some(prev_ios.clone()); - } - - RAW_MODE_ENABLED = true; - } - raw_terminal_attr(&mut ios); - set_terminal_attr(&ios)?; - Ok(()) -} - -pub fn disable_raw_mode() -> Result<()> { - unsafe { - if let Some(original_terminal_mode) = ORIGINAL_TERMINAL_MODE.as_ref() { - set_terminal_attr(original_terminal_mode)?; - RAW_MODE_ENABLED = false; - } - } - Ok(()) -} diff --git a/crossterm_utils/src/sys/winapi.rs b/crossterm_utils/src/sys/winapi.rs deleted file mode 100644 index 9a2e618..0000000 --- a/crossterm_utils/src/sys/winapi.rs +++ /dev/null @@ -1,34 +0,0 @@ -pub mod ansi { - use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; - - use crate::Result; - use crossterm_winapi::ConsoleMode; - - /// 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) -> 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 { - console_mode.set_mode(new_mode)?; - } - - Ok(()) - } -} diff --git a/crossterm_winapi/.gitignore b/crossterm_winapi/.gitignore deleted file mode 100644 index b28425c..0000000 --- a/crossterm_winapi/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -target/ -.idea/ -**/*.rs.bk -Cargo.lock \ No newline at end of file diff --git a/crossterm_winapi/CHANGELOG.md b/crossterm_winapi/CHANGELOG.md deleted file mode 100644 index 753d08c..0000000 --- a/crossterm_winapi/CHANGELOG.md +++ /dev/null @@ -1,2 +0,0 @@ -# Changes crossterm_winapi 0.2.0 -- `Console::get_handle` to `Console::handle` diff --git a/crossterm_winapi/Cargo.toml b/crossterm_winapi/Cargo.toml deleted file mode 100644 index ce7c850..0000000 --- a/crossterm_winapi/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "crossterm_winapi" -version = "0.2.0" -authors = ["T. Post"] -description = "An WinApi wrapper that provides some basic simple abstractions aground common WinApi calls" -repository = "https://github.com/crossterm-rs/crossterm" -documentation = "https://docs.rs/crossterm_winapi/" -license = "MIT" -keywords = ["winapi", "abstractions", "crossterm", "windows", "screen_buffer"] -exclude = ["target", "Cargo.lock"] -readme = "README.md" -edition = "2018" - -[target.'cfg(windows)'.dependencies] -winapi = { version = "0.3.8", features = ["winbase","consoleapi","processenv", "handleapi"] } -[package.metadata.docs.rs] -default-target = "x86_64-pc-windows-msvc" diff --git a/crossterm_winapi/LICENSE b/crossterm_winapi/LICENSE deleted file mode 100644 index 8b02a7f..0000000 --- a/crossterm_winapi/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 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. diff --git a/crossterm_winapi/README.md b/crossterm_winapi/README.md deleted file mode 100644 index ff584f9..0000000 --- a/crossterm_winapi/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Crossterm Winapi | Common WinApi Abstractions - ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] - -[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/ - -[s7]: https://travis-ci.org/crossterm-rs/crossterm.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/crossterm-rs/crossterm), but could be used apart from it. -Although, notice that it unstable right because some changes to the API could be expected. - -# Features -This crate provides some abstractions over reading input, console screen buffer, and handle. - -_The following WinApi calls_ -- 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 -- ReadConsoleW - -# Example -The [examples](https://github.com/crossterm-rs/crossterm/tree/master/examples) folder has more complete and verbose examples. - -## 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(); -} -``` diff --git a/crossterm_winapi/examples/cw_coloring_example.rs b/crossterm_winapi/examples/cw_coloring_example.rs deleted file mode 100644 index ef55773..0000000 --- a/crossterm_winapi/examples/cw_coloring_example.rs +++ /dev/null @@ -1,64 +0,0 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::{Console, ScreenBuffer}; - -#[cfg(windows)] -fn set_background_color() -> 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 new_color = fg_color | BLUE_BACKGROUND; - - // set the console text attribute to the new color value. - Console::from(**screen_buffer.handle()).set_text_attribute(new_color)?; - - Ok(()) -} - -#[cfg(windows)] -fn set_foreground_color() -> 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.handle()).set_text_attribute(color)?; - - Ok(()) -} - -#[cfg(windows)] -fn main() -> Result<()> { - set_background_color()?; - set_foreground_color() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} diff --git a/crossterm_winapi/examples/cw_console.rs b/crossterm_winapi/examples/cw_console.rs deleted file mode 100644 index ba3e595..0000000 --- a/crossterm_winapi/examples/cw_console.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::ConsoleMode; - -#[cfg(windows)] -fn change_console_mode() -> Result<()> { - let console_mode = ConsoleMode::new()?; - - // get the current console mode: - let _mode: u32 = console_mode.mode()?; - - // set the console mode (not sure if this is an actual value xp) - console_mode.set_mode(10) -} - -#[cfg(windows)] -fn main() -> Result<()> { - change_console_mode() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} diff --git a/crossterm_winapi/examples/cw_handle.rs b/crossterm_winapi/examples/cw_handle.rs deleted file mode 100644 index 2a26148..0000000 --- a/crossterm_winapi/examples/cw_handle.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::{Handle, HandleType}; - -#[cfg(windows)] -#[allow(unused_variables)] -fn main() -> Result<()> { - // see the description of the types to see what they do. - let out_put_handle = Handle::new(HandleType::OutputHandle)?; - let out_put_handle = Handle::new(HandleType::InputHandle)?; - let curr_out_put_handle = Handle::new(HandleType::CurrentOutputHandle)?; - let curr_out_put_handle = Handle::new(HandleType::CurrentInputHandle)?; - - // 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 */ - - Ok(()) -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} diff --git a/crossterm_winapi/examples/cw_screen_buffer.rs b/crossterm_winapi/examples/cw_screen_buffer.rs deleted file mode 100644 index 9d8a380..0000000 --- a/crossterm_winapi/examples/cw_screen_buffer.rs +++ /dev/null @@ -1,41 +0,0 @@ -#![allow(dead_code)] - -#[cfg(windows)] -use std::io::Result; - -#[cfg(windows)] -use crossterm_winapi::ScreenBuffer; - -#[cfg(windows)] -fn print_screen_buffer_information() -> Result<()> { - let screen_buffer = ScreenBuffer::current()?; - - // get console screen buffer information - let csbi = screen_buffer.info()?; - - println!("cursor post: {:?}", csbi.cursor_pos()); - println!("attributes: {:?}", csbi.attributes()); - println!("terminal window dimentions {:?}", csbi.terminal_window()); - println!("terminal size {:?}", csbi.terminal_size()); - - Ok(()) -} - -#[cfg(windows)] -fn multiple_screen_buffers() -> Result<()> { - // create new screen buffer - let screen_buffer = ScreenBuffer::create(); - - // which to this screen buffer - screen_buffer.show() -} - -#[cfg(windows)] -fn main() -> Result<()> { - print_screen_buffer_information() -} - -#[cfg(not(windows))] -fn main() { - println!("This example is for the Windows platform only."); -} diff --git a/crossterm_winapi/src/console.rs b/crossterm_winapi/src/console.rs deleted file mode 100644 index caaf536..0000000 --- a/crossterm_winapi/src/console.rs +++ /dev/null @@ -1,246 +0,0 @@ -use std::borrow::ToOwned; -use std::io::{self, Error, Result}; -use std::str; - -use winapi::ctypes::c_void; -use winapi::shared::minwindef::DWORD; -use winapi::shared::ntdef::NULL; -use winapi::um::consoleapi::{GetNumberOfConsoleInputEvents, ReadConsoleInputW, WriteConsoleW}; -use winapi::um::{ - wincon::{ - FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize, - SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, INPUT_RECORD, SMALL_RECT, - }, - winnt::HANDLE, -}; - -use super::{is_true, Coord, Handle, HandleType, InputRecord, WindowPositions}; - -/// 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 { - 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 { - 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 { - 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 text 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 { - // 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 = 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()) - } - - pub fn read_single_input_event(&self) -> Result> { - let buf_len = self.number_of_console_input_events()?; - - // Fast-skipping all the code below if there is nothing to read at all - if buf_len == 0 { - return Ok(None); - } - - let mut buf: Vec = Vec::with_capacity(1); - let mut size = 0; - - let a = self.read_input(&mut buf, 1, &mut size)?.1[0].to_owned(); - - // read single input event - Ok(Some(a)) - } - - pub fn read_console_input(&self) -> Result<(u32, Vec)> { - let buf_len = self.number_of_console_input_events()?; - - // Fast-skipping all the code below if there is nothing to read at all - if buf_len == 0 { - return Ok((0, vec![])); - } - - let mut buf: Vec = Vec::with_capacity(buf_len as usize); - let mut size = 0; - - self.read_input(&mut buf, buf_len, &mut size) - } - - pub fn number_of_console_input_events(&self) -> Result { - let mut buf_len: DWORD = 0; - if !is_true(unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) }) { - return Err(Error::last_os_error()); - } - - Ok(buf_len) - } - - fn read_input( - &self, - buf: &mut Vec, - buf_len: u32, - bytes_written: &mut u32, - ) -> Result<(u32, Vec)> { - if !is_true(unsafe { - ReadConsoleInputW(*self.handle, buf.as_mut_ptr(), buf_len, bytes_written) - }) { - return Err(Error::last_os_error()); - } else { - unsafe { - buf.set_len(buf_len as usize); - } - } - - Ok(( - buf_len, - buf[..(buf_len as usize)] - .iter() - .map(|x| InputRecord::from(*x)) - .collect::>(), - )) - } -} - -impl From 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 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), - } - } -} diff --git a/crossterm_winapi/src/console_mode.rs b/crossterm_winapi/src/console_mode.rs deleted file mode 100644 index a9e10c8..0000000 --- a/crossterm_winapi/src/console_mode.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::io::{Error, Result}; - -use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode}; -use winapi::um::winnt::HANDLE; - -use super::{is_true, Handle, HandleType}; - -/// 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 { - 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 { - 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 for ConsoleMode { - fn from(handle: HANDLE) -> Self { - ConsoleMode { - handle: Handle::from(handle), - } - } -} - -impl From for ConsoleMode { - fn from(handle: Handle) -> Self { - ConsoleMode { handle } - } -} - -#[cfg(test)] -mod tests { - use super::ConsoleMode; - - // TODO - Test is ignored, because it's failing on Travis CI - #[test] - #[ignore] - fn test_set_get_mode() { - let mode = ConsoleMode::new().unwrap(); - - let original_mode = mode.mode().unwrap(); - - mode.set_mode(0x0004).unwrap(); - let console_mode = mode.mode().unwrap(); - assert_eq!(console_mode & 0x0004, mode.mode().unwrap()); - - mode.set_mode(original_mode).unwrap(); - } -} diff --git a/crossterm_winapi/src/csbi.rs b/crossterm_winapi/src/csbi.rs deleted file mode 100644 index ece47b8..0000000 --- a/crossterm_winapi/src/csbi.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::mem::zeroed; - -use winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO; - -use super::{Coord, Size, WindowPositions}; - -/// 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 width 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 for ScreenBufferInfo { - fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { - ScreenBufferInfo(csbi) - } -} diff --git a/crossterm_winapi/src/handle.rs b/crossterm_winapi/src/handle.rs deleted file mode 100644 index 6fb1db3..0000000 --- a/crossterm_winapi/src/handle.rs +++ /dev/null @@ -1,188 +0,0 @@ -//! This module contains some logic for working with the console handle. - -use std::io::{self, Result}; -use std::ops::Deref; -use std::ptr::null_mut; - -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}, -}; - -/// 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 { - 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 { - let utf16: Vec = "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 { - let utf16: Vec = "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 { - 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 { - 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) -> &::Target { - &self.handle - } -} - -impl From for Handle { - fn from(handle: HANDLE) -> Self { - Handle { handle } - } -} - -#[cfg(test)] -mod tests { - use super::{Handle, HandleType}; - - #[test] - fn test_get_handle() { - assert!(Handle::new(HandleType::OutputHandle).is_ok()); - assert!(Handle::new(HandleType::InputHandle).is_ok()); - assert!(Handle::new(HandleType::CurrentOutputHandle).is_ok()); - assert!(Handle::new(HandleType::CurrentInputHandle).is_ok()); - } -} diff --git a/crossterm_winapi/src/lib.rs b/crossterm_winapi/src/lib.rs deleted file mode 100644 index 180cd8e..0000000 --- a/crossterm_winapi/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg(windows)] -#![deny(unused_imports)] - -pub use self::{ - console::Console, - console_mode::ConsoleMode, - csbi::ScreenBufferInfo, - handle::{Handle, HandleType}, - screen_buffer::ScreenBuffer, - structs::{ - ButtonState, ControlKeyState, Coord, EventFlags, InputEventType, InputRecord, - KeyEventRecord, MouseEvent, Size, WindowPositions, - }, -}; - -mod console; -mod console_mode; -mod csbi; -mod handle; -mod screen_buffer; -mod structs; - -/// 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; - } -} diff --git a/crossterm_winapi/src/screen_buffer.rs b/crossterm_winapi/src/screen_buffer.rs deleted file mode 100644 index 5cd9add..0000000 --- a/crossterm_winapi/src/screen_buffer.rs +++ /dev/null @@ -1,137 +0,0 @@ -//! This contains the logic for working with the console buffer. - -use std::io::{Error, Result}; -use std::mem::size_of; - -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 super::{is_true, Handle, HandleType, ScreenBufferInfo}; - -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 { - 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::() 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 { - 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 handle(&self) -> &Handle { - return &self.handle; - } -} - -impl From for ScreenBuffer { - fn from(handle: Handle) -> Self { - ScreenBuffer { handle } - } -} - -impl From for ScreenBuffer { - fn from(handle: HANDLE) -> Self { - ScreenBuffer { - handle: Handle::from(handle), - } - } -} - -#[cfg(test)] -mod tests { - use super::ScreenBuffer; - - #[test] - fn test_screen_buffer_info() { - let buffer = ScreenBuffer::current().unwrap(); - let info = buffer.info().unwrap(); - info.terminal_size(); - info.terminal_window(); - info.attributes(); - info.cursor_pos(); - } -} diff --git a/crossterm_winapi/src/structs.rs b/crossterm_winapi/src/structs.rs deleted file mode 100644 index bbdd07f..0000000 --- a/crossterm_winapi/src/structs.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub use self::coord::Coord; -pub use self::input::{ - ButtonState, ControlKeyState, EventFlags, InputEventType, InputRecord, KeyEventRecord, - MouseEvent, -}; -pub use self::size::Size; -pub use self::window_coords::WindowPositions; - -mod coord; -mod input; -mod size; -mod window_coords; diff --git a/crossterm_winapi/src/structs/coord.rs b/crossterm_winapi/src/structs/coord.rs deleted file mode 100644 index 1dc883c..0000000 --- a/crossterm_winapi/src/structs/coord.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! This module provides a type that represents some location/coordination. -//! For example, in WinAPi we have `COORD` which looks and feels inconvenient. -//! This module provides also some trait implementations who will make parsing and working whit `COORD` easier. - -use winapi::um::wincon::COORD; - -/// This is type represents the position of something on a certain 'x' and 'y'. -#[derive(Copy, Clone, Debug, Default, Eq, PartialOrd, PartialEq)] -pub struct Coord { - /// the position on the x axis - pub x: i16, - /// the position on the y axis - pub y: i16, -} - -impl Coord { - /// Create a new size instance by passing in the width and height. - pub fn new(x: i16, y: i16) -> Coord { - Coord { x, y } - } -} - -impl From for Coord { - fn from(coord: COORD) -> Self { - Coord::new(coord.X, coord.Y) - } -} - -impl From for COORD { - fn from(location: Coord) -> Self { - COORD { - X: location.x, - Y: location.y, - } - } -} - -impl Into<(u16, u16)> for Coord { - fn into(self) -> (u16, u16) { - (self.x as u16, self.y as u16) - } -} diff --git a/crossterm_winapi/src/structs/input.rs b/crossterm_winapi/src/structs/input.rs deleted file mode 100644 index 0b2a2d9..0000000 --- a/crossterm_winapi/src/structs/input.rs +++ /dev/null @@ -1,215 +0,0 @@ -//! This module provides a few structs to wrap common input struts to a rusty interface -//! -//! Types like: -//! - `KEY_EVENT_RECORD` -//! - `MOUSE_EVENT_RECORD` -//! - `ControlKeyState` -//! - `ButtonState` -//! - `EventFlags` -//! - `InputEventType` -//! - `INPUT_RECORD` - -use winapi::shared::minwindef::{DWORD, WORD}; -use winapi::um::wincon::{ - INPUT_RECORD_Event, KEY_EVENT_RECORD_uChar, FOCUS_EVENT, INPUT_RECORD, KEY_EVENT, - KEY_EVENT_RECORD, MENU_EVENT, MOUSE_EVENT, MOUSE_EVENT_RECORD, WINDOW_BUFFER_SIZE_EVENT, -}; - -use super::coord::Coord; - -/// Describes a keyboard input event in a console INPUT_RECORD structure. -/// link: [https://docs.microsoft.com/en-us/windows/console/key-event-record-str] -pub struct KeyEventRecord { - /// If the key is pressed, this member is TRUE. Otherwise, this member is FALSE (the key is released). - pub key_down: bool, - /// The repeat count, which indicates that a key is being held down. - /// For example, when a key is held down, you might get five events with this member equal to 1, one event with this member equal to 5, or multiple events with this member greater than or equal to 1. - pub repeat_count: u16, - /// A virtual-key code that identifies the given key in a device-independent manner. - pub virtual_key_code: WORD, - /// The virtual scan code of the given key that represents the device-dependent value generated by the keyboard hardware. - pub virtual_scan_code: u16, - /// A union of the following members. - /// - /// - UnicodeChar - /// Translated Unicode character. - /// - /// - AsciiChar - /// Translated ASCII character. - /// TODO, make this a rust type. - pub u_char: KEY_EVENT_RECORD_uChar, - /// The state of the control keys. - pub control_key_state: ControlKeyState, -} - -impl KeyEventRecord {} - -impl From for KeyEventRecord { - fn from(event: KEY_EVENT_RECORD) -> Self { - KeyEventRecord { - key_down: event.bKeyDown == 1, - repeat_count: event.wRepeatCount, - virtual_key_code: event.wVirtualKeyCode, - virtual_scan_code: event.wVirtualScanCode, - u_char: event.uChar, - control_key_state: ControlKeyState(event.dwControlKeyState), - } - } -} - -#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] -pub struct MouseEvent { - pub mouse_position: Coord, - pub button_state: ButtonState, - pub control_key_state: ControlKeyState, - pub event_flags: EventFlags, -} - -impl From for MouseEvent { - fn from(event: MOUSE_EVENT_RECORD) -> Self { - MouseEvent { - mouse_position: Coord::from(event.dwMousePosition), - button_state: ButtonState::from(event.dwButtonState), - control_key_state: ControlKeyState(event.dwControlKeyState), - event_flags: EventFlags::from(event.dwEventFlags), - } - } -} - -/// The status of the mouse buttons. -/// The least significant bit corresponds to the leftmost mouse button. -/// The next least significant bit corresponds to the rightmost mouse button. -/// The next bit indicates the next-to-leftmost mouse button. -/// The bits then correspond left to right to the mouse buttons. -/// A bit is 1 if the button was pressed. -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) -#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] -pub enum ButtonState { - Release = 0x0000, - /// The leftmost mouse button. - FromLeft1stButtonPressed = 0x0001, - /// The second button from the left. - FromLeft2ndButtonPressed = 0x0004, - /// The third button from the left. - FromLeft3rdButtonPressed = 0x0008, - /// The fourth button from the left. - FromLeft4thButtonPressed = 0x0010, - /// The rightmost mouse button. - RightmostButtonPressed = 0x0002, - /// This button state is not recognized. - Unknown = 0x0021, - /// The wheel was rotated backward, toward the user; this will only be activated for `MOUSE_WHEELED ` from `dwEventFlags` - Negative = 0x0020, -} - -impl From for ButtonState { - fn from(event: DWORD) -> Self { - let e = event as i32; - - match e { - 0x0000 => ButtonState::Release, - 0x0001 => ButtonState::FromLeft1stButtonPressed, - 0x0004 => ButtonState::FromLeft2ndButtonPressed, - 0x0008 => ButtonState::FromLeft3rdButtonPressed, - 0x0010 => ButtonState::FromLeft4thButtonPressed, - 0x0002 => ButtonState::RightmostButtonPressed, - _ if e < 0 => ButtonState::Negative, - _ => ButtonState::Unknown, - } - } -} - -#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] -pub struct ControlKeyState(u32); - -impl ControlKeyState { - pub fn has_state(&self, state: u32) -> bool { - (state & self.0) != 0 - } -} - -/// The type of mouse event. -/// If this value is zero, it indicates a mouse button being pressed or released. -/// Otherwise, this member is one of the following values. -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/mouse-event-record-str#members) -#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] -pub enum EventFlags { - PressOrRelease = 0x0000, - // The second click (button press) of a double-click occurred. The first click is returned as a regular button-press event. - DoubleClick = 0x0002, - // The horizontal mouse wheel was moved. - MouseHwheeled = 0x0008, - // If the high word of the dwButtonState member contains a positive value, the wheel was rotated to the right. Otherwise, the wheel was rotated to the left. - MouseMoved = 0x0001, - // A change in mouse position occurred. - // The vertical mouse wheel was moved, if the high word of the dwButtonState member contains a positive value, the wheel was rotated forward, away from the user. - // Otherwise, the wheel was rotated backward, toward the user. - MouseWheeled = 0x0004, -} - -impl From for EventFlags { - fn from(event: DWORD) -> Self { - match event { - 0x0000 => EventFlags::PressOrRelease, - 0x0002 => EventFlags::DoubleClick, - 0x0008 => EventFlags::MouseHwheeled, - 0x0001 => EventFlags::MouseMoved, - 0x0004 => EventFlags::MouseWheeled, - _ => panic!("Event flag {} does not exist.", event), - } - } -} - -/// Describes an input event in the console input buffer. -/// These records can be read from the input buffer by using the `ReadConsoleInput` or `PeekConsoleInput` function, or written to the input buffer by using the `WriteConsoleInput` function. -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/input-record-str) -#[derive(Clone)] -pub struct InputRecord { - /// A handle to the type of input event and the event record stored in the Event member. - pub event_type: InputEventType, - /// The event information. The format of this member depends on the event type specified by the EventType member. - /// Todo: wrap with rust type. - pub event: INPUT_RECORD_Event, -} - -impl From for InputRecord { - fn from(event: INPUT_RECORD) -> Self { - InputRecord { - event_type: InputEventType::from(event.EventType), - event: event.Event, - } - } -} - -/// A handle to the type of input event and the event record stored in the Event member. -/// -/// [Ms Docs](https://docs.microsoft.com/en-us/windows/console/input-record-str#members) -#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] -pub enum InputEventType { - /// The Event member contains a `KEY_EVENT_RECORD` structure with information about a keyboard event. - KeyEvent = KEY_EVENT as isize, - /// The Event member contains a `MOUSE_EVENT_RECORD` structure with information about a mouse movement or button press event. - MouseEvent = MOUSE_EVENT as isize, - /// The Event member contains a `WINDOW_BUFFER_SIZE_RECORD` structure with information about the new size of the console screen buffer. - WindowBufferSizeEvent = WINDOW_BUFFER_SIZE_EVENT as isize, - /// The Event member contains a `FOCUS_EVENT_RECORD` structure. These events are used internally and should be ignored. - FocusEvent = FOCUS_EVENT as isize, - /// The Event member contains a `MENU_EVENT_RECORD` structure. These events are used internally and should be ignored. - MenuEvent = MENU_EVENT as isize, -} - -impl From for InputEventType { - fn from(event: WORD) -> Self { - match event { - KEY_EVENT => InputEventType::KeyEvent, - MOUSE_EVENT => InputEventType::MouseEvent, - WINDOW_BUFFER_SIZE_EVENT => InputEventType::WindowBufferSizeEvent, - FOCUS_EVENT => InputEventType::FocusEvent, - MENU_EVENT => InputEventType::MenuEvent, - _ => panic!("Input event type {} does not exist.", event), - } - } -} diff --git a/crossterm_winapi/src/structs/size.rs b/crossterm_winapi/src/structs/size.rs deleted file mode 100644 index 59e38b7..0000000 --- a/crossterm_winapi/src/structs/size.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! This module provides a type that represents some size. -//! For example, in WinAPi we have `COORD` to represent screen/buffer size but this is a little inconvenient. -//! This module provides some trait implementations who will make parsing and working whit `COORD` easier. - -use winapi::um::wincon::COORD; - -/// This is type represents the size of something in width and height. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Size { - pub width: i16, - pub height: i16, -} - -impl Size { - /// Create a new size instance by passing in the width and height. - pub fn new(width: i16, height: i16) -> Size { - Size { width, height } - } -} - -impl From for Size { - fn from(coord: COORD) -> Self { - Size::new(coord.X, coord.Y) - } -} - -impl Into<(u16, u16)> for Size { - fn into(self) -> (u16, u16) { - (self.width as u16, self.height as u16) - } -} diff --git a/crossterm_winapi/src/structs/window_coords.rs b/crossterm_winapi/src/structs/window_coords.rs deleted file mode 100644 index 3dbd264..0000000 --- a/crossterm_winapi/src/structs/window_coords.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! This module provides a type that represents some rectangle. -//! For example, in WinAPi we have `SMALL_RECT` to represent a window size but this is a little inconvenient. -//! This module provides some trait implementations who will make parsing and working whit `COORD` easier. - -use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT}; - -/// This is a wrapper for the locations of a rectangle. -/// -/// It has left, right, bottom, top attributes. -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct WindowPositions { - pub left: i16, - pub right: i16, - pub bottom: i16, - pub top: i16, -} - -impl From for WindowPositions { - fn from(csbi: CONSOLE_SCREEN_BUFFER_INFO) -> Self { - WindowPositions { - left: csbi.srWindow.Left, - right: csbi.srWindow.Right, - bottom: csbi.srWindow.Bottom, - top: csbi.srWindow.Top, - } - } -} - -impl From for SMALL_RECT { - fn from(positions: WindowPositions) -> Self { - SMALL_RECT { - Top: positions.top, - Right: positions.right, - Bottom: positions.bottom, - Left: positions.left, - } - } -} diff --git a/docs/Contributing.md b/docs/CONTRIBUTING.md similarity index 80% rename from docs/Contributing.md rename to docs/CONTRIBUTING.md index 497ad62..8b5d123 100644 --- a/docs/Contributing.md +++ b/docs/CONTRIBUTING.md @@ -11,13 +11,13 @@ for Windows versions lower than 10. Crossterm uses WinAPI in this case. The `crossterm` crate consists of 7 crates: -* [cursor](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_cursor) -* [input](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_input) -* [style](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_style) -* [terminal](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_terminal) -* [screen](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_screen) -* [utils](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_utils) -* [winapi](https://github.com/crossterm-rs/crossterm/tree/master/crossterm_winapi) +* [cursor](https://github.com/crossterm-rs/crossterm-cursor) +* [input](https://github.com/crossterm-rs/crossterm-input) +* [style](https://github.com/crossterm-rs/crossterm-style) +* [terminal](https://github.com/crossterm-rs/crossterm-terminal) +* [screen](https://github.com/crossterm-rs/crossterm-screen) +* [utils](https://github.com/crossterm-rs/crossterm-utils) +* [winapi](https://github.com/crossterm-rs/crossterm-winapi) ### Module structure diff --git a/docs/know_problems.md b/docs/know-problems.md similarity index 85% rename from docs/know_problems.md rename to docs/know-problems.md index 7bc8eb0..826aaee 100644 --- a/docs/know_problems.md +++ b/docs/know-problems.md @@ -1,10 +1,14 @@ +# Known Problems + There are some problems I discovered during development. And I don't think it has to do anything with crossterm but it has to do whit how terminals handle ANSI or WinApi. -# Winapi-problems +## WinAPI + - Power shell does not interpreter 'DarkYellow' and is instead using gray instead, cmd is working perfectly fine. - Power shell inserts an '\n' (enter) when the program starts, this enter is the one you pressed when running the command. - After the program ran, power shell will reset the background and foreground colors. -# UNIX-terminals -The Arc and Manjaro KDE Konsole's are not seeming to resize the terminal instead they are resizing the buffer. \ No newline at end of file +## UNIX-terminals + +The Arc and Manjaro KDE Konsole's are not seeming to resize the terminal instead they are resizing the buffer. diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 9fd1aea..0000000 --- a/examples/README.md +++ /dev/null @@ -1,24 +0,0 @@ -This folder contains examples for crossterm and it's the sub-crates. - -**When using a sub-crate instead of the crossterm crate, make sure to change the namespaces in the examples from `crossterm` to `crossterm_{crate_name}`.** - -Examples, on the different functionalities -- [crossterm style](https://crates.io/crates/crossterm_style) - - [color](https://github.com/crossterm-rs/crossterm/blob/master/examples/cursor.rs): this is about the styling of the terminal -- [crossterm input](https://crates.io/crates/crossterm_input) - - [input](https://github.com/crossterm-rs/crossterm/blob/master/examples/input.rs): this is about input reading - - [key_events](https://github.com/crossterm-rs/crossterm/blob/master/examples/key_events.rs): this is about reading key events -- [crossterm screen](https://crates.io/crates/crossterm_screen) - - [alternate_screen](https://github.com/crossterm-rs/crossterm/blob/master/examples/alternate_screen.rs): this is about switching to an alternate screen buffer - - [raw_screen](https://github.com/crossterm-rs/crossterm/blob/master/examples/raw_screen.rs): this is about enabling raw screen -- [crossterm cursor](https://crates.io/crates/crossterm_cursor) - - [cursor](https://github.com/crossterm-rs/crossterm/blob/master/examples/cursor.rs): this is about the actions you can perform with the cursor -- [crossterm terminal](https://crates.io/crates/crossterm_terminal) - - [terminal](https://github.com/crossterm-rs/crossterm/blob/master/examples/terminal.rs): this is about the actions you can perform on the terminal - -Other -- [crossterm](https://github.com/crossterm-rs/crossterm/blob/master/examples/crossterm.rs): this is about the struct `Crossterm` -- [command](https://github.com/crossterm-rs/crossterm/blob/master/examples/command.rs): this is about to the command api -- [program examples](https://github.com/crossterm-rs/crossterm/tree/master/examples/program_examples): this folder will contain some real life examples -- [command_bar](https://github.com/crossterm-rs/crossterm/tree/master/examples/command_bar): this is a terminal application where multiple threads write to the output while you can enter - commands asynchronously. diff --git a/examples/alternate_screen.rs b/examples/alternate_screen.rs deleted file mode 100644 index f609e9f..0000000 --- a/examples/alternate_screen.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::{thread, time}; - -use crossterm::{style, AlternateScreen, ClearType, Color, Crossterm, Result}; - -fn print_wait_screen() -> Result<()> { - let crossterm = Crossterm::new(); - let terminal = crossterm.terminal(); - let cursor = crossterm.cursor(); - - terminal.clear(ClearType::All)?; - cursor.goto(0, 0)?; - cursor.hide()?; - terminal.write( - "Welcome to the wait screen.\n\ - Please wait a few seconds until we arrive back at the main screen.\n\ - Progress: ", - )?; - // print some progress example. - for i in 1..5 { - // print the current counter at the line of `Seconds to Go: {counter}` - cursor.goto(10, 2)?; - println!( - "{}", - style(format!("{} of the 5 items processed", i)) - .with(Color::Red) - .on(Color::Blue) - ); - - // 1 second delay - thread::sleep(time::Duration::from_secs(1)); - } - - Ok(()) -} - -/// print wait screen on alternate screen, then switch back. -fn print_wait_screen_on_alternate_window() -> Result<()> { - let _alt = AlternateScreen::to_alternate(false)?; - print_wait_screen() -} - -// cargo run --example alternate_screen -fn main() -> Result<()> { - print_wait_screen_on_alternate_window() -} diff --git a/examples/command.rs b/examples/command.rs deleted file mode 100644 index 719066d..0000000 --- a/examples/command.rs +++ /dev/null @@ -1,87 +0,0 @@ -#![allow(dead_code)] - -use std::io::{stdout, Write}; - -use crossterm::{ - execute, queue, Clear, ClearType, ExecutableCommand, Goto, Output, QueueableCommand, Result, -}; - -/// execute commands by using normal functions -fn execute_command_directly_using_functions() -> Result<()> { - // single command - stdout().execute(Output("Text1 ".to_string()))?; - - // multiple commands - stdout() - .execute(Output("Text2 ".to_string()))? - .execute(Output("Text3 ".to_string()))?; - - Ok(()) -} - -/// execute commands by using macro's -fn execute_command_directly_using_macros() -> Result<()> { - // single command - execute!(stdout(), Output("Text1 ".to_string()))?; - - // multiple commands - execute!( - stdout(), - Output("Text2 ".to_string()), - Output("Text 3".to_string()) - ) -} - -/// queue commands without executing them directly by using normal functions -fn later_execution_command_using_functions() -> Result<()> { - let mut sdout = stdout(); - - // single command - sdout.queue(Output("Text1 ".to_string()))?; - - // multiple commands - sdout - .queue(Clear(ClearType::All))? - .queue(Goto(5, 5))? - .queue(Output( - "console cleared, and moved to coord X: 5 Y: 5 ".to_string(), - ))?; - - ::std::thread::sleep(std::time::Duration::from_millis(2000)); - - // when you call this all commands will be executed - sdout.flush()?; - - Ok(()) -} - -/// queue commands without executing them directly by using macro's -fn later_execution_command_directly_using_macros() -> Result<()> { - let mut stdout = stdout(); - - // single command - queue!(stdout, Output("Text1 ".to_string()))?; - - // multiple commands - queue!( - stdout, - Clear(ClearType::All), - Goto(5, 5), - Output("console cleared, and moved to coord X: 5 Y: 5 ".to_string()) - )?; - - ::std::thread::sleep(std::time::Duration::from_millis(2000)); - - // when you call this all commands will be executed - stdout.flush()?; - - Ok(()) -} - -// cargo run --example command -fn main() -> Result<()> { - later_execution_command_directly_using_macros() - // later_execution_command_using_functions() - // execute_command_directly_using_macros() - // execute_command_directly_using_functions() -} diff --git a/examples/command_bar.rs b/examples/command_bar.rs deleted file mode 100644 index 34ba4a9..0000000 --- a/examples/command_bar.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::sync::{Arc, Mutex}; -use std::{thread, time}; - -use crossterm::{ - cursor, input, terminal, ClearType, Crossterm, InputEvent, KeyEvent, RawScreen, Result, - Terminal, TerminalCursor, -}; - -fn log(input_buf: Arc>) -> Result>> { - let mut threads = Vec::with_capacity(10); - - let (_, term_height) = terminal().size()?; - - for i in 0..1 { - let input_buffer = input_buf.clone(); - - let crossterm = Crossterm::new(); - - let join = thread::spawn(move || { - let cursor = crossterm.cursor(); - let terminal = crossterm.terminal(); - - for j in 0..1000 { - if let Err(_) = swap_write( - format!("Some output: {} from thread: {}", j, i).as_ref(), - &input_buffer.lock().unwrap(), - &terminal, - &cursor, - term_height, - ) { - return; - } - thread::sleep(time::Duration::from_millis(100)); - } - }); - - threads.push(join); - } - - Ok(threads) -} - -fn swap_write( - msg: &str, - input_buf: &String, - terminal: &Terminal, - cursor: &TerminalCursor, - term_height: u16, -) -> Result<()> { - cursor.goto(0, term_height)?; - terminal.clear(ClearType::CurrentLine)?; - terminal.write(format!("{}\r\n", msg))?; - terminal.write(format!("> {}", input_buf)).map(|_| ()) -} - -// cargo run --example command_bar -fn main() -> Result<()> { - let _screen = RawScreen::into_raw_mode(); - cursor().hide()?; - - let input_buf = Arc::new(Mutex::new(String::new())); - - let threads = log(input_buf.clone())?; - - let mut count = 0; - - thread::spawn(move || { - let input = input(); - let mut stdin = input.read_async(); - - loop { - match stdin.next() { - Some(InputEvent::Keyboard(KeyEvent::Char('\n'))) => { - input_buf.lock().unwrap().clear(); - } - Some(InputEvent::Keyboard(KeyEvent::Char(character))) => { - input_buf.lock().unwrap().push(character as char); - } - _ => {} - } - - thread::sleep(time::Duration::from_millis(10)); - count += 1; - } - }) - .join() - .expect("Couldn't join"); - - for thread in threads { - thread.join().expect("Couldn't join"); - } - - cursor().show() -} diff --git a/examples/crossterm.rs b/examples/crossterm.rs deleted file mode 100644 index dcf5ee9..0000000 --- a/examples/crossterm.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Remove once the TODO below is fixed -#![allow(unused_variables)] - -use crossterm::{Color, Crossterm}; - -// use the `Crossterm` to get an instance to the cursor module | demonstration. -// cargo run --example crossterm -fn main() { - // Create the crossterm type to access different modules. - let crossterm = Crossterm::new(); - - // pass a reference to the current screen. - let cursor = crossterm.cursor(); - let color = crossterm.color(); - let terminal = crossterm.terminal(); - let terminal = crossterm.input(); - let style = crossterm - .style("Black text on green background") - .with(Color::Black) - .on(Color::Green); - - // TODO: perform some actions with the instances above. -} diff --git a/examples/cursor.rs b/examples/cursor.rs deleted file mode 100644 index be9123c..0000000 --- a/examples/cursor.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! -//! Examples of actions that could be performed with the cursor. -//! - -#![allow(dead_code)] - -use crossterm::{cursor, Result}; - -/// Set the cursor to position X: 10, Y: 5 in the terminal. -fn goto() -> Result<()> { - // Get the cursor - let cursor = cursor(); - // Set the cursor to position X: 10, Y: 5 in the terminal - cursor.goto(10, 5)?; - - Ok(()) -} - -/// get the cursor position -fn pos() -> Result<()> { - // Get the cursor - let cursor = cursor(); - // get the cursor position. - let (x, y) = cursor.pos()?; - - println!("{} {}", x, y); - Ok(()) -} - -/// Move the cursor 3 up | demonstration. -fn move_up() -> Result<()> { - // Get the cursor - let mut cursor = cursor(); - - // Move the cursor to position 3 times to the up in the terminal - cursor.move_up(3)?; - Ok(()) -} - -/// Move the cursor 3 down | demonstration. -fn move_down() -> Result<()> { - let mut cursor = cursor(); - // Move the cursor to position 3 times to the down in the terminal - cursor.move_down(3)?; - Ok(()) -} - -/// Move the cursor 3 to the right | demonstration. -fn move_right() -> Result<()> { - let mut cursor = cursor(); - // Move the cursor to position 3 times to the right in the terminal - cursor.move_right(3)?; - Ok(()) -} - -/// Move the cursor 3 left | demonstration. -fn move_left() -> Result<()> { - let mut cursor = cursor(); - // Move the cursor to position 3 times to the left in the terminal - cursor.move_left(3)?; - Ok(()) -} - -/// Save and reset cursor position | demonstration.. -fn save_and_restore_position() -> Result<()> { - let cursor = cursor(); - - // 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. - println!("Yea!"); - // Reset back to X: 5 Y: 5. - cursor.restore_position()?; - // Print Back at X: 5 Y: 5. - println!("Back"); - - println!(); - - Ok(()) -} - -/// Hide cursor display | demonstration. -fn hide_cursor() -> Result<()> { - let cursor = cursor(); - cursor.hide() -} - -/// Show cursor display | demonstration. -fn show_cursor() -> Result<()> { - let cursor = cursor(); - cursor.show() -} - -/// Show cursor display, only works on certain terminals.| demonstration -fn blink_cursor(enable: bool) -> Result<()> { - let cursor = cursor(); - cursor.blink(enable) -} - -// cargo run --example cursor -fn main() -> Result<()> { - println!("Going to show cursor..."); - show_cursor()?; - println!("Going to enable blinking cursor & sleep for 5s..."); - blink_cursor(true)?; - std::thread::sleep(std::time::Duration::from_secs(5)); - println!("Going to disable blinking cursor..."); - blink_cursor(false) -} diff --git a/examples/input.rs b/examples/input.rs deleted file mode 100644 index 8de8e20..0000000 --- a/examples/input.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crossterm::input; - -fn read_char() { - let input = input(); - - match input.read_char() { - Ok(s) => println!("char typed: {}", s), - Err(e) => println!("char error : {}", e), - } -} - -fn read_line() { - let input = input(); - - match input.read_line() { - Ok(s) => println!("string typed: {}", s), - Err(e) => println!("error: {}", e), - } -} - -fn main() { - read_line(); - read_char(); -} diff --git a/examples/key_events.rs b/examples/key_events.rs deleted file mode 100644 index 1682494..0000000 --- a/examples/key_events.rs +++ /dev/null @@ -1,132 +0,0 @@ -#![allow(dead_code)] - -use std::{thread, time::Duration}; - -use crossterm::{input, InputEvent, KeyEvent, MouseButton, MouseEvent, RawScreen, Result}; - -fn process_input_event(key_event: InputEvent) -> bool { - match key_event { - InputEvent::Keyboard(k) => { - match k { - KeyEvent::Char(c) => match c { - 'q' => { - println!("The 'q' key is hit and the program is not listening to input anymore.\n\n"); - return true; - } - _ => { - println!("{}", format!("'{}' pressed\n\n", c)); - } - }, - KeyEvent::Alt(c) => { - println!("{}", format!("ALT +'{}' pressed\n\n", c)); - } - KeyEvent::Ctrl(c) => { - println!("{}", format!("CTRL +'{}' Pressed\n\n", c)); - } - KeyEvent::Esc => { - println!("{}", format!("ESC pressed\n\n")); - } - KeyEvent::F(number) => { - println!("{}", format!("F{} key pressed\n\n", number)); - } - KeyEvent::PageUp => { - println!("{}", format!("Page Up\n\n")); - } - KeyEvent::PageDown => { - println!("{}", format!("Page Down\n\n")); - } - KeyEvent::Delete => { - println!("{}", format!("Delete\n\n")); - } - _ => { - println!("{}", format!("OTHER: {:?}\n\n", k)); - () - } - } - } - InputEvent::Mouse(m) => match m { - MouseEvent::Press(b, x, y) => match b { - MouseButton::Left => { - println!("{}", format!("left mouse press @ {}, {}\n\n", x, y)); - } - MouseButton::Right => { - println!("{}", format!("right mouse press @ {}, {}\n\n", x, y)); - } - MouseButton::Middle => { - println!("{}", format!("mid mouse press @ {}, {}\n\n", x, y)); - } - MouseButton::WheelUp => println!("{}", format!("wheel up @ {}, {}\n\n", x, y)), - MouseButton::WheelDown => { - println!("{}", format!("wheel down @ {}, {}\n\n", x, y)); - } - }, - MouseEvent::Release(x, y) => { - println!("{}", format!("mouse released @ {}, {}\n\n", x, y)); - } - MouseEvent::Hold(x, y) => { - println!("{}", format!("dragging @ {}, {}\n\n", x, y)); - } - _ => { - println!("{}", "Unknown mouse event"); - } - }, - _ => println!("Unknown!"), - } - - false -} - -fn read_asynchronously() -> Result<()> { - // make sure to enable raw mode, this will make sure key events won't be handled by the terminal it's self and allows crossterm to read the input and pass it back to you. - let _raw = RawScreen::into_raw_mode()?; - - let input = input(); - - // enable mouse events to be captured. - input.enable_mouse_mode()?; - - let mut stdin = input.read_async(); - - loop { - if let Some(key_event) = stdin.next() { - if process_input_event(key_event) { - break; - } - } - thread::sleep(Duration::from_millis(50)); - } - - // disable mouse events to be captured. - input.disable_mouse_mode() -} // <=== raw modes will be disabled here - -fn read_synchronously() -> Result<()> { - // make sure to enable raw mode, this will make sure key events won't be handled by the terminal it's self and allows crossterm to read the input and pass it back to you. - let _raw = RawScreen::into_raw_mode()?; - - let input = input(); - - // enable mouse events to be captured. - input.enable_mouse_mode()?; - - let mut sync_stdin = input.read_sync(); - - loop { - let event = sync_stdin.next(); - - if let Some(key_event) = event { - if process_input_event(key_event) { - break; - } - } - } - - // disable mouse events to be captured. - input.disable_mouse_mode() -} // <=== raw modes will be disabled here - -// cargo run --example key_events -fn main() -> Result<()> { - read_synchronously() - // read_asynchronously() -} diff --git a/examples/program_examples/README.md b/examples/program_examples/README.md deleted file mode 100644 index 710f199..0000000 --- a/examples/program_examples/README.md +++ /dev/null @@ -1,15 +0,0 @@ -This folder will contain some examples of how to use this crate in a real live environment. - -If you have created a game or something feel free to upload it, would be a great help for other people and me to make this crate better! - -The programs are: - -- First depth search: - - This is a search algorithm implemented visually. This program uses the following functionalities: cursor movement, coloring, alternate screen and terminal clearing. - To run this use `cargo run` in the directory of first depth search. - -- Snake - - This is a snake game implemented with this library. - To run this use `cargo run` in the directory of snake. diff --git a/examples/program_examples/first_depth_search/Cargo.toml b/examples/program_examples/first_depth_search/Cargo.toml deleted file mode 100644 index c8ea4db..0000000 --- a/examples/program_examples/first_depth_search/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "first_depth_search" -version = "0.1.0" -authors = ["TimonPost "] -edition = "2018" - -[dependencies] -rand = "0.4.2" - -[dependencies.crossterm] -path = "../../../" diff --git a/examples/program_examples/first_depth_search/src/algorithm.rs b/examples/program_examples/first_depth_search/src/algorithm.rs deleted file mode 100644 index d0f91ac..0000000 --- a/examples/program_examples/first_depth_search/src/algorithm.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! Implementation of the first depth search algorithm - -use std::io::Write; -use std::{thread, time}; - -use crossterm::{execute, Color, Colorize, Goto, Hide, PrintStyledFont, Result, SetBg, SetFg}; -use rand; -use rand::distributions::{IndependentSample, Range}; - -use super::map::Map; -use super::variables::{Direction, Position}; - -pub struct FirstDepthSearch { - direction: Direction, - map: Map, - stack: Vec, - root_pos: Position, - is_terminated: bool, -} - -impl FirstDepthSearch { - pub fn new(map: Map, start_pos: Position) -> FirstDepthSearch { - FirstDepthSearch { - direction: Direction::Up, - map, - stack: Vec::new(), - root_pos: start_pos, - is_terminated: false, - } - } - - pub fn start(&mut self) -> Result<()> { - self.is_terminated = false; - - // push first position on the stack - self.stack.push(self.root_pos); - - execute!( - ::std::io::stdout(), - Hide, - SetFg(Color::Green), - SetBg(Color::Black) - )?; - - // loop until there are now items left in the stack. - loop { - if self.stack.len() == 0 { - break; - } - - self.choose_random_neighbor(); - - if self.is_terminated { - break; - } - - self.update_position(); - - let pos = self.root_pos.clone(); - - let x = pos.x as u16; - let y = pos.y as u16; - - execute!( - ::std::io::stdout(), - Goto(x, y), - PrintStyledFont(" ".on_yellow()) - )?; - - thread::sleep(time::Duration::from_millis(1)); - } - - Ok(()) - } - - /// With this function we are choosing an random neighbor that we havent visited yet. - fn choose_random_neighbor(&mut self) { - let mut available_directions: Vec = Vec::with_capacity(4); - - // check every direction if the direction is not visited we can add it to the list. - // note that if the y or x is 0 that we don't want to subtract because we get an subtract overflow. - if self.root_pos.y != 0 - && !self - .map - .is_cell_visited(self.root_pos.x, self.root_pos.y - 1) - { - available_directions.push(Direction::Up) - } - - if !&self - .map - .is_cell_visited(self.root_pos.x, self.root_pos.y + 1) - { - available_directions.push(Direction::Down) - } - - if self.root_pos.x != 0 - && !self - .map - .is_cell_visited(self.root_pos.x - 1, self.root_pos.y) - { - available_directions.push(Direction::Left) - } - - if !&self - .map - .is_cell_visited(self.root_pos.x + 1, self.root_pos.y) - { - available_directions.push(Direction::Right) - } - - let directions_count = available_directions.len(); - - // if there are no directions left we need to backtrack until we find directions to go to. - if directions_count != 0 { - let step = Range::new(0, directions_count); - let mut rng = rand::thread_rng(); - let choice = step.ind_sample(&mut rng); - - // set the current direction to the new random generated direction. - self.direction = available_directions[choice]; - } else { - self.find_first_possible_direction(); - } - } - - /// Find direction to go to if there is no direction pop the current position of the stack for back tracking to the previous position. - fn find_first_possible_direction(&mut self) { - // if there are no elements left in the stack that means we have visited all cell and we van terminate the program. - if let &Some(previous_cell) = &self.stack.pop() { - // update root pos to previous cell and continue searching for new neighbours - self.root_pos = previous_cell; - self.choose_random_neighbor(); - } else { - self.is_terminated = true; - } - } - - /// update the root position to the new direction we went in - fn update_position(&mut self) { - match self.direction { - Direction::Up => self.root_pos.y -= 1, - Direction::Down => self.root_pos.y += 1, - Direction::Left => self.root_pos.x -= 1, - Direction::Right => self.root_pos.x += 1, - }; - - self.map.set_visited(self.root_pos.x, self.root_pos.y); - self.stack.push(self.root_pos); - } -} diff --git a/examples/program_examples/first_depth_search/src/main.rs b/examples/program_examples/first_depth_search/src/main.rs deleted file mode 100644 index 673cfa0..0000000 --- a/examples/program_examples/first_depth_search/src/main.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::io::{stdout, Write}; -use std::iter::Iterator; -use std::{thread, time}; - -use crossterm::{ - color, cursor, execute, input, style, terminal, AlternateScreen, Clear, ClearType, Color, Goto, - Hide, InputEvent, KeyEvent, Output, PrintStyledFont, RawScreen, Result, SetBg, SetFg, SetSize, -}; - -use self::variables::{Position, Size}; - -mod algorithm; -mod map; -mod messages; -mod variables; - -fn main() -> Result<()> { - run() -} - -/// run the program -fn run() -> Result<()> { - // let screen = RawScreen::into_raw_mode()?; - print_welcome_screen()?; - start_algorithm()?; - exit() -} - -fn start_algorithm() -> Result<()> { - // we first want to switch to alternate screen. On the alternate screen we are going to run or firstdepthsearch algorithm - let ref _alternate_screen = AlternateScreen::to_alternate(true)?; - // setup the map size and the position to start searching for a path. - let map_size = Size::new(50, 40); - let start_pos = Position::new(10, 10); - - // create and render the map. Or map border is going to have an █ look and inside the map is just a space. - let mut map = map::Map::new(map_size, '█', ' '); - map.render_map()?; - - // create the algorithm and start it on the alternate screen. Make sure to pass the refrence to the AlternateScreen screen. - let mut algorithm = algorithm::FirstDepthSearch::new(map, start_pos); - algorithm.start() -} - -fn print_welcome_screen() -> Result<()> { - // we have to keep this screen arround to prevent te - let _screen = RawScreen::into_raw_mode()?; - - execute!( - stdout(), - SetSize(110, 60), - Clear(ClearType::All), - Goto(0, 0), - PrintStyledFont( - style(format!("{}", messages::WELCOME_MESSAGE.join("\n\r"))).with(Color::Cyan) - ), - Hide, - Goto(0, 10), - Output("The first depth search algorithm will start in: Seconds".to_string()), - Goto(0, 11), - Output("Press `q` to abort the program".to_string()) - )?; - - let mut stdin = input().read_async(); - - // print some progress example. - for i in (1..5).rev() { - if let Some(InputEvent::Keyboard(KeyEvent::Char('q'))) = stdin.next() { - exit()?; - terminal().exit(); - break; - } else { - // print the current counter at the line of `Seconds to Go: {counter}` - execute!( - stdout(), - Goto(48, 10), - SetFg(Color::Red), - SetBg(Color::Blue), - Output(i.to_string()) - )?; - } - - color().reset()?; - - // 1 second delay - thread::sleep(time::Duration::from_secs(1)); - } - - Ok(()) -} - -fn exit() -> Result<()> { - RawScreen::disable_raw_mode()?; - cursor().show()?; - color().reset() -} diff --git a/examples/program_examples/first_depth_search/src/map.rs b/examples/program_examples/first_depth_search/src/map.rs deleted file mode 100644 index 4201cd7..0000000 --- a/examples/program_examples/first_depth_search/src/map.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::io::{stdout, Write}; - -use crossterm::{queue, Color, Crossterm, Goto, PrintStyledFont, Result}; - -use super::variables::{Cell, Position, Size}; - -pub struct Map { - pub map: Vec>, - pub size: Size, -} - -impl Map { - pub fn new(map_size: Size, wall_cell_char: char, map_cell_char: char) -> Map { - let mut map: Vec> = Vec::new(); - - // initialize the map shown on the screen. Each cell of terminal should have a value that could be changed by the algorithm - // create n rows with n cells. - for y in 0..map_size.height { - let mut row: Vec = Vec::new(); - - for x in 0..map_size.width { - if (y == 0 || y == map_size.height - 1) || (x == 0 || x == map_size.width - 1) { - row.push(Cell::new( - Position::new(x, y), - Color::Black, - wall_cell_char, - true, - )); - } else { - row.push(Cell::new( - Position::new(x, y), - Color::Black, - map_cell_char, - false, - )); - } - } - map.push(row); - } - - Map { - map, - size: Size::new(map_size.width, map_size.height), - } - } - - // render the map on the screen. - pub fn render_map(&mut self) -> Result<()> { - let crossterm = Crossterm::new(); - - for row in self.map.iter_mut() { - for column in row.iter_mut() { - // we only have to render the walls - if (column.position.y == 0 || column.position.y == self.size.height - 1) - || (column.position.x == 0 || column.position.x == self.size.width - 1) - { - queue!( - stdout(), - Goto(column.position.x as u16, column.position.y as u16), - PrintStyledFont(crossterm.style(column.look).on(column.color)) - )?; - } - } - } - - Ok(()) - } - - // check if position in the map at the given coords is visted. - pub fn is_cell_visited(&self, x: usize, y: usize) -> bool { - self.map[y][x].visited - } - - // change a position in the map to visited. - pub fn set_visited(&mut self, x: usize, y: usize) { - self.map[y][x].visited = true; - } -} diff --git a/examples/program_examples/first_depth_search/src/messages.rs b/examples/program_examples/first_depth_search/src/messages.rs deleted file mode 100644 index 5919229..0000000 --- a/examples/program_examples/first_depth_search/src/messages.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub const WELCOME_MESSAGE: [&str; 6] = [ - "__ __ .__ __ ", - "/ \\ / \\ ____ | | | | ______ _____ ____ ", - "\\ \\/\\/ // __ \\| | | |/ / _ \\ / \\_/ __ \\ ", - " \\ /\\ ___/| |_| < <_> ) Y Y \\ ___/ ", - " \\__/\\ / \\___ >____/__|_ \\____/|__|_| /\\___ > ", - " \\/ \\/ \\/ \\/ \\/ ", -]; diff --git a/examples/program_examples/first_depth_search/src/variables.rs b/examples/program_examples/first_depth_search/src/variables.rs deleted file mode 100644 index d8a48d9..0000000 --- a/examples/program_examples/first_depth_search/src/variables.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crossterm::Color; - -#[derive(Copy, Clone, Debug)] -pub enum Direction { - Up = 0, - Down = 1, - Left = 2, - Right = 3, -} - -#[derive(Copy, Clone, Debug)] -pub struct Position { - pub x: usize, - pub y: usize, -} - -impl Position { - pub fn new(x: usize, y: usize) -> Position { - Position { x, y } - } -} - -#[derive(Copy, Clone)] -pub struct Size { - pub width: usize, - pub height: usize, -} - -impl Size { - pub fn new(width: usize, height: usize) -> Size { - Size { width, height } - } -} - -pub struct Cell { - pub position: Position, - pub color: Color, - pub look: char, - pub visited: bool, -} - -impl Cell { - pub fn new(position: Position, color: Color, look: char, visited: bool) -> Cell { - Cell { - position, - color, - look, - visited, - } - } -} diff --git a/examples/program_examples/snake/Cargo.toml b/examples/program_examples/snake/Cargo.toml deleted file mode 100644 index 33f6a55..0000000 --- a/examples/program_examples/snake/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "snake" -version = "0.1.0" -authors = ["TimonPost "] -edition = "2018" - -[dependencies] -rand = "0.4.2" - -[dependencies.crossterm] -path = "../../../" diff --git a/examples/program_examples/snake/src/main.rs b/examples/program_examples/snake/src/main.rs deleted file mode 100644 index 3b7fa1a..0000000 --- a/examples/program_examples/snake/src/main.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! The snake game. -//! -//! This is not a properly designed game! Mainly game loop, input events -//! handling, UI separation, ... The main purpose of this example is to -//! test the `crossterm` crate and demonstrate some of the capabilities. -use std::convert::TryFrom; -use std::io::{stdout, Write}; -use std::iter::Iterator; -use std::{thread, time}; - -use crossterm::{ - execute, input, style, AsyncReader, Clear, ClearType, Color, Crossterm, Goto, InputEvent, - KeyEvent, PrintStyledFont, RawScreen, Result, Show, -}; - -use map::Map; -use snake::Snake; -use types::Direction; - -mod map; -mod messages; -mod snake; -mod types; - -/// An input (user) event. -#[derive(Debug)] -pub enum Event { - /// User wants to change the snake direction. - UpdateSnakeDirection(Direction), - /// User wants to quite the game. - QuitGame, -} - -fn main() -> Result<()> { - // Print the welcome screen and ask for the map size. - let crossterm = Crossterm::new(); - let (map_width, map_height) = ask_for_map_size(crossterm.terminal().size()?)?; - - // Switch screen to the raw mode to avoid printing key presses on the screen - // and hide the cursor. - let _raw = RawScreen::into_raw_mode(); - crossterm.cursor().hide()?; - - // Draw the map border. - let mut map = Map::new(map_width, map_height); - map.draw_border()?; - - // Create a new snake, draw it and spawn some food. - let mut snake = Snake::new(map_width, map_height); - snake.draw()?; - map.spawn_food(&snake)?; - - // Game loop - let mut stdin = crossterm.input().read_async(); - loop { - // Handle the next user input event (if there's any). - match next_event(&mut stdin, snake.direction()) { - Some(Event::UpdateSnakeDirection(direction)) => snake.set_direction(direction), - Some(Event::QuitGame) => break, - _ => {} - }; - - // Update the snake (move & redraw). If it returns `false` -> new head - // collides with the snake body -> can't eat self -> quit the game loop. - if !snake.update()? { - break; - } - - // Check if the snake ate some food. - if snake.head_position() == map.food_position() { - // Tell the snake to grow ... - snake.set_ate_food(true); - // ... and spawn new food. - map.spawn_food(&snake)?; - } - - // Check if the snake head position is out of bounds. - if map.is_position_out_of_bounds(snake.head_position()) { - break; - } - - // Wait for some time. - thread::sleep(time::Duration::from_millis(200)); - } - - show_game_over_screen(snake.len()) -} - -/// Returns a next user event (if there's any). -fn next_event(reader: &mut AsyncReader, snake_direction: Direction) -> Option { - // The purpose of this loop is to consume events that are not actionable. Let's - // say that the snake is moving to the right and the user hits the right arrow - // key three times and then the up arrow key. The up arrow key would be handled - // in the 4th iteration of the game loop. That's not what we really want and thus - // we are consuming all events here till we find an actionable one or none. - while let Some(event) = reader.next() { - match event { - InputEvent::Keyboard(KeyEvent::Char(character)) => { - if let Ok(new_direction) = Direction::try_from(character) { - if snake_direction.can_change_to(new_direction) { - return Some(Event::UpdateSnakeDirection(new_direction)); - } - } - } - InputEvent::Keyboard(KeyEvent::Esc) => return Some(Event::QuitGame), - InputEvent::Keyboard(key) => { - if let Ok(new_direction) = Direction::try_from(key) { - if snake_direction.can_change_to(new_direction) { - return Some(Event::UpdateSnakeDirection(new_direction)); - } - } - } - _ => {} - }; - } - None -} - -/// Asks the user for a single map dimension. If the input can't be parsed or is outside -/// of the `min..=default_max` range, `min` or `default_max` is returned. -fn ask_for_map_dimension(name: &str, min: u16, default_max: u16, pos: (u16, u16)) -> Result { - let message = format!( - "Enter map {} (min: {}, default/max: {}):", - name, min, default_max - ); - let message_len = message.chars().count() as u16; - - execute!( - stdout(), - Goto(pos.0, pos.1), - PrintStyledFont(style(message).with(Color::Green)), - Goto(pos.0 + message_len + 1, pos.1) - )?; - - let dimension = input() - .read_line()? - .parse::() - .map(|x| { - if x > default_max { - default_max - } else if x < min { - min - } else { - x - } - }) - .unwrap_or(default_max); - - Ok(dimension) -} - -/// Prints the welcome screen and asks the user for the map size. -fn ask_for_map_size(terminal_size: (u16, u16)) -> Result<(u16, u16)> { - let mut row = 0u16; - - execute!( - stdout(), - Clear(ClearType::All), - Goto(0, row), - PrintStyledFont(style(format!("{}", messages::SNAKE.join("\n\r"))).with(Color::Cyan)) - )?; - - row += messages::SNAKE.len() as u16 + 2; - let width = ask_for_map_dimension("width", 10, terminal_size.0, (0, row))?; - row += 2; - let height = ask_for_map_dimension("height", 10, terminal_size.1, (0, row))?; - - execute!(stdout(), Clear(ClearType::All))?; - - Ok((width, height)) -} - -/// Prints the game over screen. -fn show_game_over_screen(score: usize) -> Result<()> { - execute!( - stdout(), - Clear(ClearType::All), - Goto(0, 0), - PrintStyledFont(style(format!("{}", messages::GAME_OVER.join("\n\r"))).with(Color::Red)), - Goto(0, messages::GAME_OVER.len() as u16 + 2), - PrintStyledFont( - style(format!("Your score is {}. You can do better!", score)).with(Color::Red) - ), - Show, - Goto(0, messages::GAME_OVER.len() as u16 + 4) - ) -} diff --git a/examples/program_examples/snake/src/map.rs b/examples/program_examples/snake/src/map.rs deleted file mode 100644 index e552ae3..0000000 --- a/examples/program_examples/snake/src/map.rs +++ /dev/null @@ -1,124 +0,0 @@ -use std::io::{stdout, Write}; - -use crossterm::{queue, Colorize, Goto, PrintStyledFont, Result}; -use rand::{ - self, - distributions::{IndependentSample, Range}, -}; - -use super::snake::Snake; -use super::types::Position; - -/// A food. -struct Food { - /// The food position. - position: Position, -} - -impl Food { - /// Creates a new food with the given `position`. - fn new(position: Position) -> Self { - Food { position } - } - - /// Draws the food. - fn draw(&self) -> Result<()> { - queue!( - stdout(), - Goto(self.position.x, self.position.y), - PrintStyledFont("❖".green()) - ) - } -} - -/// A world map. -pub struct Map { - /// The map width. - width: u16, - /// The map height. - height: u16, - /// Food. - food: Option, -} - -impl Map { - /// Crates a new map with the given `width` & `height`. - pub fn new(width: u16, height: u16) -> Self { - Map { - width, - height, - food: None, - } - } - - /// Draws the map border. - pub fn draw_border(&self) -> Result<()> { - for y in 0..self.height { - queue!( - stdout(), - Goto(0, y), - PrintStyledFont("█".magenta()), - Goto(self.width - 1, y), - PrintStyledFont("█".magenta()) - )?; - } - for x in 0..self.width { - queue!( - stdout(), - Goto(x, 0), - PrintStyledFont("█".magenta()), - Goto(x, self.height - 1), - PrintStyledFont("█".magenta()) - )?; - } - Ok(()) - } - - /// Check if the given `position` is out of bounds. - /// - /// Every map has a border and out of bounds means that the position - /// is inside the border. - pub fn is_position_out_of_bounds(&self, position: Position) -> bool { - position.x == 0 - || position.y == 0 - || position.x >= self.width - 1 - || position.y >= self.height - 1 - } - - /// Returns food position. - /// - /// # Panics - /// - /// It's forbidden to call this function before calling the `spawn_food()` function. - /// Considered as a programmer error and will panic. - pub fn food_position(&self) -> Position { - self.food.as_ref().unwrap().position - } - - /// Spawns a new food and draws it. - /// - /// The `snake` argument is used to check that the food position doesn't collide - /// with any snake fragment. - pub fn spawn_food(&mut self, snake: &Snake) -> Result<()> { - let free_area_width = self.width - 2; - let free_area_height = self.height - 2; - let free_area_position_count = free_area_width * free_area_height; - - // Naive implementation, but enough for an example - let position = loop { - let index = Range::new(0, free_area_position_count).ind_sample(&mut rand::thread_rng()); - let x = index % free_area_width + 1; - let y = index / free_area_width + 1; - let position = (x, y).into(); - - if !snake.fragment_exists_at_position(position) { - break position; - } - }; - - let food = Food::new(position); - food.draw()?; - self.food = Some(food); - return Ok(()); - } -} diff --git a/examples/program_examples/snake/src/messages.rs b/examples/program_examples/snake/src/messages.rs deleted file mode 100644 index 9471eb4..0000000 --- a/examples/program_examples/snake/src/messages.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Game ASCII art messages. -//! -//! These messages were generated on the http://patorjk.com/software/taag/ site. - -pub const SNAKE: [&str; 9] = [ - " ███████╗███╗ ██╗ █████╗ ██╗ ██╗███████╗ ", - " ██╔════╝████╗ ██║██╔══██╗██║ ██╔╝██╔════╝ ", - " ███████╗██╔██╗ ██║███████║█████╔╝ █████╗ ", - " ╚════██║██║╚██╗██║██╔══██║██╔═██╗ ██╔══╝ ", - " ███████║██║ ╚████║██║ ██║██║ ██╗███████╗ ", - " ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ ", - " ", - " Snake controls: WASD or arrow keys. ", - " Hit Esc to quit the game when playing. ", -]; - -pub const GAME_OVER: [&str; 10] = [ - " ▄████ ▄▄▄ ███▄ ▄███▓▓█████ ▒█████ ██▒ █▓▓█████ ██▀███ ", - " ██▒ ▀█▒▒████▄ ▓██▒▀█▀ ██▒▓█ ▀ ▒██▒ ██▒▓██░ █▒▓█ ▀ ▓██ ▒ ██▒ ", - " ▒██░▄▄▄░▒██ ▀█▄ ▓██ ▓██░▒███ ▒██░ ██▒ ▓██ █▒░▒███ ▓██ ░▄█ ▒ ", - " ░▓█ ██▓░██▄▄▄▄██ ▒██ ▒██ ▒▓█ ▄ ▒██ ██░ ▒██ █░░▒▓█ ▄ ▒██▀▀█▄ ", - " ░▒▓███▀▒ ▓█ ▓██▒▒██▒ ░██▒░▒████▒ ░ ████▓▒░ ▒▀█░ ░▒████▒░██▓ ▒██▒ ", - " ░▒ ▒ ▒▒ ▓▒█░░ ▒░ ░ ░░░ ▒░ ░ ░ ▒░▒░▒░ ░ ▐░ ░░ ▒░ ░░ ▒▓ ░▒▓░ ", - " ░ ░ ▒ ▒▒ ░░ ░ ░ ░ ░ ░ ░ ▒ ▒░ ░ ░░ ░ ░ ░ ░▒ ░ ▒░ ", - " ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ▒ ░░ ░ ░░ ░ ", - " ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ", - " ░ ", -]; diff --git a/examples/program_examples/snake/src/snake.rs b/examples/program_examples/snake/src/snake.rs deleted file mode 100644 index 9f29efc..0000000 --- a/examples/program_examples/snake/src/snake.rs +++ /dev/null @@ -1,260 +0,0 @@ -use std::fmt; - -use crossterm::Result; - -use super::types::{Direction, Position}; - -/// A snake fragment kind. -/// -/// Describes how a snake fragment is visualized. -#[derive(Debug, Clone, Copy)] -enum FragmentKind { - Horizontal, - Vertical, - Left, - Right, - Up, - Down, - UpToRight, - UpToLeft, - DownToRight, - DownToLeft, - LeftToUp, - LeftToDown, - RightToUp, - RightToDown, -} - -impl FragmentKind { - /// Creates a snake fragment kind from the snake head direction. - fn with_head_direction(direction: Direction) -> Self { - match direction { - Direction::Up => FragmentKind::Up, - Direction::Down => FragmentKind::Down, - Direction::Left => FragmentKind::Left, - Direction::Right => FragmentKind::Right, - } - } -} - -impl fmt::Display for FragmentKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let repr = match self { - FragmentKind::Horizontal => "━", - FragmentKind::Vertical => "┃", - FragmentKind::Up => "╻", - FragmentKind::Down => "╹", - FragmentKind::Left => "╺", - FragmentKind::Right => "╸", - FragmentKind::UpToRight | FragmentKind::LeftToDown => "┏", - FragmentKind::UpToLeft | FragmentKind::RightToDown => "┓", - FragmentKind::DownToRight | FragmentKind::LeftToUp => "┗", - FragmentKind::DownToLeft | FragmentKind::RightToUp => "┛", - }; - write!(f, "{}", repr) - } -} - -/// A snake fragment. -#[derive(Debug)] -struct Fragment { - /// The fragment position in the terminal. - position: Position, - /// The fragment kind. - kind: FragmentKind, -} - -impl Fragment { - /// Creates a `Fragment` from the given `position` and `kind`. - fn new>(position: P, kind: FragmentKind) -> Fragment { - Fragment { - position: position.into(), - kind, - } - } - - /// Draws the fragment. - fn draw(&self) -> Result<()> { - self.position.draw(self.kind) - } - - /// Clears the fragment from the screen. - fn clear(&self) -> Result<()> { - self.position.clear_char() - } - - /// Creates a new head `Fragment` from the given snake `direction`. - /// - /// Assumes that the `self` is the current snake head. - fn with_head_direction(&self, direction: Direction) -> Fragment { - let position = match direction { - Direction::Up => (self.position.x, self.position.y - 1), - Direction::Down => (self.position.x, self.position.y + 1), - Direction::Left => (self.position.x - 1, self.position.y), - Direction::Right => (self.position.x + 1, self.position.y), - }; - - Fragment { - position: position.into(), - kind: FragmentKind::with_head_direction(direction), - } - } - - /// Updates the fragment kind with the given snake `direction` and redraws - /// it. - /// - /// Assumes that the `self` is the current snake head (soon to be the old - /// one = 2nd fragment). - /// - /// # Examples - /// - /// Let's say we have a snake `━╸` that is moving to the `Right` and the new - /// direction is `Up`. We can't create / draw a new head only, because then - /// the snake will look like this one: - /// - /// ``` - /// ╻ - /// ━╸ - /// ``` - /// - /// We have to change the current head kind `╻` to `┛` to get a snake like this - /// one: - /// - /// ``` - /// ╻ - /// ━┛ - /// ``` - fn update_with_head_direction(&mut self, direction: Direction) -> Result<()> { - let new_kind = match (self.kind, direction) { - (FragmentKind::Left, Direction::Left) => FragmentKind::Horizontal, - (FragmentKind::Right, Direction::Right) => FragmentKind::Horizontal, - (FragmentKind::Up, Direction::Up) => FragmentKind::Vertical, - (FragmentKind::Down, Direction::Down) => FragmentKind::Vertical, - (FragmentKind::Left, Direction::Up) => FragmentKind::LeftToUp, - (FragmentKind::Left, Direction::Down) => FragmentKind::LeftToDown, - (FragmentKind::Right, Direction::Up) => FragmentKind::RightToUp, - (FragmentKind::Right, Direction::Down) => FragmentKind::RightToDown, - (FragmentKind::Up, Direction::Left) => FragmentKind::UpToLeft, - (FragmentKind::Up, Direction::Right) => FragmentKind::UpToRight, - (FragmentKind::Down, Direction::Left) => FragmentKind::DownToLeft, - (FragmentKind::Down, Direction::Right) => FragmentKind::DownToRight, - (kind, _) => kind, - }; - self.kind = new_kind; - self.draw() - } -} - -/// A snake. -#[derive(Debug)] -pub struct Snake { - /// The snake fragments. Head index is always 0. - fragments: Vec, - /// The current direction. - direction: Direction, - /// Says if the snake ate some food or not. - ate_food: bool, -} - -impl Snake { - /// Creates a new snake. - pub fn new(map_width: u16, map_height: u16) -> Snake { - let center_x = map_width / 2; - let center_y = map_height / 2; - - let parts = vec![ - Fragment::new((center_x, center_y), FragmentKind::Right), - Fragment::new((center_x - 1, center_y), FragmentKind::Left), - ]; - - Snake { - fragments: parts, - direction: Direction::Right, - ate_food: false, - } - } - - /// The current snake length. - pub fn len(&self) -> usize { - self.fragments.len() - } - - /// The current snake direction. - pub fn direction(&self) -> Direction { - self.direction - } - - /// Updates the snake direction. - pub fn set_direction(&mut self, direction: Direction) { - self.direction = direction; - } - - /// Sets if the snake ate food. - /// - /// If set to `true`, the next `update()` call will move the head, but - /// won't move the tail. - pub fn set_ate_food(&mut self, ate_food: bool) { - self.ate_food = ate_food; - } - - /// The snake head position. - pub fn head_position(&self) -> Position { - self.fragments[0].position - } - - /// Returns `true` if there's an existing snake fragment at the - /// given `position`. - pub fn fragment_exists_at_position(&self, position: Position) -> bool { - for fragment in &self.fragments { - if fragment.position == position { - return true; - } - } - false - } - - /// Moves the snake and redraws updated fragments only. - /// - /// Returns `Ok(true)` if the snake was updated. Returns `Ok(false)` if the - /// new head position collides with the existing snake fragments. - pub fn update(&mut self) -> Result { - // Get the current head, update fragment kind and redraw it. - // Let's say that the snake is moving down (╻). We want to draw a full - // vertical line (┃) at the current head position. - let current_head = self.fragments.first_mut().unwrap(); - current_head.update_with_head_direction(self.direction)?; - - // Create & draw the new head. - let new_head = current_head.with_head_direction(self.direction); - new_head.draw()?; - - // Check if the new head collides with existing snake fragments - if self.fragment_exists_at_position(new_head.position) { - // Collision, new snake head collides with existing fragment - return Ok(false); - } - - self.fragments.insert(0, new_head); - - if self.ate_food { - // Snake ate some food, we just set the state to false and - // do nothing (no tail movement) - self.ate_food = false; - } else { - // Snake didn't eat any food, tail is moving, which means that - // we are going to clear the fragment from the screen and drop it - let tail = self.fragments.pop().unwrap(); - tail.clear()?; - } - - Ok(true) - } - - /// Redraws the snake. - pub fn draw(&self) -> Result<()> { - for fragment in &self.fragments { - fragment.draw()?; - } - Ok(()) - } -} diff --git a/examples/program_examples/snake/src/types.rs b/examples/program_examples/snake/src/types.rs deleted file mode 100644 index 23ab049..0000000 --- a/examples/program_examples/snake/src/types.rs +++ /dev/null @@ -1,103 +0,0 @@ -use std::convert::TryFrom; -use std::fmt::Display; -use std::io::{stdout, Write}; - -use crossterm::{style, Color, Crossterm, KeyEvent, Result, TerminalCursor}; - -/// Position in the terminal window. -#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] -pub struct Position { - /// The position column index (0 based). - pub x: u16, - /// The position row index (0 based). - pub y: u16, -} - -impl Position { - /// Creates a new position from the given `x` & `y`. - pub fn new(x: u16, y: u16) -> Position { - Position { x, y } - } - - /// Draws the given `value` at this position. - pub fn draw(&self, value: D) -> Result<()> { - let cursor = TerminalCursor::new(); - cursor.goto(self.x, self.y)?; - - print!("{}", style(value).with(Color::Red)); - stdout().flush()?; - Ok(()) - } - - /// Clears character (writes single space) at this position. - pub fn clear_char(&self) -> Result<()> { - let crossterm = Crossterm::new(); - crossterm.cursor().goto(self.x, self.y)?; - crossterm.terminal().write(" ")?; - Ok(()) - } -} - -/// Crates a `Position` from a `(u16, u16)` tuple. -impl From<(u16, u16)> for Position { - fn from(pos: (u16, u16)) -> Self { - Position::new(pos.0, pos.1) - } -} - -/// A snake direction. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Direction { - Up, - Down, - Left, - Right, -} - -impl Direction { - /// Returns `true` if the direction is vertical (`Up` or `Down`). - fn is_vertical(&self) -> bool { - self == &Direction::Up || self == &Direction::Down - } - - /// Returns `true` if the direction can be changed to the given - /// `direction`. - /// - /// It's allowed to change direction from vertical to horizontal - /// and vice versa. It's not allowed to change the `Right` direction - /// to either `Left` or `Right`, but it's allowed to change it - /// to either `Up` or `Down`. - pub fn can_change_to(&self, direction: Direction) -> bool { - self.is_vertical() != direction.is_vertical() - } -} - -/// Tries to create a `Direction` from the `KeyEvent` (arrow keys). -impl TryFrom for Direction { - type Error = (); - - fn try_from(value: KeyEvent) -> std::result::Result { - match value { - KeyEvent::Up => Ok(Direction::Up), - KeyEvent::Left => Ok(Direction::Left), - KeyEvent::Down => Ok(Direction::Down), - KeyEvent::Right => Ok(Direction::Right), - _ => Err(()), - } - } -} - -/// Tries to create a `Direction` from the `char` (WASD keys). -impl TryFrom for Direction { - type Error = (); - - fn try_from(value: char) -> std::result::Result { - match value { - 'w' => Ok(Direction::Up), - 'a' => Ok(Direction::Left), - 's' => Ok(Direction::Down), - 'd' => Ok(Direction::Right), - _ => Err(()), - } - } -} diff --git a/examples/raw_mode.rs b/examples/raw_mode.rs deleted file mode 100644 index e9f4d42..0000000 --- a/examples/raw_mode.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::io::{stdout, Write}; -use std::{thread, time}; - -use crossterm::{style, AlternateScreen, ClearType, Color, Crossterm, Result}; - -fn print_wait_screen() -> Result<()> { - let crossterm = Crossterm::new(); - let terminal = crossterm.terminal(); - let cursor = crossterm.cursor(); - - terminal.clear(ClearType::All)?; - - cursor.hide()?; - cursor.goto(0, 0)?; - println!("Welcome to the wait screen."); - cursor.goto(0, 1)?; - println!("Please wait a few seconds until we arrive back at the main screen."); - cursor.goto(0, 2)?; - println!("Progress:"); - cursor.goto(0, 3)?; - - // print some progress example. - for i in 1..5 { - // print the current counter at the line of `Seconds to Go: {counter}` - cursor.goto(10, 2)?; - print!( - "{}", - style(format!("{} of the 5 items processed", i)) - .with(Color::Red) - .on(Color::Blue) - ); - - stdout().flush()?; - - // 1 second delay - thread::sleep(time::Duration::from_secs(1)); - } - - Ok(()) -} - -fn print_wait_screen_on_alternate_window() -> Result<()> { - // by passing in 'true' the alternate screen will be in raw modes. - let _alt = AlternateScreen::to_alternate(true)?; - print_wait_screen() -} - -// cargo run --example raw_mode -fn main() -> Result<()> { - print_wait_screen_on_alternate_window() -} diff --git a/examples/style.rs b/examples/style.rs deleted file mode 100644 index 0cda023..0000000 --- a/examples/style.rs +++ /dev/null @@ -1,417 +0,0 @@ -//! -//! Examples of coloring the terminal. -//! - -#![allow(dead_code)] - -use crossterm::{color, Attribute, Color, Colored, Colorize, Styler}; - -/// print some red text | demonstration. -fn paint_foreground() { - println!("{}", "Red foreground text: {}".red()); - println!("{} Red foreground text", Colored::Fg(Color::Red)); -} - -/// print some text on red background | demonstration. -fn paint_background() { - println!("{}", "Red background text: {}".on_red()); - println!("{} Red background text", Colored::Bg(Color::Red)); -} - -/// Print all available foreground colors | demonstration. -fn print_all_foreground_colors_with_enum() { - // we use `Reset` to restore the foreground back to normal at the end of the line. - println!( - "Black : \t\t {} ■ {}\n", - Colored::Fg(Color::Black), - Attribute::Reset - ); - println!( - "DarkGrey : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkGrey), - Attribute::Reset - ); - println!( - "Red : \t\t {} ■ {}\n", - Colored::Fg(Color::Red), - Attribute::Reset - ); - println!( - "DarkRed : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkRed), - Attribute::Reset - ); - println!( - "Cyan : \t\t {} ■ {}\n", - Colored::Fg(Color::Cyan), - Attribute::Reset - ); - println!( - "DarkCyan : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkCyan), - Attribute::Reset - ); - println!( - "Green : \t\t {} ■ {}\n", - Colored::Fg(Color::Green), - Attribute::Reset - ); - println!( - "DarkGreen : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkGreen), - Attribute::Reset - ); - println!( - "Blue : \t\t {} ■ {}\n", - Colored::Fg(Color::Blue), - Attribute::Reset - ); - println!( - "DarkBlue : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkBlue), - Attribute::Reset - ); - println!( - "Magenta : \t\t {} ■ {}\n", - Colored::Fg(Color::Magenta), - Attribute::Reset - ); - println!( - "DarkMagenta : \t\t{} ■ {}\n", - Colored::Fg(Color::DarkMagenta), - Attribute::Reset - ); - println!( - "Yellow : \t\t {} ■ {}\n", - Colored::Fg(Color::Yellow), - Attribute::Reset - ); - println!( - "DarkYellow : \t\t {} ■ {}\n", - Colored::Fg(Color::DarkYellow), - Attribute::Reset - ); - println!( - "Grey : \t\t {} ■ {}\n", - Colored::Fg(Color::Grey), - Attribute::Reset - ); - println!( - "White : \t\t {} ■ {}\n", - Colored::Fg(Color::White), - Attribute::Reset - ); - - // custom rgb value (Windows 10 and UNIX systems) - println!( - "{} some colored text", - Colored::Fg(Color::Rgb { - r: 10, - g: 10, - b: 10 - }) - ); - - // custom ansi color value (Windows 10 and UNIX systems) - println!("{} some colored text", Colored::Fg(Color::AnsiValue(10))); -} - -/// Print all available foreground colors | demonstration. -fn print_all_foreground_colors_with_method() { - println!( - "Black : \t\t {} {}\n", - "■".black(), - Attribute::Reset - ); - println!( - "DarkGrey : \t\t {} {}\n", - "■".dark_grey(), - Attribute::Reset - ); - println!("Red : \t\t {} {}\n", "■".red(), Attribute::Reset); - println!( - "DarkRed : \t\t {} {}\n", - "■".dark_red(), - Attribute::Reset - ); - println!("Cyan : \t\t {} {}\n", "■".cyan(), Attribute::Reset); - println!( - "DarkCyan : \t\t {} {}\n", - "■".dark_cyan(), - Attribute::Reset - ); - println!( - "Green : \t\t {} {}\n", - "■".green(), - Attribute::Reset - ); - println!( - "DarkGreen : \t\t {} {}\n", - "■".dark_green(), - Attribute::Reset - ); - println!("Blue : \t\t {} {}\n", "■".blue(), Attribute::Reset); - println!( - "DarkBlue : \t\t {} {}\n", - "■".dark_blue(), - Attribute::Reset - ); - println!( - "Magenta : \t\t {} {}\n", - "■".magenta(), - Attribute::Reset - ); - println!( - "DarkMagenta : \t\t {} {}\n", - "■".dark_magenta(), - Attribute::Reset - ); - println!( - "Yellow : \t\t {} {}\n", - "■".yellow(), - Attribute::Reset - ); - println!( - "DarkYellow : \t\t {} {}\n", - "■".dark_yellow(), - Attribute::Reset - ); - println!("Grey : \t\t {} {}\n", "■".grey(), Attribute::Reset); - println!( - "White : \t\t {} {}\n", - "■".white(), - Attribute::Reset - ); -} - -/// Print all available foreground colors | demonstration. -fn print_all_background_colors_with_enum() { - println!( - "Black : \t\t {} ■ {}\n", - Colored::Bg(Color::Black), - Attribute::Reset - ); - println!( - "DarkGrey : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkGrey), - Attribute::Reset - ); - println!( - "Red : \t\t {} ■ {}\n", - Colored::Bg(Color::Red), - Attribute::Reset - ); - println!( - "DarkRed : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkRed), - Attribute::Reset - ); - println!( - "Cyan : \t\t {} ■ {}\n", - Colored::Bg(Color::Cyan), - Attribute::Reset - ); - println!( - "DarkCyan : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkCyan), - Attribute::Reset - ); - println!( - "Green : \t\t {} ■ {}\n", - Colored::Bg(Color::Green), - Attribute::Reset - ); - println!( - "DarkGreen : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkGreen), - Attribute::Reset - ); - println!( - "Blue : \t\t {} ■ {}\n", - Colored::Bg(Color::Blue), - Attribute::Reset - ); - println!( - "DarkBlue : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkBlue), - Attribute::Reset - ); - println!( - "Magenta : \t\t {} ■ {}\n", - Colored::Bg(Color::Magenta), - Attribute::Reset - ); - println!( - "DarkMagenta : \t\t{} ■ {}\n", - Colored::Bg(Color::DarkMagenta), - Attribute::Reset - ); - println!( - "Yellow : \t\t {} ■ {}\n", - Colored::Bg(Color::Yellow), - Attribute::Reset - ); - println!( - "DarkYellow : \t\t {} ■ {}\n", - Colored::Bg(Color::DarkYellow), - Attribute::Reset - ); - println!( - "Grey : \t\t {} ■ {}\n", - Colored::Bg(Color::Grey), - Attribute::Reset - ); - println!( - "White : \t\t {} ■ {}\n", - Colored::Bg(Color::White), - Attribute::Reset - ); - - // custom rgb value (Windows 10 and UNIX systems) - println!( - "{} some colored text", - Colored::Bg(Color::Rgb { - r: 80, - g: 10, - b: 10 - }) - ); - - // custom ansi color value (Windows 10 and UNIX systems) - println!("{} some colored text", Colored::Bg(Color::AnsiValue(10))); -} - -/// Print all available foreground colors | demonstration. -fn print_all_background_colors_with_method() { - println!( - "Black : \t\t {} {}\n", - "■".on_black(), - Attribute::Reset - ); - println!( - "DarkGrey : \t\t {} {}\n", - "■".on_dark_grey(), - Attribute::Reset - ); - println!( - "Red : \t\t {} {}\n", - "■".on_red(), - Attribute::Reset - ); - println!( - "DarkRed : \t\t {} {}\n", - "■".on_dark_red(), - Attribute::Reset - ); - println!( - "Cyan : \t\t {} {}\n", - "■".on_cyan(), - Attribute::Reset - ); - println!( - "DarkCyan : \t\t {} {}\n", - "■".on_dark_cyan(), - Attribute::Reset - ); - println!( - "Green : \t\t {} {}\n", - "■".on_green(), - Attribute::Reset - ); - println!( - "DarkGreen : \t\t {} {}\n", - "■".on_dark_green(), - Attribute::Reset - ); - println!( - "Blue : \t\t {} {}\n", - "■".on_blue(), - Attribute::Reset - ); - println!( - "DarkBlue : \t\t {} {}\n", - "■".on_dark_blue(), - Attribute::Reset - ); - println!( - "Magenta : \t\t {} {}\n", - "■".on_magenta(), - Attribute::Reset - ); - println!( - "DarkMagenta : \t\t {} {}\n", - "■".on_dark_magenta(), - Attribute::Reset - ); - println!( - "Yellow : \t\t {} {}\n", - "■".on_yellow(), - Attribute::Reset - ); - println!( - "DarkYellow : \t\t {} {}\n", - "■".on_dark_yellow(), - Attribute::Reset - ); - println!( - "Grey : \t\t {} {}\n", - "■".on_grey(), - Attribute::Reset - ); - println!( - "White : \t\t {} {}\n", - "■".on_white(), - Attribute::Reset - ); -} - -/// Print text with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration.. -#[cfg(unix)] -fn print_text_with_attributes() { - println!("{}", "Normal text"); - println!("{}", "Bold text".bold()); - println!("{}", "Italic text".italic()); - println!("{}", "Slow blinking text".slow_blink()); - println!("{}", "Rapid blinking text".rapid_blink()); - println!("{}", "Hidden text".hidden()); - println!("{}", "Underlined text".underlined()); - println!("{}", "Reversed text".reverse()); - println!("{}", "Dim text".dim()); - println!("{}", "Crossed out text".crossed_out()); - // ... - - println!( - "{} Underlined {} No Underline", - Attribute::Underlined, - Attribute::NoUnderline - ); - // ... -} - -// Print text with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration.. -#[cfg(windows)] -fn print_text_with_attributes() { - println!("{}", "Normal text"); - println!("{}", "Bold text".bold()); - println!("{}", "Underlined text".underlined()); - println!("{}", "Negative text".negative()); -} - -/// Print all supported RGB colors, not supported for Windows systems < 10 | demonstration. -fn print_supported_colors() { - let count = color().available_color_count(); - - for i in 0..count { - println!("Test {}", Colored::Bg(Color::AnsiValue(i as u8))); - } -} - -fn reset_fg_and_bg() { - println!("{}", Colored::Fg(Color::Reset)); - println!("{}", Colored::Bg(Color::Reset)); -} - -// cargo run --example style -fn main() { - print_all_background_colors_with_method() -} diff --git a/examples/terminal.rs b/examples/terminal.rs deleted file mode 100644 index 6006292..0000000 --- a/examples/terminal.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! -//! Terminal Examples -//! - -#![allow(dead_code)] - -use crossterm::{cursor, terminal, ClearType, Result}; - -fn print_test_data() { - for i in 0..100 { - println!("Test data to test terminal: {}", i); - } -} - -/// Clear all lines in terminal | demonstration -fn clear_all_lines() -> Result<()> { - 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 -fn clear_from_cursor_down() -> Result<()> { - 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 -fn clear_from_cursor_up() -> Result<()> { - 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 -fn clear_current_line() -> Result<()> { - 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 -fn clear_until_new_line() -> Result<()> { - 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. -fn print_terminal_size() -> Result<()> { - let terminal = terminal(); - - // Get terminal size - let (width, height) = terminal.size()?; - - // Print results - print!("X: {}, y: {}", width, height); - Ok(()) -} - -/// Set the terminal size to width 10, height: 10 | demonstration. -fn set_terminal_size() -> Result<()> { - let terminal = terminal(); - - terminal.set_size(10, 10) -} - -/// Scroll down 10 lines | demonstration. -fn scroll_down() -> Result<()> { - let terminal = terminal(); - - print_test_data(); - - // Scroll down 10 lines. - terminal.scroll_down(10) -} - -/// Scroll down 10 lines | demonstration. -fn scroll_up() -> Result<()> { - let terminal = terminal(); - - print_test_data(); - - // Scroll up 10 lines. - terminal.scroll_up(10) -} - -/// exit the current proccess. -fn exit() { - let terminal = terminal(); - terminal.exit(); -} - -// cargo run --example terminal -fn main() -> Result<()> { - scroll_down() -} diff --git a/scripts/test-examples.sh b/scripts/test-examples.sh deleted file mode 100755 index 4cafc03..0000000 --- a/scripts/test-examples.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -set -ev -pushd examples/program_examples -for d in */ ; do - pushd "$d" - cargo build - if [ "$TRAVIS_RUST_VERSION" = "stable" ]; then - cargo fmt --all -- --check - fi - popd -done -popd