diff --git a/README.md b/README.md index d8a919d..576e86d 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,55 @@ # Crossterm | cross-platform terminal manipulating library written in rust. + ![Lines of Code][s7] [![Latest Version][s1]][l1] [![MIT][s2]][l2] [![docs][s3]][l3] ![Lines of Code][s6] -[![Latest Version](https://img.shields.io/crates/v/crossterm.svg)](https://crates.io/crates/crossterm) | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) | [![docs.rs](https://docs.rs/crossterm/badge.svg)](https://docs.rs/crossterm/) | [Examples](https://github.com/TimonPost/crossterm/tree/master/examples) | [Changelog](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) | [Release Nodes](https://github.com/TimonPost/crossterm/blob/master/docs/ReleaseNotes.md) -|----|----|----|----|----|---- +[s1]: https://img.shields.io/crates/v/crossterm.svg +[l1]: https://crates.io/crates/crossterm -Ever got disappointed when a terminal library for rust was only written for unix systems? -Crossterm provides the same core functionalities for both windows and unix systems. +[s2]: https://img.shields.io/badge/license-MIT-blue.svg +[l2]: ./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/ + +[s6]: https://tokei.rs/b1/github/TimonPost/crossterm?category=code +[s7]: https://travis-ci.org/TimonPost/crossterm.svg?branch=master + +Ever got disappointed when a terminal library for rust was only written for UNIX systems? +Crossterm provides the same core functionalities for both Windows and UNIX systems. Crossterm aims to be simple and easy to call in code. -Though the simplicity of Crossterm, you do not have to worry about the platform you are working with. -You can just call whatever action you want and behind the scenes it will check what to do based on the current platform. +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](https://github.com/TimonPost/crossterm#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) ## Table of contents: -- [Getting started](https://github.com/TimonPost/crossterm#getting-started) -- [Useful links](https://github.com/TimonPost/crossterm#useful-links) -- [Features](https://github.com/TimonPost/crossterm#features) -- [Examples](https://github.com/TimonPost/crossterm#examples) - - [Crossterm Wrapper](https://github.com/TimonPost/crossterm#crossterm-type--see-more) - - [Styling](https://github.com/TimonPost/crossterm#crossterm-type--see-more) - - [Cursor](https://github.com/TimonPost/crossterm#cursor--see-more) - - [Input](https://github.com/TimonPost/crossterm#input--see-more) - - [Terminal](https://github.com/TimonPost/crossterm#terminal--see-more) -- [Tested Terminals](https://github.com/TimonPost/crossterm#tested-terminals) -- [How it works](https://github.com/TimonPost/crossterm#how-it-works) -- [Notice](https://github.com/TimonPost/crossterm#notice) -- [Todo](https://github.com/TimonPost/crossterm#todo) -- [Contributing](https://github.com/TimonPost/crossterm#contributing) -- [Authors](https://github.com/TimonPost/crossterm#authors) -- [License](https://github.com/TimonPost/crossterm#license) +- [Getting started](#getting-started) +- [Useful links](#useful-links) +- [Features](#features) +- [Examples](#examples) + - [Crossterm Wrapper](#crossterm-type--see-more) + - [Styling](#crossterm-type--see-more) + - [Cursor](#cursor--see-more) + - [Input](#input--see-more) + - [Terminal](#terminal--see-more) +- [Tested Terminals](#tested-terminals) +- [Notice](#notice) +- [Todo](#todo) +- [Contributing](#contributing) +- [Authors](#authors) +- [License](#license) ## Getting Started -This documentation is only for Crossterm version `0.4.*` if you have an older version of Crossterm I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) for more information about how to upgrade to a newer version. -Also check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders which detailed examples for all functionality of this crate. - +This documentation is only for Crossterm version `0.5` if you have an older version of Crossterm I suggest you check the [Upgrade Manual](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md). Also, check out the [examples](https://github.com/TimonPost/crossterm/tree/master/examples) folders which detailed examples for all functionality of this crate. Add the Crossterm package to your `Cargo.toml` file. ``` [dependencies] -crossterm = "0.4.0" +crossterm = "0.5.0" ``` And import the Crossterm modules you want to use. @@ -49,55 +58,57 @@ And import the Crossterm modules you want to use. extern crate crossterm; // this module is used for styling the terminal -use self::crossterm::style::*; +use crossterm::style::*; // this module is used for cursor related actions -use self::crossterm::cursor::*; +use crossterm::cursor::*; // this mudule is used for terminal related actions -use self::crossterm::terminal::*; +use crossterm::terminal::*; // this mudule is used for input related actions -use self::crossterm::input::*; +use crossterm::input::*; ``` -## Useful Links +### Useful Links -- Code [documentation](https://docs.rs/crossterm/). -- The [Cargo Page](https://crates.io/crates/crossterm) -- More real live [examples](https://github.com/TimonPost/crossterm/tree/master/examples/program_examples) -- Detailed [examples](https://github.com/TimonPost/crossterm/tree/master/examples) +- [Book](http://atcentra.com/crossterm/) +- [Documentation](https://docs.rs/crossterm/) +- [Crates.io](https://crates.io/crates/crossterm) +- [Program Examples](https://github.com/TimonPost/crossterm/tree/master/examples/program_examples) +- [Examples](https://github.com/TimonPost/crossterm/tree/master/examples) ## Features These are the features from this crate: +- Cross-platform +- Everything is multithreaded (Send, Sync) +- Detailed documentation on every item - Cursor. - - Moving _n_ times Up, Down, Left, Right. - - Goto a certain position. + - Moving _n_ times Up, Down, Left, Right + - Goto a certain position - Get cursor position - - Storing the current cursor position and resetting to that stored cursor position later. - - Hiding an showing the cursor. - - Control over blinking of the terminal cursor (only some terminals are supporting this). + - Storing the current cursor position and resetting to that stored cursor position later + - Hiding an showing the cursor + - Control over blinking of the terminal cursor (only some terminals are supporting this) - Styled output - Foreground color (16 base colors) - Background color (16 base colors) - - 256 color support (unix only). - - Text Attributes like: bold, italic, underscore and crossed word ect (unix only). - - Custom ANSI color code input to set fore- and background color (unix only). + - 256 color support (unix only) + - Text Attributes like: bold, italic, underscore and crossed word ect (unix only) + - Custom ANSI color code input to set fore- and background color (unix only) - Terminal - Clearing (all lines, current line, from cursor down and up, until new line) - Scrolling (Up, down) - Get the size of the terminal - - Set the size of the terminal. + - Set the size of the terminal - Alternate screen - - Raw screen + - Raw screen + - Exit the current process - Input - Read character - Read line - Read async - Read async until -- Exit the current process. -- Everything is multithreaded (Send, Sync) -- Detailed documentation on every item. -- Crossplatform + - Wait for key event (terminal pause) ## Examples These are some basic examples demonstrating how to use this crate. See [examples](https://github.com/TimonPost/crossterm/blob/master/examples/) for more. @@ -107,8 +118,7 @@ This is a wrapper for all the modules crossterm provides like terminal, cursor, ```rust // screen wheron the `Crossterm` methods will be executed. -let screen = Screen::default(); -let crossterm = Crossterm::new(&screen); +let crossterm = Crossterm::new(); // get instance of the modules, whereafter you can use the methods the particulary module provides. let color = crossterm.color(); @@ -116,22 +126,18 @@ let cursor = crossterm.cursor(); let terminal = crossterm.terminal(); // styling -let style = crossterm.style("Black font on Green background color").with(Color::Black).on(Color::Green); -style.paint(&screen); +println!("{}", crossterm.style("Black font on Green background color").with(Color::Black).on(Color::Green)); ``` -### Styled Font | [see more](https://github.com/TimonPost/crossterm/blob/master/examples/color/mod.rs) -This module provides the functionalities to style the terminal cursor. +### Styled Font | [see more](http://atcentra.com/crossterm/styling.html) +This module provides the functionalities to style the terminal. ```rust use crossterm::style::{Color, style}; -use crossterm::Screen; // store objcets so it could be painted later to the screen. let style1 = style("Some Blue font on Black background").with(Color::Blue).on(Color::Black); let style2 = style("Some Red font on Yellow background").with(Color::Red).on(Color::Yellow); -let screen = Screen::default(); - /// !! The following code only works for unix based systems !! // some attributes let normal = style("Normal text"); @@ -146,33 +152,30 @@ let dimmed = style("Dim text").dim(); let crossed_out = style("Crossed out font").crossed_out(); // paint styled text to screen (this could also be called inline) -style1.paint(&screen); -style2.paint(&screen); -bold.paint(&screen); -hidden.paint(&screen); +println!("{}", style1); +println!("{}", style2); +println!("{}", bold); +println!("{}", hidden); +... // cursom rgb value style("RGB color (10,10,10) ").with(Color::Rgb { r: 10, g: 10, b: 10 -}).paint(&screen); +})); // custom ansi color value -style("ANSI color value (50) ").with(Color::AnsiValue(50)).paint(&screen); +style("ANSI color value (50) ").with(Color::AnsiValue(50)); ``` ### Cursor | [see more](https://github.com/TimonPost/crossterm/blob/master/examples/cursor/mod.rs) This module provides the functionalities to work with the terminal cursor. ```rust - -use crossterm::Screen; use crossterm::cursor; -// create Screen to wheron the `cursor()` should function. -let screen = Screen::default(); -let mut cursor = cursor(&screen); +let mut cursor = cursor(); /// Moving the cursor // Set the cursor to position X: 10, Y: 5 in the terminal @@ -207,19 +210,14 @@ cursor.blink(true) ``` -### Input | [see more](https://github.com/TimonPost/crossterm/tree/master/examples/input) +### Input | [see more](http://atcentra.com/crossterm/input.html) This module provides the functionalities to work with terminal input. -Check [this](https://github.com/TimonPost/crossterm/blob/master/examples/input/keyboard/async_input.rs) for handling async input. - ```rust - -use crossterm::Screen; use crossterm::input; // create Screen to wheron the `cursor()` should function. -let screen = Screen::default(); -let mut input = input(&screen); +let mut input = input(); match input.read_char() { Ok(s) => println!("char typed: {}", s), @@ -238,11 +236,9 @@ This module provides the functionalities to work with the terminal in general. ```rust use crossterm::terminal::{terminal,ClearType}; -use crossterm::Screen; // create Screen to wheron the `terminal()` should function. -let screen = Screen::default(); -let mut terminal = terminal(&screen); +let mut terminal = terminal(); // Clear all lines in terminal; terminal.clear(ClearType::All); @@ -273,7 +269,8 @@ terminal.exit(); terminal.write("Some text\n Some text on new line"); ``` -Check these links: [AlternateScreen](https://github.com/TimonPost/crossterm/blob/master/examples/terminal/alternate_screen.rs) and [RawScreen](https://github.com/TimonPost/crossterm/blob/master/examples/terminal/raw_mode.rs) for information about how to work with these features. +### Alternate and Raw Screen +These concepts are a little more complex, please checkout the [book](http://atcentra.com/crossterm/screen.html) topics about these subjects. ## Tested terminals @@ -284,14 +281,15 @@ Check these links: [AlternateScreen](https://github.com/TimonPost/crossterm/blob - Windows 8.1 (N) - Ubuntu Desktop Terminal - Ubuntu 17.10 -- Arch linux Konsole +- (Arch, Manjaro) KDE Konsole +- Linux Mint This crate supports all Unix terminals and windows terminals down to Windows 7 but not all of them have been tested. If you have used this library for a terminal other than the above list without issues feel free to add it to the above list, I really would appreciate it. ## Notice -This library is not stable yet but I expect it to not to change that much anymore. -And if there are any changes that affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change when upgrading Crossterm to a newer version. +This library is average stable now but I don't expect it to not to change that much. +If there are any changes that will affect previous versions I will [describe](https://github.com/TimonPost/crossterm/blob/master/docs/UpgradeManual.md) what to change to upgrade. ## Todo I still have some things in mind to implement. diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 0f9926c..7c36bbd 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -1,12 +1,12 @@ # Changes crossterm 0.5.0 - Implemented Display for styled object. -- Removed `Screen` from the following functions: `crossterm::cursor(), crossterm::color(), crossterm::terminal()`, you won't need to care about `Screen` unless you are working with alternate or raw screen. -- more to come ... +- More convenient API, no need to care about `Screen` unless working with when working with alternate or raw screen [PR](https://github.com/TimonPost/crossterm/pull/44) +- Added ability to pause the terminal [issue](https://github.com/TimonPost/crossterm/issues/39) # Changes crossterm to 0.4.3 - Fixed bug [issue 41](https://github.com/TimonPost/crossterm/issues/41) -# Changes crossterm to 0.4.2 +# Changes crossterm to 0.4.2 - Added functionality to make a styled object writable to screen [issue 33](https://github.com/TimonPost/crossterm/issues/33) - Added unit tests. - Bugfix with getting terminal size unix. diff --git a/docs/UpgradeManual.md b/docs/UpgradeManual.md index 14aac3d..8dbfb28 100644 --- a/docs/UpgradeManual.md +++ b/docs/UpgradeManual.md @@ -1,3 +1,68 @@ +## Upgrade crossterm to 0.5.0 + +***WARNING*** + +I workded on making the user API more conviniant therefore I had to make some chages to the user API. The problem with `0.4` is that you need to pass a `Screen` to the modules: `cursor(), color(), terminal()`. + +In the new situation you only have to do this when working with raw or alternate screen. When you just want to perform actions like styling on the main screen you don't have to to pass in the `Screen` any more. This will look like the following: + +#### 1. Remove `Screen` from the function calls: `cursor(), color(), terminal(), input()` + +_**old**_ +``` +let screen = Screen::default(); + +let color = color(&screen); +let cursor = cursor(&screen); +let input = input(&screen); +let terminal = terminal(&screen); +let crossterm = Crossterm::new(&screen); +let terminal = Terminal::new(&screen.stdout); +let cursor = TerminalCursor::new(&screen.stdout); +let color = TerminalColor::new(&screen.stdout); +let input = TerminalInput::new(&screen.stdout); +``` +_**new**_ +``` +let color = color(); +let cursor = cursor(); +let input = input(); +let terminal = terminal(); +let crossterm = Crossterm::new(); +let terminal = Terminal::new(); +let cursor = TerminalCursor::new(); +let color = TerminalColor::new(); +let input = TerminalInput::new(); +``` + +#### 2. When working with alternate or raw screen. + +When working with alternate and or raw screen you still have to provide a `Screen` instance since information of the alternate and raw screen is stored in it. When doing this, the actions of the module will be perfomed on the alternate screen. If you don't do this your actions will executed at the main screen. + +``` +use crossterm::cursor; +use crossterm::color; +use crossterm::input; +use crossterm::terminal; + +let screen = Screen::default(); + +if let Ok(alternate) = screen.enable_alternate_modes(false) { + let screen = alternate.screen; + let color = color::from_screen(&screen); + let cursor = cursor::from_screen(&screen); + let input = input::from_screen(&screen); + let terminal = terminal::from_screen(&screen); + let crossterm = Crossterm::from_screen(&screen); + + let terminal = Terminal::from_output(&screen.stdout); + let cursor = TerminalCursor::from_output(&screen.stdout); + let color = TerminalColor::from_output(&screen.stdout); + let input = TerminalInput::from_output(&screen.stdout); +} + +``` + ## Upgrade crossterm to 0.4.0 ***WARNING*** @@ -7,12 +72,12 @@ I really did not want to do this but it had to be done for some reasons. #### 1. You need to pass a reference to an `Screen` to the modules: `cursor(), color(), terminal()` +_**old**_ ``` use crossterm::terminal::terminal; use crossterm::cursor::cursor; use crossterm::style::color; -/// Old situation use crossterm::Context; let context: Rc = Context::new(); @@ -20,8 +85,9 @@ let context: Rc = Context::new(); let cursor = cursor(&context); let terminal = terminal(&context); let color = color(&context); - -/// new situation +``` +_**new**_ +``` use crossterm::Screen; let screen: Screen = Screen::default(); @@ -95,8 +161,8 @@ I really did not want to do this but it had to be done for some reasons. Check ` First thing that has changed is that you need to pass a reference to an `Rc` to the modules: `cursor(), color(), terminal()` +_**old**_ ``` - use crossterm::terminal::terminal; use crossterm::cursor::cursor; use crossterm::style::color; @@ -105,8 +171,9 @@ use crossterm::style::color; let cursor = cursor(); let terminal = terminal(); let color = color(); - -/// new situation +``` +_**new**_ +``` use crossterm::Context; let context: Rc = Context::new(); diff --git a/docs/mdbook/book.toml b/docs/mdbook/book.toml new file mode 100644 index 0000000..7da0aa3 --- /dev/null +++ b/docs/mdbook/book.toml @@ -0,0 +1,4 @@ +[book] +authors = ["T. Post"] +multilingual = false +src = "src" diff --git a/docs/mdbook/src/SUMMARY.md b/docs/mdbook/src/SUMMARY.md new file mode 100644 index 0000000..7255bd4 --- /dev/null +++ b/docs/mdbook/src/SUMMARY.md @@ -0,0 +1,8 @@ +# Summary +This book will cover styling and user input. It will also give a detailed description about working with alternate and raw screen. + +- [Styling Output](styling.md) + - [example](styling_example.md) +- [Async Input](input.md) +- [Alternate, Raw Screen](screen.md) + - [example](screen_example.md) diff --git a/docs/mdbook/src/input.md b/docs/mdbook/src/input.md new file mode 100644 index 0000000..7072332 --- /dev/null +++ b/docs/mdbook/src/input.md @@ -0,0 +1,77 @@ +Crossterm provides a way to work with the terminal input. We will not cover the basic usage but instead asynchronous reading of input. +Please checkout these [examples](https://github.com/TimonPost/crossterm/blob/master/examples/input/keyboard/input.rs) for reading a line or a character from the user. + +So what does 'reading async input' mean? +This means that you can catch user input without your program having to wait for it. +The user input will be read from another thread. +UNIX systems will get input from TTY and Windows will get input with '_getwch' and '_getwche'. + +This could be useful in a case where you want to perform some logic with a periodic check if the user entered some character. + +# Example +In the following example we will run some loop until the user has pressed 'x'. + +So lets start by setting up the basics. + +``` +use std::io::Read; +use crossterm::{input, Screen}; +use std::{thread, time::Duration}; + +fn main() { + println!("Press 'x' to quit."); + + /* next code here */ +} +``` + +Next we need to put the terminal into raw mode. We do this because whe don't want the user input to be printed to the terminal screen. +Once the user pressed 'x' we manually want to process it and stop the loop. + +```rust +// enable raw modes by passing in 'true' +let screen = Screen::new(true); + +// create a input from our screen. +let input = input::from_screen(&screen); + +/* next code here */ +``` + +Take note that whe need to use our 'Screen' to create an `TerminalInput` instance, check [this](screen.md#important-notice) out for more information why that is. + +Next we call `input.read_async()`. This will spin up a thread which will poll all input from the user. +This thread send all input via an 'mpsc' channel to the `AsyncReader` we got back from this function. + +By calling `bytes()` on the `AsyncReader` we get an iterator back over the characters (bytes). + +``` +let mut stdin = input.read_async().bytes(); + +/* next code here */ +``` + +Now we got an iterator back we can call `next()` on it to get the next pressed character (byte). +If an character is pressed we will get `Some(Ok(u8))` back which we compare to see if 'x' is pressed. +If the 'x' is pressed we break the loop. + +``` +loop { + let pressed_char: Option> = stdin.next(); + + // check if the pressed char is equal to 'c' + if let Some(Ok(b'x')) = pressed_char { + println!("The key: `x` was pressed and program is terminated."); + break; + } + + thread::sleep(Duration::from_millis(20)); +} +``` + +You should now have a fully functional program waiting for the user to press 'x'. +User input will be recorded on the background so that the main logic of your program can continue. +The code from this tutorial could be found [here](https://github.com/TimonPost/crossterm/blob/master/examples/input/keyboard/async_input.rs#L45). + +--------------------------------------------------------------------------------------------------------------------------------------------- +More examples could be found at this [link](https://github.com/TimonPost/crossterm/tree/master/examples/input/keyboard). \ No newline at end of file diff --git a/docs/mdbook/src/screen.md b/docs/mdbook/src/screen.md new file mode 100644 index 0000000..e15f77c --- /dev/null +++ b/docs/mdbook/src/screen.md @@ -0,0 +1,78 @@ +# Basic Usage of Screen +As you may have seen crossterm has a type called `Screen`. This type should be used when working with the alternate or raw screen. + +Before we continue, I'll explain what those concepts are. + +## Screen Buffer +A screen buffer is a two-dimensional array of characters and color data to be output in a console window. An terminal can have multiple of those screen buffers, and the active screen buffer is the one that is displayed on the screen. + +Crossterm allows you to switch between those buffers; the screen you are working in is called the 'main screen'. We call the other screen the 'alternate screen'. + +### Alternate Screen +Normally you are working on the main screen but an alternate screen is somewhat different from a normal screen. +For example, it has the exact dimensions of the terminal window, without any scrollback region. An example of this is vim when it is launched from bash. + +Vim uses the entirety of the screen to edit the file, then exits to bash leaving the original buffer unchanged. + +Crossterm provides the ability to switch to the alternate screen, make some changes, and then go back to the main screen. The main screen will still have its original data since we made all the edits on the alternate screen. + +## Raw screen +To understand the concept of a 'raw screen' let's look at the following points: + +**No line buffering.** +Normally the terminals use line buffering. This means that the input will be sent to the terminal line by line. With raw mode, the input will send one byte at a time. + +**Input** + + All input has to be written to the screen buffer manually by the programmer. + +**Characters** + +The characters are not processed by the terminal driver. Also, special character have no meaning. For example, backspace will not be interpreted as backspace but instead will be sent directly to the terminal. + +**Escape Characters** +Note that in raw mode `\n` `\r` will move the cursor to a new line but it will be at the same position as it was on the previous line. + +_example of what I mean_ + ``` + some text + some text + ``` + +To start at the beginning of the next line, use `\n\r`. + +# Important Notice +When we want to print some text to the alternate screen we can't just write on it using `print!(), println!(), or write!()`. +The same goes for coloring, cursor movement, input, and terminal actions. Crossterm provides a solution for that by introducing the `Screen` type. +Please checkout this [example](screen_example.md) for more information on how to use it. + + +``` +use crossterm::cursor; +use crossterm::color; +use crossterm::input; +use crossterm::terminal; + +let screen = Screen::default(); + +if let Ok(alternate) = screen.enable_alternate_modes(false) { + let screen = alternate.screen; + + let color = color::from_screen(&screen); + let cursor = cursor::from_screen(&screen); + let input = input::from_screen(&screen); + let terminal = terminal::from_screen(&screen); + let crossterm = Crossterm::from_screen(&screen); + + let terminal = Terminal::from_output(&screen.stdout); + let cursor = TerminalCursor::from_output(&screen.stdout); + let color = TerminalColor::from_output(&screen.stdout); + let input = TerminalInput::from_output(&screen.stdout); +} + +``` + +The above modules will now all be executed at the 'alternate screen'. + +--------------------------------------------------------------------------------------------------------------------------------------------- +Next up: [Examples](screen_example.md) \ No newline at end of file diff --git a/docs/mdbook/src/screen_example.md b/docs/mdbook/src/screen_example.md new file mode 100644 index 0000000..d967cc6 --- /dev/null +++ b/docs/mdbook/src/screen_example.md @@ -0,0 +1,155 @@ +Let's build some basic programs who are using the alternate and raw screen. + +# Raw Screen +_setup the basics_ +```rust +extern crate crossterm; + +use crossterm::Screen; + +fn main() { + /* next code here */ +} +``` + +We use the `Screen` type to enable raw mode for the terminal. +When creating a screen you have the option to pass it a boolean, this boolean specifies whether the screen will be in raw or normal mode. + +Let's play around a bit to see what raw screen does. + +```rust +// Create raw screen by passing in true. +let screen = Screen::new(true); + +println!("Some text"); +println!("Some text"); +``` +When you run this program you will directly see that the output is a little strange like: +``` +Some text + Some text +``` + +Another fun thing to do is reading the input. This should not work since the input is not recorded by the terminal when in raw mode. + +_Take note this will cause your terminal to freeze. +Since `read_line` expects some line input but it will never be recorded because of raw mode. +You should just restart the terminal when you are stuck_. +```rust +// Create raw screen by passing in true. +let screen = Screen::new(true); +let text = std::io::stdin().read_line(&mut string); +println!("{:?}", text); +``` + +Note that we spoke about the reason why this [previously](screen.md#raw-screen). +However, if you want to read input in raw mode you should checkout [Async Input](input.md). + +# Alternate Screen +_setup the basics_ +```rust +extern crate crossterm; + +use crossterm::Screen; +use std::{thread, time}; +use std::io::Write; + +fn main() { + /* next code here */ +} +``` + +As we spoke of [previously](screen.md#alternate-screen), with `Screen` we can manage alternate screen and raw screen. +Let's make some simple program who is switching from the main to alternate screen whereafter it will wait for 2 seconds. +When those seconds are over we will go back to the main screen. + +First off, create an instance of `Screen`. We can call `enable_alternate_modes()` on this screen, this will switch the screen buffer to the alternate buffer. +When we call this function we will get an `AlternateScreen` instance back which represents the alternate screen. +We should use this instance when we want to do any writing, styling, cursor, and terminal related things on the alternate screen ([Important Notice](screen.md##important-notice)). + +```rust +let screen = Screen::default(); + +if let Ok(mut alternate) = screen.enable_alternate_modes(false) { + /* next code here */ +} +``` + +Next, we use the instance we got back and write our message to it whereafter we wait for 2 seconds. +We wait 2 seconds to give us some time to see the alternate screen. +If the `AlternateScreen` goes out of scope it will automatically switch back to the main screen. + +```rust +write!(alternate.screen, "{}", "Welcome to the wait screen.\n Please wait a 2 seconds until we arrive back at the main screen."); +thread::sleep(time::Duration::from_secs(2)); +``` + + +By now you should be able to execute the program, you will see that directly you are being redirected to another screen with no scrollback region. +You will see this screen open whereafter it closes after 2 seconds. +When the program finishes you will notice you are on the main screen again with it's contents unmodified. + +# Perform Actions on Alternate Screen. +Now we have covered the basics of alternate screen let's make a program who styles some text on the 'raw' alternate screen. + +_setup the basics_ +```rust +extern crate crossterm; + +use crossterm::Screen; +use std::{thread, time}; +use std::io::Write; + +fn main() { + // switch to alternate screen and make it raw by passing in true. + if let Ok(mut alternate) = screen.enable_alternate_modes(true) { + /* next code here */ + } +} +``` +Some basics steps the following code will do: +1. Create [Crossterm]() type to manage the cursor and styling +2. Set the position of the cursor to x: 0 and y: 0 +3. Write the styled text, you can use the two described ways +4. Set the position of the cursor to x: 0 and y: 1 +5. Write other text and flush it to the screen +6. Sleep two seconds to see the results + +```rust +let alternate_screen = &mut alternate.screen; + +// could manage styling and cursor movement. +let crossterm = Crossterm::from_screen(alternate_screen); +let cursor = crossterm.cursor(); + +cursor.goto(0,0); + +// 1) print the 'styled object' by converting it into a type that is displayable for alternate screen. +println!("{}", crossterm.style("Welcome to the wait screen.") + .with(Color::Red) + .on(Color::Blue) + .into_displayable(alternate_screen) +); + +// 2) use the `paint` method to print it to the alternate screen. +crossterm.style("Welcome to the wait screen.") + .with(Color::Red) + .on(Color::Blue) + .paint(alternate_screen); + +cursor.goto(0,1); + +write!(alternate_screen, "{}", "Please wait a few seconds until we arrive back at the main screen."); +alternate_screen.flush(); + +thread::sleep(time::Duration::from_secs(2)); + +``` + +As you might have noticed, you need to to manually move the cursor, flush the buffer. This is because the terminal is in raw modes. +Also, by printing with `paint` or calling `into_displayable` you pass in a reference to the alternate screen. +Tip: Take a look at [how](screen.md#important-notice) you should use other modules crossterm provides on the alternate screen. + +--------------------------------------------------------------------------------------------------------------------------------------------- +More examples could be found at this [link](https://github.com/TimonPost/crossterm/tree/master/examples/terminal). + diff --git a/docs/mdbook/src/styling.md b/docs/mdbook/src/styling.md new file mode 100644 index 0000000..44ec3b5 --- /dev/null +++ b/docs/mdbook/src/styling.md @@ -0,0 +1,44 @@ +Crossterm provides options for you to style your font and terminal. Take for example coloring output and applying attributes. + +**Color support** +Windows systems with versions less than 10 only have support for 16 colors and don't have any support for attributes. + +## Colors +There are 16 base colors which available for almost all terminals even windows 7 and 8. + +| Light Variant | Dark Variant | +| :-------------| :------------- | +| Grey | Black | +| Red | DarkRed | +| Green | DarkGreen | +| Yellow | DarkYellow | +| Blue | DarkBlue | +| Magenta | DarkMagenta| +| Cyan | DarkCyan | +| White | DarkWhite | + +In addition to 16 colours, most unix terminals also support more colors. +For example, GNOME-terminals are supporting the [True color (24-bit)](https://en.wikipedia.org/wiki/Color_depth#True_color_(24-bit)) coloring scheme which allows you to use [RGB](https://nl.wikipedia.org/wiki/RGB-kleursysteem) for setting the terminal color. +All xterm terminals are at least supporting the [256 (Xterm, 8-bit)](https://jonasjacek.github.io/colors/) colors. + +## Attributes +UNIX terminals are supporting attributes on top of text. Crossterm allows you to add attributes to the text. +Not all attributes are widely supported for all terminals, keep that in mind when working with this. + +| Attribute | Note | +| :-------------: | :-------------: | +| Bold | | +| Underlined | | +| Dim | | +| SlowBlink | less than 150 per minute | +| CrosseOut | characters legible, but marked for deletion. | +| Italic | not widely supported; Sometimes treated as inverse | +| RapidBlink | not widely supported; MS-DOS ANSI.SYS; 150+ per minute | +| Reverse | not widely supported | +| Hidden | not widely supported | + + +Now we have covered the basics of styling lets go some [examples](styling_example.md). + +--------------------------------------------------------------------------------------------------------------------------------------------- +Next up: [Examples](styling_example.md) \ No newline at end of file diff --git a/docs/mdbook/src/styling_example.md b/docs/mdbook/src/styling_example.md new file mode 100644 index 0000000..3dcd5f2 --- /dev/null +++ b/docs/mdbook/src/styling_example.md @@ -0,0 +1,77 @@ +# Example + +_setup the basics_ +```rust +extern crate crossterm; + +use crossterm::style::{style, Color, Attribute}; + +fn main() { + /* your code here */ +} +``` + +Let's dive into styling with crossterm. The easiest way to do this is by making use of the `style()` function. + +```rust +let styled_object = style("This is some text converted into a styled object"); +``` + +The function `style()` takes in any type that implement `Display` +and returns a `StyledObject`. A `StyledObject` is just a wrapper crossterm uses to store the text and style together. + +The above code will not do any coloring magic yet. Lets play around with some colors to see it in working. + +## Coloring +```rust +let styled_object = style("'Red' text on 'White' background") + .with(Color::Red) + .on(Color::White); + +println!("{}", styled_object); +``` + +With the function `with()` you can decide the foreground color and with the function `on()` you can decide the background color of the text. +Because `StyledObject` you got from `style()` implements `Display` you are allowed to print it with: `print!, println!, write` etc. +When running the above code you are supposed to see colored text with foreground color 'red' and with the background color 'white'. + +_note: you don't have to color both backround an foreground, if not specified they remain as they are_. + +### RGB +Most UNIX terminals are supporting [True color(24-bit)](https://en.wikipedia.org/wiki/Color_depth#True_color_(24-bit)) coloring scheme. +You can set the color of the terminal by using `Color::RGB(r,g,b)`. + +``` +let styled_object = style("'Light green' text on 'Black' background") + .with(Color::Rgb {r: 0, g: 255, b: 128}) + .on(Color::Rgb {r: 0, g: 0, b: 0}); +``` +This will print some light green text on black background. + +### Custom ANSI color value +When working on unix you could also specify a custom ANSI value ranging up from 0 to 256. +See [256 (Xterm, 8-bit) colors](https://jonasjacek.github.io/colors/) for more information. + +``` +let styled_object = style("'Red' text on 'White' background") + .with(Color::AnsiValue(9)) + .on(Color::AnsiValue(15)); + + +println!("{}", styled_object); +``` + +## Attributes +When working in Linux you could also use attributes to style your font. For example you could cross your text with a line and make it bold. +See [above](styling.md#Attributes) for more information. + +``` +let styled_object = style("'Red' text on 'White' background") + .attr(Attribute::CrossedOut) + .attr(Attribute::Bold); + +println!("{}", styled_object); +``` + +--------------------------------------------------------------------------------------------------------------------------------------------- +More examples could be found at this [link](https://github.com/TimonPost/crossterm/blob/master/examples/color/mod.rs). \ No newline at end of file diff --git a/examples/examples.rs b/examples/examples.rs index c2c1b11..5c807fb 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -8,15 +8,18 @@ extern crate crossterm; // modules that could be test -mod color; -mod cursor; -mod input; -mod some_types; -mod terminal; +//mod color; +//mod cursor; +//mod input; +//mod some_types; +//mod terminal; -use crossterm::input::{TerminalInput, KeyEvent}; +use crossterm::style::{style, Color, Attribute}; fn main() { - println!("Press 'x' to quit..."); - TerminalInput::wait_until(KeyEvent::OnKeyPress(b'x')); + let styled_object = style("'Red' text on 'White' background") + .with(Color::AnsiValue(9)) + .on(Color::AnsiValue(15)); + + println!("{}", styled_object); } \ No newline at end of file diff --git a/src/common/functions.rs b/src/common/functions.rs index eadc060..58c5291 100644 --- a/src/common/functions.rs +++ b/src/common/functions.rs @@ -77,7 +77,7 @@ pub fn write(stdout: &Option<&Arc>, string: String) -> io::Resul pub fn write_str(stdout: &Option<&Arc>, string: &str) -> io::Result { match stdout { None => match io::stdout().flush() { - Ok(_) => Ok(string.len()), + Ok(_) => { write!(io::stdout(), "{}", string)?; Ok(string.len()) }, Err(e) => Err(e), }, Some(output) => output.write_str(string), diff --git a/src/modules/input/mod.rs b/src/modules/input/mod.rs index 01ab7c3..1d7eef9 100644 --- a/src/modules/input/mod.rs +++ b/src/modules/input/mod.rs @@ -13,7 +13,7 @@ use self::unix_input::UnixInput; #[cfg(target_os = "windows")] use self::windows_input::WindowsInput; -pub use self::input::{input, TerminalInput}; +pub use self::input::{input, from_screen, TerminalInput}; use std::io::{self, Error, ErrorKind, Read}; use std::sync::{mpsc, Arc}; diff --git a/src/modules/style/styledobject.rs b/src/modules/style/styledobject.rs index 03f4c0a..a9f73fb 100644 --- a/src/modules/style/styledobject.rs +++ b/src/modules/style/styledobject.rs @@ -210,13 +210,20 @@ impl Display for StyledObject { reset = true; } + #[cfg(unix)] + for attr in self.object_style.attrs.iter() { + write!(f, "{}", format!(csi!("{}m"), *attr as i16)); + reset = true; + } + fmt::Display::fmt(&self.content, f)?; std::io::stdout().flush().expect("Flush stdout failed"); if reset { +// write!(f, "\x1b[0m")?; colored_terminal.reset(); + std::io::stdout().flush().expect("Flush stdout failed"); } - Ok(()) } }