Merge pull request #10 from TimonPost/development

Development
This commit is contained in:
Timon 2018-05-20 14:52:33 +02:00 committed by GitHub
commit ad20c75616
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 2853 additions and 1549 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="Timon">
<words>
<w>swhitching</w>
</words>
</dictionary>
</component>

File diff suppressed because it is too large Load Diff

View File

@ -2,23 +2,24 @@
name = "crossterm"
version = "0.2.0"
authors = ["T Post <timonpost@hotmail.nl>"]
description = "An crossplarform terminal library for manipulating terminals."
description = "An crossplatform terminal library for manipulating terminals."
repository = "https://github.com/TimonPost/crossterm"
documentation = "https://atcentra.com/crossterm/"
documentation = "https://docs.rs/crossterm/0.2.0/crossterm/"
license = "MIT"
keywords = ["console", "color", "cursor", "terminal", "cli"]
exclude = ["target", "Cargo.lock"]
readme = "README.md"
[dependencies]
rand = "0.4.2"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
[target.'cfg(unix)'.dependencies]
libc = "0.2"
termios = "0.3.0"
[[bin]]
name = "example_bin"
path = "./examples/bin.rs"
[lib]
name = "crossterm"
path = "src/lib.rs"

View File

@ -8,6 +8,8 @@ Currently working on the alternatescreen and raw terminal features.
## Getting Started
This documentation is only for the newest version of crossterm. See the [Upgrade manual for more info](https://github.com/TimonPost/crossterm/blob/development/UPGRADE%20Manual)
Add the crossterm package to your `Cargo.toml` file.
```
@ -23,18 +25,18 @@ And use the crossterm modules withs you want to use.
extern crate crossterm;
// this module is used for styling the terminal
use self::crossterm::crossterm_style::*;
use self::crossterm::style::*;
// this module is used for cursor related actions
use self::crossterm::crossterm_cursor::*;
use self::crossterm::cursor::*;
// this mudule is used for terminal related actions
use self::crossterm::crossterm_terminal::*;
use self::crossterm::terminal::*;
```
## Links
Documentation version 0.1.0 can be found [here](https://docs.rs/crossterm/0.1.0/crossterm/)
Documentation for the code version 0.1 can be found [here](https://docs.rs/crossterm/0.1.0/crossterm/)
Documentation version 0.2.0 can be found [here](https://docs.rs/crossterm/0.2.0/crossterm/)
Documentation for the code version 0.2 can be found [here](https://docs.rs/crossterm/0.2.0/crossterm/)
The Cargo Page can be found [here](https://crates.io/search?q=crossterm)
@ -44,7 +46,7 @@ For detailed examples of all crossterm functionalities check the [examples](http
### Styled font
```rust
use crossterm::crossterm_style::{paint, Color};
use crossterm::style::{paint, Color};
// Crossterm provides method chaining so that you can style the font nicely.
// the `with()` methods sets the foreground color and the `on()` methods sets the background color
@ -79,9 +81,9 @@ For detailed examples of all crossterm functionalities check the [examples](http
### Cursor
```rust
use crossterm::crossterm_cursor::get;
use crossterm::cursor::cursor();
let mut cursor = get();
let mut cursor = cursor();
/// Moving the cursor
// Set the cursor to position X: 10, Y: 5 in the terminal
@ -119,9 +121,9 @@ For detailed examples of all crossterm functionalities check the [examples](http
### Terminal
```rust
use crossterm::crossterm_terminal::{get,ClearType};
use crossterm::terminal::{terminal,ClearType};
let mut terminal = get();
let mut terminal = terminal();
// Clear all lines in terminal;
terminal.clear(ClearType::All);
@ -172,11 +174,20 @@ For detailed examples of all crossterm functionalities check the [examples](http
- Storing the current cursor position and resetting to that stored cursor position later.
- Resizing the terminal.
### fixes in crossterm 0.2.1
- Default ANSI escape codes for windows machines, if windows does not support ANSI switsh back to WINAPI.
- method grammer mistake fixed [Issue 3](https://github.com/TimonPost/crossterm/issues/3)
- Some Refactorings in method names see [issue 4](https://github.com/TimonPost/crossterm/issues/4)
- Removed bin refrence from crate [Issue 6](https://github.com/TimonPost/crossterm/issues/6)
- The terminal state will be set to its original state when process ends [issue7](https://github.com/TimonPost/crossterm/issues/7).
- Get position unix fixed [issue 8](https://github.com/TimonPost/crossterm/issues/8)
## TODO Features crossterm 0.3
- Raw state implementation [Issue 5](https://github.com/TimonPost/crossterm/issues/5).
- The terminal state will be set to its original state when process ends [issue7](https://github.com/TimonPost/crossterm/issues/7).
- Default ANSI escape codes for windows machines, if windows does not support ANSI switsh back to WINAPI.
- Some Refactorings in method names see [issue 4](https://github.com/TimonPost/crossterm/issues/4)
- Alternate screen implementation.
- Tests
## Tested terminals

24
UPGRADE Manual Normal file
View File

@ -0,0 +1,24 @@
Upgrade crossterm 0.2 to 0.2.1
Namespaces:
I have changed the namespaces. I found the namsespaces to long so I have shoted them like the followin:
Old: crossterm::crosster_style
New: crossterm::style
Old: crossterm::crosster_terminal
New: crossterm::terminal
Old: crossterm::crosster_cursor
New: crossterm::cursor
Method names that changed [Issue 4](https://github.com/TimonPost/crossterm/issues/4):
Old: crossterm::crossterm_cursor::get();
New: use crossterm::cursor::cursor();
Old: crossterm::crossterm_terminal::get();
New: use crossterm::terminal::terminal();
Old: crossterm::crossterm_style::color::get();
New: use crossterm::style::color::color();

View File

@ -11,19 +11,9 @@
//!
//! - Run program with: `cargo run`
// Import crossterm crate.
extern crate crossterm;
// Add the usings for the crossterms modules to play with crossterm
use self::crossterm::crossterm_style;
use self::crossterm::crossterm_cursor;
use self::crossterm::crossterm_terminal;
// Import the example modules.
pub mod color;
pub mod cursor;
pub mod terminal;
use crossterm::cursor;
fn main() {
}

View File

@ -4,7 +4,7 @@
extern crate crossterm;
use self::crossterm::crossterm_style::{paint, Color};
use self::crossterm::style::{paint, Color};
/// print some red font | demonstration.
pub fn paint_foreground()
@ -103,7 +103,7 @@ pub fn print_all_background_colors()
println!("RGB (10,10,10): \t {}", paint(" ").on(Color::AnsiValue(50)));
}
/// Print font with all available attributes. Note that this can only be used at unix systems and that some are not supported widely.
/// Print font with all available attributes. Note that this can only be used at unix systems and that some are not supported widely | demonstration..
#[cfg(unix)]
pub fn print_font_with_attributes()
{
@ -119,15 +119,14 @@ pub fn print_font_with_attributes()
println!("{}", paint("Crossed out font").crossed_out());
}
/// Print all supported rgb colors
#[cfg(unix)]#[cfg(unix)]
/// Print all supported rgb colors | demonstration.
#[cfg(unix)]
pub fn print_supported_colors()
{
let count = crossterm::crossterm_style::get().get_available_color_count().unwrap();
let count = crossterm::style::color().get_available_color_count().unwrap();
for i in 0..count
{
println!("{}", paint(format!("Color: {}",i)).with(Color::AnsiValue(i as u8)));
}
}

View File

@ -4,13 +4,13 @@
extern crate crossterm;
use self::crossterm::crossterm_cursor::{get, TerminalCursor};
use self::crossterm::cursor::{cursor, TerminalCursor};
/// Set the cursor to position X: 10, Y: 5 in the terminal.
pub fn goto()
{
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Set the cursor to position X: 10, Y: 5 in the terminal
cursor.goto(10,5);
}
@ -19,7 +19,7 @@ pub fn goto()
pub fn move_up()
{
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Move the cursor to position 3 times to the up in the terminal
cursor.move_up(3);
}
@ -28,7 +28,7 @@ pub fn move_up()
pub fn move_right()
{
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Move the cursor to position 3 times to the right in the terminal
cursor.move_right(3);
}
@ -37,7 +37,7 @@ pub fn move_right()
pub fn move_down()
{
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Move the cursor to position 3 times to the down in the terminal
cursor.move_down(3);
}
@ -46,7 +46,7 @@ pub fn move_down()
pub fn move_left()
{
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Move the cursor to position 3 times to the left in the terminal
cursor.move_left(3);
}
@ -57,7 +57,7 @@ pub fn print()
// To print an some displayable content on an certain position.
// Get the cursor
let mut cursor = get();
let mut cursor = cursor();
// Set the cursor to position X: 10, Y: 5 in the terminal
cursor.goto(10,5);
// Print the @ symbol at position X: 10, Y: 5 in the terminal
@ -67,32 +67,32 @@ pub fn print()
use std::io::Write;
std::io::stdout().flush();
/* Because the above method is a little to mutch code,
/* Because the above method is a little to much code,
you can use the `print()` method for printing an value at an certain position in the terminal.
Crossterm provides method chaining so that the above points can be inlined.
*/
get().goto(10,5).print("@");
cursor.goto(10,5).print("@");
}
/// Save and reset cursor position.
/// Save and reset cursor position | demonstration..
pub fn safe_and_reset_position()
{
let mut cursor = get();
let mut cursor = cursor();
// Goto X: 5 Y: 5
cursor.goto(5,5);
// Safe cursor position: X: 5 Y: 5
cursor.safe_position();
cursor.save_position();
// Goto X: 5 Y: 20
cursor.goto(5,20);
// Print at X: 5 Y: 20.
print!("Yea!");
println!("Yea!");
// Reset back to X: 5 Y: 5.
cursor.reset_position();
// Print Back at X: 5 Y: 5.
print!("Back");
println!("Back");
println!()
}

View File

@ -0,0 +1,73 @@
// alternate screen is not working correctly currently
//extern crate crossterm;
//
//use crossterm::terminal::screen::{AlternateScreen, ToAlternateScreen, ToMainScreen};
//use crossterm::cursor::cursor;
//use crossterm::terminal::{self, ClearType};
//
//use std::io::{Write, stdout};
//use std::{time, thread};
//
//fn print_wait_screen(screen: &mut Write)
//{
// terminal::terminal().clear(ClearType::All);
// write!(screen,
// "Welcome to the wait screen.\n\
// Please wait a few seconds until we arrive back at the main screen.\n\
// Seconds to Go: "
// );
//
// let mut counter = 5;
// // get cursor instance
// let mut cursor = cursor();
//
// // loop until the counter hits 0
// loop
// {
// // 1 second delay
// thread::sleep(time::Duration::from_secs(1));
// // decrement counter
// counter -= 1;
//
// // print the current counter at the line of `Seconds to Go: {counter}`
// cursor.goto(15,2).print(counter);
//
// if counter <= 0
// {
// break;
// }
// }
//}
//
//pub fn with_alternate_screen_instance()
//{
// // create scope. If this scope ends the screen will be switched back to mainscreen.
// // becouse `AlternateScreen` switches back to main screen when switching back.
// {
// // create new alternate screen instance and switch to the alternate screen.
// let mut screen = AlternateScreen::from(stdout());
//
// // Print the wait screen.
// print_wait_screen(&mut screen);
// }
//
// println!("Whe are back at the main screen");
//}
//
//pub fn manually_switch_to_alternate_screen()
//{
// // You can switch to alternate screen manually but if you forget to switch back your terminal may cause some undefined behavior.
//
// let mut screen = stdout();
//
// // switch to alternate screeen
// write!(screen, "{}", ToAlternateScreen);
// // load wait screen
// print_wait_screen(&mut screen);
// // switch back
// write!(screen,"{}", ToMainScreen);
// println!("Whe are back at the main screen");
//
//}

View File

@ -1,135 +1,5 @@
//!
//! Terminal Examples
//!
mod alternate_screen;
mod raw_mode;
extern crate crossterm;
use crossterm::crossterm_terminal::{get, Terminal, ClearType};
use crossterm::crossterm_cursor;
fn print_test_data()
{
for i in 0..100 {
println!("abcdefghijTest data to test terminal: {}",i);
}
}
/// Clear all lines in terminal | demonstration
pub fn clear_all_lines()
{
// Get terminal
let mut terminal = get();
print_test_data();
// Clear all lines in terminal;
terminal.clear(ClearType::All);
}
/// Clear all lines from cursor position X:4, Y:4 down | demonstration
pub fn clear_from_cursor_down()
{
// Get terminal
let mut terminal = get();
print_test_data();
// Set terminal cursor position (see example for more info).
crossterm_cursor::get().goto(4,8);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorDown);
}
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_from_cursor_up()
{
// Get terminal
let mut terminal = get();
print_test_data();
// Set terminal cursor position (see example for more info).
crossterm_cursor::get().goto(4,8);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorUp);
}
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_current_line()
{
// Get terminal
let mut terminal = get();
print_test_data();
// Set terminal cursor position (see example for more info).
crossterm_cursor::get().goto(4,4);
// Clear current line cells.
terminal.clear(ClearType::CurrentLine);
}
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_until_new_line()
{
// Get terminal
let mut terminal = get();
print_test_data();
// Set terminal cursor position (see example for more info).
crossterm_cursor::get().goto(4,7);
// Clear all the cells until next line.
terminal.clear(ClearType::UntilNewLine);
}
pub fn print_terminal_size()
{
// Get terminal
let mut terminal = get();
// Get terminal size
let terminal_size = terminal.terminal_size().unwrap();
// Print results
print!("X: {}, y: {}", terminal_size.0, terminal_size.1);
}
/// Set the terminal size to width 10, height: 10.
pub fn set_terminal_size()
{
let mut terminal = get();
terminal.set_size(10,10);
}
// scroll down 10 lines
pub fn scroll_down()
{
print_test_data();
// Get terminal
let mut terminal = get();
// Scroll down 10 lines.
terminal.scroll_down(10);
}
// scroll down 10 lines
pub fn scroll_up()
{
print_test_data();
// Get terminal
let mut terminal = get();
// Scroll up 10 lines.
terminal.scroll_up(10);
}
// Resize the terminal to X: 10, Y: 10
pub fn resize_terminal()
{
// Get terminal
let mut terminal = get();
// Get terminal size
terminal.set_size(1,1);
}
pub mod terminal;

View File

@ -0,0 +1 @@
// raw screen is not working correctly currently

View File

@ -0,0 +1,136 @@
//!
//! Terminal Examples
//!
extern crate crossterm;
use crossterm::terminal::{ Terminal, ClearType, terminal};
use crossterm::cursor;
fn print_test_data()
{
for i in 0..100 {
println!("Test data to test terminal: {}",i);
}
}
/// Clear all lines in terminal | demonstration
pub fn clear_all_lines()
{
// Get terminal
let mut terminal = terminal();
print_test_data();
// Clear all lines in terminal;
terminal.clear(ClearType::All);
}
/// Clear all lines from cursor position X:4, Y:4 down | demonstration
pub fn clear_from_cursor_down()
{
// Get terminal
let mut terminal = terminal();
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor().goto(4,8);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorDown);
}
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_from_cursor_up()
{
// Get terminal
let mut terminal = terminal();
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor().goto(4,4);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorUp);
}
/// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_current_line()
{
// Get terminal
let mut terminal = terminal();
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor().goto(4,4);
// Clear current line cells.
terminal.clear(ClearType::CurrentLine);
}
/// Clear all lines from cursor position X:4, Y:7 up | demonstration
pub fn clear_until_new_line()
{
// Get terminal
let mut terminal = terminal();
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor().goto(4,20);
// Clear all the cells until next line.
terminal.clear(ClearType::UntilNewLine);
}
/// Print the the current terminal size | demonstration.
pub fn print_terminal_size()
{
// Get terminal
let mut terminal = terminal();
// Get terminal size
let terminal_size = terminal.terminal_size();
// Print results
print!("X: {}, y: {}", terminal_size.0, terminal_size.1);
}
/// Set the terminal size to width 10, height: 10 | demonstration.
pub fn set_terminal_size()
{
let mut terminal = terminal();
terminal.set_size(10,10);
}
/// Scroll down 10 lines | demonstration.
pub fn scroll_down()
{
print_test_data();
// Get terminal
let mut terminal = terminal();
// Scroll down 10 lines.
terminal.scroll_down(10);
}
/// Scroll down 10 lines | demonstration.
pub fn scroll_up()
{
print_test_data();
// Get terminal
let mut terminal = terminal();
// Scroll up 10 lines.
terminal.scroll_up(10);
}
/// Resize the terminal to X: 10, Y: 10 | demonstration.
pub fn resize_terminal()
{
// Get terminal
let mut terminal = terminal();
// Get terminal size
terminal.set_size(10,10);
}

View File

@ -1,27 +0,0 @@
//! This trait defines the actions that can be preformed with the termial cursor.
//! This trait can be inplemented so that an concrete inplementation of the ITerminalCursor can forfill
//! the wishes to work on an specific platform.
//!
//! ## For example:
//!
//! This trait is inplemented for winapi (Windows specific) and ansi (Unix specific),
//! so that the cursor related actions can be preformed on both unix and windows systems.
pub trait ITerminalCursor {
/// Goto some location (x,y) in the terminal.
fn goto(&self, x: u16, y: u16);
/// Get the location (x,y) of the current curor in the terminal
fn pos(&self) -> (i16, i16);
/// Move cursor n times up
fn move_up(&self, count: u16);
/// Move the cursor `n` times to the right.
fn move_right(&self, count: u16);
/// Move the cursor `n` times down.
fn move_down(&self, count: u16);
/// Move the cursor `n` times left.
fn move_left(&self, count: u16);
/// Save cursor position for recall later. Note that this position is stored program based not per instance of the cursor struct.
fn safe_position(&mut self);
/// Return to saved cursor position
fn reset_position(&self);
}

View File

@ -1,15 +0,0 @@
mod base_cursor;
mod cursor;
#[cfg(unix)]
mod ansi_cursor;
#[cfg(windows)]
mod winapi_cursor;
#[cfg(unix)]
use self::ansi_cursor::AnsiCursor;
#[cfg(windows)]
use self::winapi_cursor::WinApiCursor;
pub use self::cursor::{ get, TerminalCursor };

View File

@ -1,56 +0,0 @@
use Construct;
use kernel::windows_kernel::cursor;
use super::base_cursor::ITerminalCursor;
/// This struct is an windows implementation for cursor related actions.
pub struct WinApiCursor;
impl Construct for WinApiCursor {
fn new() -> Box<WinApiCursor> {
Box::from(WinApiCursor { })
}
}
impl ITerminalCursor for WinApiCursor {
fn goto(&self, x: u16, y: u16) {
cursor::set(x as i16, y as i16);
}
fn pos(&self) -> (i16, i16) {
cursor::pos()
}
fn move_up(&self, count: u16) {
let (xpos,ypos) = cursor::pos();
cursor::set(xpos, ypos - count as i16);
}
fn move_right(&self, count: u16) {
let (xpos,ypos) = cursor::pos();
cursor::set(xpos + count as i16, ypos);
}
fn move_down(&self, count: u16) {
let (xpos,ypos) = cursor::pos();
cursor::set(xpos, ypos + count as i16);
}
fn move_left(&self, count: u16) {
let (xpos,ypos) = cursor::pos();
cursor::set(xpos - count as i16, ypos);
}
fn safe_position(&mut self)
{
cursor::save_cursor_pos();
}
fn reset_position(&self)
{
cursor::reset_to_saved_position();
}
}

View File

@ -1,12 +0,0 @@
pub mod base_color;
pub mod color;
#[cfg(unix)]
mod ansi_color;
#[cfg(windows)]
mod winapi_color;
#[cfg(unix)]
use self::ansi_color::ANSIColor;
#[cfg(windows)]
use self::winapi_color::WinApiColor;

View File

@ -1,38 +0,0 @@
use Construct;
use super::super::{ColorType, Color};
use super::base_color::ITerminalColor;
use kernel::windows_kernel;
/// This struct is an windows implementation for color related actions.
#[derive(Debug)]
pub struct WinApiColor {
original_console_color: u16,
}
impl Construct for WinApiColor {
fn new() -> Box<WinApiColor> {
Box::from(WinApiColor {
original_console_color: windows_kernel::kernel::get_original_console_color(),
})
}
}
impl ITerminalColor for WinApiColor {
fn set_fg(&self, fg_color: Color) {
let color_value = &self.color_value(fg_color, ColorType::Foreground);
windows_kernel::color::set_fg_color(color_value.parse().unwrap());
}
fn set_bg(&self, bg_color: Color) {
let color_value = &self.color_value(bg_color, ColorType::Background);
windows_kernel::color::set_bg_color(color_value.parse().unwrap());
}
fn reset(&self) {
windows_kernel::color::reset(self.original_console_color);
}
fn color_value(&self, color: Color, color_type: ColorType) -> String {
windows_kernel::color::winapi_color_val(color, color_type).to_string()
}
}

View File

@ -1,2 +0,0 @@
pub mod objectstyle;
pub mod styledobject;

View File

@ -1,21 +0,0 @@
/// Enum that can be used for the kind of clearing that can be done in the terminal.
pub enum ClearType {
All,
FromCursorDown,
FromCursorUp,
CurrentLine,
UntilNewLine,
}
pub trait ITerminal {
/// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y)
fn terminal_size(&self) -> Option<(u16, u16)>;
/// Scroll `n` lines up in the current terminal.
fn scroll_up(&self, count: i16);
/// Scroll `n` lines down in the current terminal.
fn scroll_down(&self, count: i16);
/// Resize terminal to the given width and height.
fn set_size(&self,width: i16, height: i16);
}

View File

@ -1,15 +0,0 @@
mod base_terminal;
mod terminal;
#[cfg(unix)]
mod ansi_terminal;
#[cfg(windows)]
mod winapi_terminal;
#[cfg(unix)]
use self::ansi_terminal::UnixTerminal;
#[cfg(windows)]
use self::winapi_terminal::WinApiTerminal;
pub use self::base_terminal::ClearType;
pub use self::terminal::{ get, Terminal };

View File

@ -1,40 +0,0 @@
use Construct;
use super::base_terminal::{ClearType, ITerminal};
use kernel::windows_kernel::terminal;
/// This struct is an windows implementation for terminal related actions.
pub struct WinApiTerminal;
impl Construct for WinApiTerminal {
fn new() -> Box<WinApiTerminal> {
Box::from(WinApiTerminal {})
}
}
impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType) {
match clear_type
{
ClearType::All => terminal::clear_entire_screen(),
ClearType::FromCursorDown => terminal::clear_after_cursor(),
ClearType::FromCursorUp => terminal::clear_before_cursor(),
ClearType::CurrentLine => terminal::clear_current_line(),
ClearType::UntilNewLine => terminal::clear_until_line(),
};
}
fn terminal_size(&self) -> Option<(u16, u16)> {
terminal::terminal_size()
}
fn scroll_up(&self, count: i16) {
// yet to be inplemented
}
fn scroll_down(&self, count: i16) {
terminal::scroll_down(count as i16);
}
fn set_size(&self, width: i16, height: i16) { terminal::resize_terminal(width,height); }
}

View File

@ -1,8 +1,11 @@
use std::io;
use std::io::Write;
//! This is an ANSI specific implementation for cursor related action.
//! This module is used for windows 10 terminals and unix terminals by default.
use Construct;
use super::base_cursor::ITerminalCursor;
use shared::functions;
use super::ITerminalCursor;
use std::io::{ self, Write };
/// This struct is an ansi implementation for cursor related actions.
pub struct AnsiCursor;
@ -20,8 +23,8 @@ impl ITerminalCursor for AnsiCursor {
write!(&mut some_writer, csi!("{};{}H"), y + 1, x +1);
}
fn pos(&self) -> (i16, i16) {
(0, 0)
fn pos(&self) -> (u16, u16) {
functions::get_cursor_position()
}
fn move_up(&self, count: u16) {
@ -45,7 +48,7 @@ impl ITerminalCursor for AnsiCursor {
write!(&mut some_writer, csi!("{}D"), count);
}
fn safe_position(&mut self)
fn save_position(&mut self)
{
let mut some_writer = io::stdout();
write!(&mut some_writer, csi!("s"));

View File

@ -1,27 +1,31 @@
//! With this module you can perform actions that are cursor related.
//! Like changing and displaying the position of the cursor in terminal.
//!
//! Note that positions of the cursor are 0 -based witch means that the coordinates starts counting from 0
use super::*;
use shared::functions;
use {Construct, Context};
use std::fmt::Display;
use Construct;
use super::base_cursor::ITerminalCursor;
#[cfg(unix)]
use super::AnsiCursor;
#[cfg(windows)]
use super::WinApiCursor;
use std::ops::Drop;
/// Struct that stores an specific platform implementation for cursor related actions.
pub struct TerminalCursor {
terminal_cursor: Option<Box<ITerminalCursor>>,
}
impl TerminalCursor {
/// Instantiates an platform specific cursor implementation whereon cursor related actions can be performed.
pub fn init(&mut self) {
if let None = self.terminal_cursor {
self.terminal_cursor = get_cursor_options();
}
impl TerminalCursor
{
/// Create new cursor instance whereon cursor related actions can be performed.
pub fn new() -> TerminalCursor {
#[cfg(target_os = "windows")]
let cursor = functions::get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new());
#[cfg(not(target_os = "windows"))]
let cursor = Some(AnsiCursor::new() as Box<ITerminalCursor>);
TerminalCursor { terminal_cursor: cursor }
}
/// Goto some position (x,y) in the terminal.
@ -32,13 +36,12 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// crossterm_cursor::get().goto(10,10);
/// cursor::cursor().goto(10,10);
///
/// ```
pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor {
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.goto(x, y);
}
@ -53,14 +56,13 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// let pos = crossterm_cursor::get().pos();
/// let pos = cursor::cursor().pos();
/// println!("{:?}", pos);
///
/// ```
pub fn pos(&mut self) -> (i16, i16) {
&self.init();
pub fn pos(&mut self) -> (u16, u16) {
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.pos()
} else {
@ -76,17 +78,16 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// // Move 1 time up
/// crossterm_cursor::get().move_up(1);
/// cursor::cursor().move_up(1);
///
/// // Move 2 times up
/// crossterm_cursor::get().move_up(2);
/// cursor::cursor().move_up(2);
///
/// ```
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor {
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.move_up(count);
}
@ -101,18 +102,17 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
///
/// // move 1 time right
/// crossterm_cursor::get().move_right(1);
/// cursor::cursor().move_right(1);
///
/// // move 2 times right
/// crossterm_cursor::get().move_right(2);
/// cursor::cursor().move_right(2);
///
/// ```
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor {
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.move_right(count);
}
@ -127,17 +127,16 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// // move 1 time down
/// crossterm_cursor::get().move_down(1);
/// cursor::cursor().move_down(1);
///
/// // move 2 times down
/// crossterm_cursor::get().move_down(2);
/// cursor::cursor().move_down(2);
///
/// ```
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor {
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.move_down(count);
}
@ -152,17 +151,16 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// // move 1 time left
/// crossterm_cursor::get().move_left(1);
/// cursor::cursor().move_left(1);
///
/// // move 2 time left
/// crossterm_cursor::get().move_left(2);
/// cursor::cursor().move_left(2);
///
/// ```
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor {
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.move_left(count);
}
@ -186,17 +184,17 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
/// use std;
/// use std::io::Write;
///
/// // of course we can just do this.
/// crossterm_cursor::get().goto(10,10);
/// cursor::cursor().goto(10,10);
/// print!("@");
/// std::io::stdout().flush();
///
/// // but now we can chain the methods so it looks cleaner and it automatically flushes the buffer.
/// crossterm_cursor::get()
/// cursor::cursor()
/// .goto(10,10)
/// .print("@");
///
@ -220,22 +218,21 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor::get;
/// use self::crossterm::cursor;
///
/// get().safe_position();
/// cursor::cursor().safe_position();
///
/// ```
pub fn safe_position(&mut self)
pub fn save_position(&mut self)
{
&self.init();
if let Some(ref mut terminal_cursor) = self.terminal_cursor {
terminal_cursor.safe_position();
terminal_cursor.save_position();
}
}
/// Return to saved cursor position
///
/// Note that this method reset to the position set by `safe_position()` and that this position is stored program based not per instance of the `Cursor` struct.
/// Note that this method reset to the position set by `save_position()` and that this position is stored program based not per instance of the `Cursor` struct.
///
/// #Example
///
@ -243,29 +240,19 @@ impl TerminalCursor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor::get;
/// use self::crossterm::cursor::cursor;
///
/// get().reset_position();
/// cursor().reset_position();
///
/// ```
pub fn reset_position(&mut self)
{
&self.init();
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.reset_position();
}
}
}
/// Get the concrete ITerminalCursor implementation based on the current operating system.
fn get_cursor_options() -> Option<Box<ITerminalCursor>> {
#[cfg(unix)]
return Some(AnsiCursor::new());
#[cfg(windows)]
return Some(WinApiCursor::new());
}
/// Get an TerminalCursor implementation whereon cursor related actions can be performed.
///
/// Check `/examples/cursor` in the libary for more spesific examples.
@ -276,18 +263,16 @@ fn get_cursor_options() -> Option<Box<ITerminalCursor>> {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_cursor;
/// use self::crossterm::cursor;
///
/// // Get cursor and goto pos X: 5, Y: 10
/// let mut cursor = crossterm_cursor::get();
/// let mut cursor = cursor::cursor();
/// cursor.goto(5,10);
///
/// //Or you can do it in one line.
/// crossterm_cursor::get().goto(5,10);
/// cursor::cursor().goto(5,10);
///
/// ```
pub fn get() -> Box<TerminalCursor> {
Box::from(TerminalCursor {
terminal_cursor: get_cursor_options(),
})
pub fn cursor() -> Box<TerminalCursor> {
Box::from(TerminalCursor::new())
}

47
src/cursor/mod.rs Normal file
View File

@ -0,0 +1,47 @@
//! This trait defines the actions that can be preformed with the terminal cursor.
//! This trait can be implemented so that an concrete implementation of the ITerminalCursor can for fill
//! the wishes to work on an specific platform.
//!
//! ## For example:
//!
//! This trait is implemented to work with WINAPI (Windows specific) and ANSI (Unix specific),
//! so that the cursor related actions can be preformed on both unix and windows systems.
//!
mod cursor;
#[cfg(target_os = "windows")]
mod winapi_cursor;
mod ansi_cursor;
#[cfg(target_os = "windows")]
use self::winapi_cursor::WinApiCursor;
use self::ansi_cursor::AnsiCursor;
pub use self::cursor::{ cursor, TerminalCursor };
///! This trait defines the actions that can be preformed with the terminal cursor.
///! This trait can be implemented so that an concrete implementation of the ITerminalCursor can forfill
///! the wishes to work on an specific platform.
///!
///! ## For example:
///!
///! This trait is implemented for `WINAPI` (Windows specific) and `ANSI` (Unix specific),
///! so that cursor related actions can be preformed on both unix and windows systems.
pub trait ITerminalCursor {
/// Goto some location (x,y) in the terminal.
fn goto(&self, x: u16, y: u16);
/// Get the location (x,y) of the current curor in the terminal
fn pos(&self) -> (u16, u16);
/// Move cursor n times up
fn move_up(&self, count: u16);
/// Move the cursor `n` times to the right.
fn move_right(&self, count: u16);
/// Move the cursor `n` times down.
fn move_down(&self, count: u16);
/// Move the cursor `n` times left.
fn move_left(&self, count: u16);
/// Save cursor position for recall later. Note that this position is stored program based not per instance of the cursor struct.
fn save_position(&mut self);
/// Return to saved cursor position
fn reset_position(&self);
}

View File

@ -0,0 +1,59 @@
//! This is an WINAPI specific implementation for cursor related action.
//! This module is used for windows terminals that do not support ANSI escape codes.
use Construct;
use super::ITerminalCursor;
use kernel::windows_kernel::{kernel, cursor};
/// This struct is an windows implementation for cursor related actions.
pub struct WinApiCursor;
impl Construct for WinApiCursor {
fn new() -> Box<WinApiCursor> {
Box::from(WinApiCursor { })
}
}
impl ITerminalCursor for WinApiCursor {
fn goto(&self, x: u16, y: u16) {
kernel::set_console_cursor_position(x as i16, y as i16);
}
fn pos(&self) -> (u16, u16) {
cursor::pos()
}
fn move_up(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos, ypos - count);
}
fn move_right(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos + count, ypos);
}
fn move_down(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos, ypos + count);
}
fn move_left(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos - count, ypos);
}
fn save_position(&mut self)
{
cursor::save_cursor_pos();
}
fn reset_position(&self)
{
cursor::reset_to_saved_position();
}
}

View File

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

View File

@ -1,34 +0,0 @@
use libc;
use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort};
use self::libc::ioctl;
/// A representation of the size of the current terminal
#[repr(C)]
#[derive(Debug)]
pub struct UnixSize {
/// number of rows
pub rows: c_ushort,
/// number of columns
pub cols: c_ushort,
x: c_ushort,
y: c_ushort,
}
/// Gets the current terminal size
pub fn terminal_size() -> Option<(u16,u16)> {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize {
rows: 0,
cols: 0,
x: 0,
y: 0,
};
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &us) };
if r == 0 {
// because crossterm works starts counting at 0 and unix terminal starts at cell 1 you have subtract one to get 0-based results.
Some((us.cols -1, us.rows -1))
} else {
None
}
}

View File

@ -1,4 +1,6 @@
//! All platform specific (unsafe) code will be handled in this module.
#[cfg(unix)]
pub mod linux_kernel;
pub mod unix_kernel;
#[cfg(windows)]
pub mod windows_kernel;

View File

@ -0,0 +1,4 @@
//! This module contains all the specific `unix` code.
pub mod terminal;

View File

@ -0,0 +1,144 @@
//! This module contains all `unix` specific terminal related logic.
use { libc, Context };
use termios::Termios;
pub use self::libc::{termios};
use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort, ioctl, c_int};
use state::commands::{ NoncanonicalModeCommand, IContextCommand} ;
use std::{ io, mem };
use std::io::Error;
/// A representation of the size of the current terminal.
#[repr(C)]
#[derive(Debug)]
pub struct UnixSize {
/// number of rows
pub rows: c_ushort,
/// number of columns
pub cols: c_ushort,
x: c_ushort,
y: c_ushort,
}
/// Get the current terminal size.
pub fn terminal_size() -> (u16,u16) {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize {
rows: 0,
cols: 0,
x: 0,
y: 0,
};
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &us) };
if r == 0 {
// because crossterm works starts counting at 0 and unix terminal starts at cell 1 you have subtract one to get 0-based results.
(us.cols -1, us.rows -1)
} else {
(0,0)
}
}
/// Get the current cursor position.
pub fn pos() -> (u16,u16)
{
use std::io::Error;
use std::io::{ Write,Read };
let mut context = Context::new();
{
let mut command = NoncanonicalModeCommand::new(&mut context);
command.0.execute();
// This code is original written by term_cursor credits to them.
let mut stdout = io::stdout();
// Write command
stdout.write(b"\x1B[6n");
stdout.flush();
// Read back result
let mut buf = [0u8; 2];
// Expect `ESC[`
io::stdin().read_exact(&mut buf);
if buf[0] != 0x1B || buf[1] as char != '[' {
return (0, 0);
}
// Read rows and cols through a ad-hoc integer parsing function
let read_num = || -> (i32, char) {
let mut num = 0;
let mut c;
loop {
let mut buf = [0u8; 1];
io::stdin().read_exact(&mut buf);
c = buf[0] as char;
if let Some(d) = c.to_digit(10) {
num = if num == 0 { 0 } else { num * 10 };
num += d as i32;
} else {
break;
}
}
(num, c)
};
// Read rows and expect `;`
let (rows, c) = read_num();
if c != ';' {
return (0, 0);
}
// Read cols
let (cols, c) = read_num();
// Expect `R`
let res = if c == 'R' { (cols as u16, rows as u16) } else { return (0, 0) };
res
}
}
/// Set the terminal mode to the given mode.
pub fn set_terminal_mode(termios: &Termios) -> io::Result<()>
{
extern "C" {
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
}
is_true(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
}
/// Transform the given mode into an raw mode (non-canonical) mode.
pub fn make_raw(termios: &mut Termios) {
extern "C" {
pub fn cfmakeraw(termptr: *mut Termios);
}
unsafe { cfmakeraw(termios) }
}
/// Get the current terminal mode.
pub fn get_terminal_mode() -> io::Result<Termios>
{
extern "C" {
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
}
unsafe {
let mut termios = mem::zeroed();
is_true(tcgetattr(0, &mut termios))?;
Ok(termios)
}
}
/// Is the return value true?
fn is_true(value: i32) -> Result<(), Error>
{
match value
{
-1 => Err(io::Error::last_os_error()),
0 => Ok(()),
_ => Err(io::Error::last_os_error()),
}
}

View File

@ -1,7 +1,70 @@
use super::kernel;
//! This module handles the enabling `ANSI escape codes` for windows terminals.
/// Enables ansi for windows terminals.
pub fn enable_ansi_support() {
let enable_ansi_code: u32 = 7;
kernel::set_console_mode(enable_ansi_code);
use Context;
use state::commands::ICommand;
static mut HAS_BEEN_TRYED_TO_ENABLE: bool = false;
static mut IS_ANSI_ON_WINDOWS_ENABLED: Option<bool> = None;
static mut DOES_WINDOWS_SUPPORT_ANSI: Option<bool> = None;
/// Try enable `ANSI escape codes` and return the result.
pub fn try_enable_ansi_support() -> bool
{
use state::commands::win_commands::EnableAnsiCommand;
let mut command = EnableAnsiCommand::new();
let success = command.execute();
set_is_windows_ansi_supportable(success);
set_ansi_enabled(success);
has_been_tried_to_enable(true);
success
}
/// Get whether ansi has been enabled.
pub fn ansi_enabled() -> bool
{
unsafe { IS_ANSI_ON_WINDOWS_ENABLED.unwrap_or_else(| | false) }
}
/// Get whether windows supports ansi
pub fn windows_supportable() -> bool
{
unsafe { DOES_WINDOWS_SUPPORT_ANSI.unwrap_or_else(| | false)}
}
/// Get whether ansi has been tried to enable before.
pub fn has_been_tried_to_enable_ansi() -> bool
{
unsafe
{
return HAS_BEEN_TRYED_TO_ENABLE;
}
}
/// Set the is ansi escape property enabled or disabled. So whe can determine if the ansi escape codes are enabled.
pub fn set_ansi_enabled(is_enabled :bool)
{
unsafe
{
IS_ANSI_ON_WINDOWS_ENABLED = Some(is_enabled);
}
}
/// Set the is_windows_ansi_supportable property. So whe can determine whether windows supports ansi.
fn set_is_windows_ansi_supportable(is_enabled :bool)
{
unsafe
{
DOES_WINDOWS_SUPPORT_ANSI = Some(is_enabled);
}
}
/// Set the has_been_tried_to_enable property. So we can determine whether ansi has been tried to enable before.
fn has_been_tried_to_enable(has_been_tried: bool)
{
unsafe
{
HAS_BEEN_TRYED_TO_ENABLE = has_been_tried;
}
}

View File

@ -1,108 +0,0 @@
use winapi::um::wincon;
use super::{kernel};
use crossterm_style as style;
/// This will set the foreground color by the given winapi color.
pub fn set_fg_color(fg_color: u16) {
let csbi = kernel::get_console_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.wAttributes;
let bg_color = attrs & 0x0070;
color = fg_color | bg_color;
// background intensity is a seperate 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;
}
kernel::set_console_text_attribute(color);
}
/// This will set the background color by the given winapi color value.
pub fn set_bg_color(bg_color: u16) {
let csbi = kernel::get_console_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.wAttributes;
let fg_color = attrs & 0x0007;
color = fg_color | bg_color;
// foreground intensity is a separate value in attrs,
// wee 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;
}
kernel::set_console_text_attribute(color);
}
/// This will reset the colors to the given winapi color value.
pub fn reset(original_color: u16) {
kernel::set_console_text_attribute(original_color);
}
/// This will get the winapi color value from the Color and ColorType struct
pub fn winapi_color_val(color: style::Color, color_type: style::ColorType) -> u16 {
use crossterm_style::{Color, ColorType};
let winapi_color: u16;
let fg_green = wincon::FOREGROUND_GREEN;
let fg_red = wincon::FOREGROUND_RED;
let fg_blue = wincon::FOREGROUND_BLUE;
let fg_intensity = wincon::FOREGROUND_INTENSITY;
let bg_green = wincon::BACKGROUND_GREEN;
let bg_red = wincon::BACKGROUND_RED;
let bg_blue = wincon::BACKGROUND_BLUE;
let bg_intensity = wincon::BACKGROUND_INTENSITY;
match color_type {
ColorType::Foreground => {
winapi_color = match color {
Color::Black => 0,
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::Grey => fg_intensity,
Color::White => fg_intensity | fg_red | fg_green | fg_blue,
};
}
ColorType::Background => {
winapi_color = match color {
Color::Black => 0,
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::Grey => bg_intensity,
Color::White => bg_intensity | bg_red | bg_green | bg_blue,
};
}
};
winapi_color as u16
}

View File

@ -1,19 +1,15 @@
//! This module handles some logic for cursor interaction in the windows console.
use super::kernel;
/// This stores the cursor pos, at program level. So it can be recalled later.
static mut SAVED_CURSOR_POS:(i16,i16) = (0,0);
/// Set the current cursor position to X and Y
pub fn set(x: i16, y: i16)
{
kernel::set_console_cursor_position(x, y );
}
static mut SAVED_CURSOR_POS:(u16,u16) = (0,0);
/// Reset to saved cursor position
pub fn reset_to_saved_position()
{
unsafe {
kernel::set_console_cursor_position(SAVED_CURSOR_POS.0, SAVED_CURSOR_POS.1);
kernel::set_console_cursor_position(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16);
}
}
@ -27,9 +23,8 @@ pub fn save_cursor_pos()
}
}
/// Get current cursor position (X,Y)
pub fn pos() -> (i16,i16)
pub fn pos() -> (u16,u16)
{
let csbi = kernel::get_console_screen_buffer_info();
( csbi.dwCursorPosition.X , csbi.dwCursorPosition.Y )
( csbi.dwCursorPosition.X as u16, csbi.dwCursorPosition.Y as u16 )
}

View File

@ -1,17 +1,23 @@
//! This module is the core of all the `WINAPI` actions. All unsafe `WINAPI` function call are done here.
use winapi::um::winnt::HANDLE;
use winapi::um::winbase::STD_OUTPUT_HANDLE;
use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE };
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::{GetStdHandle};
use winapi::um::consoleapi::{SetConsoleMode};
use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize,
use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode, };
use winapi::shared::minwindef::{TRUE};
use winapi::um::wincon;
use winapi::um::wincon::
{
SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, CreateConsoleScreenBuffer,SetConsoleActiveScreenBuffer,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
FillConsoleOutputCharacterA, FillConsoleOutputAttribute,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, CHAR_INFO, PSMALL_RECT
};
use super::{Empty};
static mut CONSOLE_OUTPUT_HANDLE: Option<HANDLE> = None;
static mut CONSOLE_INPUT_HANDLE: Option<HANDLE> = None;
/// Get the std_output_handle of the console
pub fn get_output_handle() -> HANDLE {
@ -20,22 +26,46 @@ pub fn get_output_handle() -> HANDLE {
handle
} else {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !is_valid_handle(&handle)
{
panic!("Cannot get output handle")
}
CONSOLE_OUTPUT_HANDLE = Some(handle);
handle
}
}
}
/// Get the std_input_handle of the console
pub fn get_input_handle() -> HANDLE {
unsafe {
if let Some(handle) = CONSOLE_INPUT_HANDLE {
handle
} else {
let handle = GetStdHandle(STD_INPUT_HANDLE);
if !is_valid_handle(&handle)
{
panic!("Cannot get input handle")
}
CONSOLE_INPUT_HANDLE = Some(handle);
handle
}
}
}
/// Checks if the console handle is an invalid handle value.
pub fn is_valid_handle(handle: &HANDLE) -> bool {
if *handle == INVALID_HANDLE_VALUE {
true
} else {
false
} else {
true
}
}
/// Get console screen buffer info.
pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
let output_handle = get_output_handle();
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
@ -64,12 +94,19 @@ pub fn get_original_console_color() -> u16 {
console_buffer_info.wAttributes as u16
}
pub fn set_console_mode(console_mode: u32)
pub fn set_console_mode(handle: &HANDLE, console_mode: u32) -> bool
{
let output_handle = get_output_handle();
unsafe {
SetConsoleMode(output_handle, console_mode);
let success = SetConsoleMode(*handle, console_mode);
return is_true(success);
}
}
pub fn get_console_mode(handle: &HANDLE, current_mode: &mut u32) -> bool
{
unsafe {
let success = GetConsoleMode(*handle, &mut *current_mode);
return is_true(success);
}
}
@ -166,13 +203,92 @@ pub fn fill_console_output_attribute(cells_written: &mut u32, start_location: CO
is_true(success)
}
pub fn create_console_screen_buffer() -> HANDLE
{
use winapi::shared::ntdef::NULL;
use winapi::um::wincon::CONSOLE_TEXTMODE_BUFFER;
use winapi::um::winnt::{GENERIC_READ, GENERIC_WRITE, FILE_SHARE_READ, FILE_SHARE_WRITE};
use winapi::um::minwinbase::SECURITY_ATTRIBUTES;
use std::mem::size_of;
unsafe
{
let mut security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES
{
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: NULL,
bInheritHandle: TRUE
};
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
);
new_screen_buffer
}
}
pub fn set_active_screen_buffer(new_buffer: HANDLE)
{
unsafe
{
if !is_true(SetConsoleActiveScreenBuffer(new_buffer))
{
panic!("Cannot set active screen buffer");
}
}
}
pub fn read_console_output(read_buffer: &HANDLE, copy_buffer: &mut [CHAR_INFO;160], buffer_size: COORD, buffer_coord: COORD, source_buffer: PSMALL_RECT)
{
use self::wincon::ReadConsoleOutputA;
unsafe
{
if !is_true(ReadConsoleOutputA(
*read_buffer, // screen buffer to read from
copy_buffer.as_mut_ptr(), // buffer to copy into
buffer_size, // col-row size of chiBuffer
buffer_coord, // top left dest. cell in chiBuffer
source_buffer) // screen buffer source rectangle
){
panic!("Cannot read console output");
}
}
}
pub fn write_console_output(write_buffer: &HANDLE, copy_buffer: &mut [CHAR_INFO;160], buffer_size: COORD, buffer_coord: COORD, source_buffer: PSMALL_RECT)
{
use self::wincon::WriteConsoleOutputA;
unsafe
{
if !is_true(WriteConsoleOutputA(
*write_buffer, // screen buffer to write to
copy_buffer.as_mut_ptr(), // buffer to copy into
buffer_size, // col-row size of chiBuffer
buffer_coord, // top left dest. cell in chiBuffer
source_buffer)// screen buffer source rectangle
){
panic!("Cannot write to console output");
}
}
}
/// Parse integer to an bool
fn is_true(value: i32) -> bool
{
if value == 0{
false
return false;
}
else{
true
return true;
}
}

View File

@ -1,8 +1,39 @@
pub mod cursor;
pub mod color;
pub mod ansi_support;
pub mod kernel;
pub mod terminal;
mod winapi_extentions;
//! This module contains the `windows` specific (unsafe) logic.
pub mod kernel;
pub mod cursor;
pub mod terminal;
pub mod ansi_support;
use winapi;
use shared::traits::Empty;
use self::winapi::um::wincon::{COORD, CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT};
impl Empty for COORD {
fn empty() -> COORD {
COORD { X: 0, Y: 0 }
}
}
impl Empty for SMALL_RECT {
fn empty() -> SMALL_RECT {
SMALL_RECT {
Top: 0,
Right: 0,
Bottom: 0,
Left: 0,
}
}
}
impl Empty for CONSOLE_SCREEN_BUFFER_INFO {
fn empty() -> CONSOLE_SCREEN_BUFFER_INFO {
CONSOLE_SCREEN_BUFFER_INFO {
dwSize: COORD::empty(),
dwCursorPosition: COORD::empty(),
wAttributes: 0,
srWindow: SMALL_RECT::empty(),
dwMaximumWindowSize: COORD::empty(),
}
}
}

View File

@ -1,221 +1,8 @@
use super::{cursor, kernel};
use winapi::um::wincon::{SMALL_RECT, COORD};
/// Get the terminal size (y,x)
pub fn terminal_size() -> Option<(u16, u16)> {
let csbi = kernel::get_console_screen_buffer_info();
Some((
/// Get the terminal size
pub fn terminal_size() -> (u16, u16) {
let csbi = super::kernel::get_console_screen_buffer_info();
(
(csbi.srWindow.Right - csbi.srWindow.Left) as u16,
(csbi.srWindow.Bottom - csbi.srWindow.Top) as u16,
))
}
/// Scroll down `n` rows
pub fn scroll_down(rows: i16) {
let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window;
// Set srctWindow to the current window size and location.
srct_window = csbi.srWindow;
// Check whether the window is too close to the screen buffer top
if srct_window.Bottom < csbi.dwSize.Y - rows {
srct_window.Top += rows; // move top down
srct_window.Bottom += rows; // move bottom down
let success = kernel::set_console_info(true, &mut srct_window);
if success {
panic!("Something went wrong when scrolling down");
}
}
}
pub fn clear_after_cursor() {
let csbi = kernel::get_console_screen_buffer_info();
let (mut x,mut y) = cursor::pos();
// if cursor position is at the outer right position
if x > csbi.dwSize.X
{
y += 1;
x = 0;
}
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear(start_location,cells_to_write);
}
pub fn clear_before_cursor() {
let csbi = kernel::get_console_screen_buffer_info();
let (xpos,ypos) = cursor::pos();
// 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 { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X as u32 * ypos as u32) + (xpos as u32 + 1);
clear(start_location, cells_to_write);
}
pub fn clear_entire_screen() {
let csbi = kernel::get_console_screen_buffer_info();
// position x at start
let x = 0;
// position y at start
let y = 0;
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear( start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(0, 0);
}
pub fn clear_current_line()
{
let csbi = kernel::get_console_screen_buffer_info();
// position x at start
let x = 0;
// position y at start
let y = cursor::pos().1;
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32;
clear(start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(x, y);
}
pub fn clear_until_line()
{
let csbi = kernel::get_console_screen_buffer_info();
let (x,y) = cursor::pos();
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X - x) as u32;
clear(start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(x, y);
}
pub fn resize_terminal(width: i16, height: i16)
{
if width <= 0
{
panic!("Cannot set the terminal width lower than 1");
}
if height <= 0
{
panic!("Cannot set the terminal height lower then 1")
}
// Get the position of the current console window
let csbi = kernel::get_console_screen_buffer_info();
let mut success = false;
// 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 mut size = COORD { X: csbi.dwSize.X, Y: csbi.dwSize.Y };
if csbi.dwSize.X < csbi.srWindow.Left + width
{
if csbi.srWindow.Left >= i16::max_value() - width
{
panic!("Argument out of range when setting terminal width.");
}
size.X = csbi.srWindow.Left + width;
resize_buffer = true;
}
if csbi.dwSize.Y < csbi.srWindow.Top + height {
if csbi.srWindow.Top >= i16::max_value() - height
{
panic!("Argument out of range when setting terminal height");
}
size.Y = csbi.srWindow.Top + height;
resize_buffer = true;
}
if resize_buffer {
success = kernel::set_console_screen_buffer_size(size);
if !success
{
panic!("Something went wrong when setting screen buffer size.");
}
}
let mut fsr_window: SMALL_RECT = csbi.srWindow;
// Preserve the position, but change the size.
fsr_window.Bottom = fsr_window.Top + height;
fsr_window.Right = fsr_window.Left + width;
let success = kernel::set_console_info(true, &fsr_window);
if success {
// If we resized the buffer, un-resize it.
if resize_buffer {
kernel::set_console_screen_buffer_size(csbi.dwSize);
}
let bounds = kernel::get_largest_console_window_size();
if width > bounds.X
{
panic!("Argument width: {} out of range when setting terminal width.", width);
}
if height > bounds.Y
{
panic!("Argument height: {} out of range when setting terminal height", height);
}
}
}
fn clear(
start_loaction: COORD,
cells_to_write: u32
) {
let mut cells_written = 0;
let mut success = false;
success = kernel::fill_console_output_character(&mut cells_written,start_loaction,cells_to_write);
if !success {
panic!("Could not clear screen after cursor");
}
cells_written = 0;
success = kernel::fill_console_output_attribute(&mut cells_written,start_loaction, cells_to_write);
if !success {
panic!("Couldnot reset attributes after cursor");
}
)
}

View File

@ -1,31 +0,0 @@
use winapi::um::wincon::{COORD, CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT};
use super::Empty;
impl Empty for COORD {
fn empty() -> COORD {
COORD { X: 0, Y: 0 }
}
}
impl Empty for SMALL_RECT {
fn empty() -> SMALL_RECT {
SMALL_RECT {
Top: 0,
Right: 0,
Bottom: 0,
Left: 0,
}
}
}
impl Empty for CONSOLE_SCREEN_BUFFER_INFO {
fn empty() -> CONSOLE_SCREEN_BUFFER_INFO {
CONSOLE_SCREEN_BUFFER_INFO {
dwSize: COORD::empty(),
dwCursorPosition: COORD::empty(),
wAttributes: 0,
srWindow: SMALL_RECT::empty(),
dwMaximumWindowSize: COORD::empty(),
}
}
}

View File

@ -1,14 +1,25 @@
//! Crossterm provides the same core functionalities for both windows and unix systems.
//! Crossterm aims to be simple and easy to call in code.
//! True the simplicity of Crossterm you do not have to worry about the platform your working with.
//! You can just call the action you want to preform and under water it will check what to do based on the current platform.
#[macro_use]
mod shared;
mod kernel;
mod state;
pub mod crossterm_cursor;
pub mod crossterm_style;
pub mod crossterm_terminal;
pub mod cursor;
pub mod style;
pub mod terminal;
use shared::traits::{Construct};
pub use state::{ Context};
#[cfg(windows)]
extern crate winapi;
#[cfg(unix)]
extern crate libc;
#[cfg(unix)]
extern crate termios;
extern crate rand;

54
src/shared/functions.rs Normal file
View File

@ -0,0 +1,54 @@
//! Some actions need to preformed platform independently since they can not be solved `ANSI escape codes`.
use {Context};
#[cfg(windows)]
use kernel::windows_kernel::terminal::terminal_size;
#[cfg(unix)]
use kernel::unix_kernel::terminal::terminal_size;
#[cfg(windows)]
use kernel::windows_kernel::cursor::pos;
#[cfg(unix)]
use kernel::unix_kernel::terminal::pos;
/// Get the terminal size based on the current platform.
pub fn get_terminal_size() -> (u16, u16)
{
terminal_size()
}
/// Get the cursor position based on the current platform.
pub fn get_cursor_position() -> (u16,u16)
{
pos()
}
#[cfg(windows)]
/// Get an module specific implementation based on the current platform.
pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T>
{
let mut term: Option<T> = None;
let mut does_support = true;
if cfg!(target_os = "windows") {
#[cfg(windows)]
use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
// Try to enable ansi on windows if not than use WINAPI.
does_support = try_enable_ansi_support();
// println!("does support = {}", does_support);
if !does_support
{
term = Some(winapi_impl);
}
}
if does_support
{
term = Some(unix_impl);
}
term
}

View File

@ -1,3 +1,6 @@
//! This module contains some code that can be used for all module in this library.
#[macro_use]
pub mod macros;
pub mod traits;
pub mod functions;

View File

@ -10,3 +10,4 @@ pub trait Construct {
pub trait Empty {
fn empty() -> Self;
}

50
src/state/commands/mod.rs Normal file
View File

@ -0,0 +1,50 @@
//! In this module I make use of the command pattern to wrap state changes.
//!
//! The `command pattern` is an OOP concept but what it does is very handy.
//! Shortly said what this pattern can do is having an command (struct) like `EnableRawModeCommand` this command has two methods one to `execute` that command and one to `undo`.
//! Every time you preform some action you can push it into an list and at the end when you want to revert all the commands you have executed than you can loop true that loop true that list and `undo` the actions.
//!
//! So where do whe use the `Commands` for? This is so that we can push all or terminal state changes into list.
//! When we do not need those changes we can revert all the changes by looping true the list and undo all the action.
//!
//! See the `Context` struct where we store the commands for more info.
use rand;
#[cfg(unix)]
pub mod unix_command;
#[cfg(windows)]
pub mod win_commands;
pub mod shared_commands;
#[cfg(unix)]
pub use self::unix_command::*;
#[cfg(windows)]
pub use self::win_commands::*;
use super::Context;
/// This command can be used for simple commands witch just have an `undo()` and an `execute()`
pub trait ICommand
{
fn new() -> Box<Self> where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
/// This command is used for complex commands whits change the terminal state.
/// By passing an `Context` instance this command will register it self to notify the terminal state change.
pub trait IContextCommand
{
fn new(context: &mut Context) -> (Box<Self>, i16) where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
/// This generates an random key for the `ContextCommand`.
/// So that we can identify the `ContextCommand` in an list of commands.
fn generate_key() -> i16 {
rand::random::<i16>()
}

View File

@ -0,0 +1,36 @@
//! This module contains the commands that can be used for both unix and windows systems.
use super::ICommand;
use std::io;
use std::io::Write;
/// This command is used for switching to alternate screen and back to main screen.
#[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand;
impl ICommand for ToAlternateScreenBufferCommand
{
fn new() -> Box<ToAlternateScreenBufferCommand> {
Box::from(ToAlternateScreenBufferCommand { })
}
fn execute(&mut self) -> bool
{
let mut some_writer = io::stdout();
match write!(some_writer, csi!("?1049h"))
{
Ok(_) => true,
Err(_) => false
}
}
fn undo(&mut self) -> bool
{
let mut some_writer = io::stdout();
match write!(some_writer, csi!("?1049l"))
{
Ok(_) => true,
Err(_) => false
}
}
}

View File

@ -0,0 +1,113 @@
//! This module contains the commands that can be used for unix systems.
use Context;
use super::IContextCommand;
use kernel::unix_kernel::terminal;
use termios::{Termios, tcsetattr, TCSAFLUSH, ICANON, ECHO, CREAD};
const FD_STDIN: ::std::os::unix::io::RawFd = 1;
/// This command is used for switching to NoncanonicalMode.
#[derive(Clone, Copy)]
pub struct NoncanonicalModeCommand
{
key: i16
}
impl IContextCommand for NoncanonicalModeCommand
{
fn new(context: &mut Context) -> (Box<NoncanonicalModeCommand>, i16) {
let key = super::generate_key();
let command = NoncanonicalModeCommand { key: key };
context.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
// Set noncanonical mode
if let Ok(orig) = Termios::from_fd(FD_STDIN)
{
let mut noncan = orig.clone();
noncan.c_lflag &= !ICANON;
noncan.c_lflag &= !ECHO;
noncan.c_lflag &= !CREAD;
match tcsetattr(FD_STDIN, TCSAFLUSH, &noncan)
{
Ok(_) => return true,
Err(_) => return false,
};
}else {
return false
}
}
fn undo(&mut self) -> bool
{
// Disable noncanonical mode
if let Ok(orig) = Termios::from_fd(FD_STDIN)
{
let mut noncan = orig.clone();
noncan.c_lflag &= ICANON;
noncan.c_lflag &= ECHO;
noncan.c_lflag &= CREAD;
match tcsetattr(FD_STDIN, TCSAFLUSH, &noncan)
{
Ok(_) => return true,
Err(_) => return false,
};
}else {
return false;
}
}
}
/// This command is used for enabling and disabling raw mode for the terminal.
#[derive(Clone, Copy)]
pub struct EnableRawModeCommand
{
original_mode: Option<Termios>,
key: i16
}
impl IContextCommand for EnableRawModeCommand
{
fn new(context: &mut Context) -> (Box<EnableRawModeCommand>, i16) {
let key = super::generate_key();
let command = EnableRawModeCommand { original_mode: None, key: key };
context.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
if let Ok(original_mode) = terminal::get_terminal_mode()
{
self.original_mode = Some(original_mode);
let mut new_mode = self.original_mode.unwrap();
terminal::make_raw(&mut new_mode);
terminal::set_terminal_mode(&new_mode);
true
}else {
return false;
}
}
fn undo(&mut self) -> bool
{
if let Ok(original_mode) = terminal::get_terminal_mode()
{
let result = terminal::set_terminal_mode(&self.original_mode.unwrap());
match result
{
Ok(()) => true,
Err(_) => false
}
}else {
return false;
}
}
}

View File

@ -0,0 +1,215 @@
//! This module contains the commands that can be used for windows systems.
use super::{ICommand, IContextCommand};
use super::super::Context;
use kernel::windows_kernel::{kernel, ansi_support};
use winapi::shared::minwindef::DWORD;
use winapi::um::wincon;
use winapi::um::wincon::{ENABLE_VIRTUAL_TERMINAL_PROCESSING ,SMALL_RECT, COORD, CHAR_INFO};
use std::mem;
/// This command is used for enabling and disabling ANSI code support for windows systems,
/// For more info check: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.
#[derive(Clone, Copy)]
pub struct EnableAnsiCommand
{
mask: DWORD,
}
impl ICommand for EnableAnsiCommand
{
fn new() -> Box<EnableAnsiCommand> {
let key = super::generate_key();
let command = EnableAnsiCommand { mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING };
Box::from(command)
}
fn execute(&mut self) -> bool
{
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded.
if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled()
{
return ansi_support::windows_supportable();
} else {
let output_handle = kernel::get_output_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
panic!("Cannot get console mode");
return false;
}
dw_mode |= self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode)
{
panic!("Cannot get console mode");
return false;
}
return true;
}
}
fn undo(&mut self) -> bool
{
if ansi_support::ansi_enabled()
{
let output_handle = kernel::get_output_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
return false;
}
dw_mode &= !self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode)
{
return false;
}
ansi_support::set_ansi_enabled(false);
}
return true;
}
}
/// 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 EnableRawModeCommand
{
mask: DWORD,
key: i16
}
impl IContextCommand for EnableRawModeCommand
{
fn new(context: &mut Context) -> (Box<EnableRawModeCommand>, i16) {
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_PROCESSED_OUTPUT, ENABLE_WRAP_AT_EOL_OUTPUT, ENABLE_ECHO_INPUT};
let key = super::generate_key();
let command = EnableRawModeCommand { mask: ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT, key: key };
context.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_ECHO_INPUT};
let input_handle = kernel::get_input_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&input_handle, &mut dw_mode)
{
return false;
}
let new_mode = dw_mode & !self.mask;
if !kernel::set_console_mode(&input_handle, new_mode)
{
return false;
}
true
}
fn undo(&mut self) -> bool
{
let output_handle = kernel::get_output_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
return false;
}
let new_mode = dw_mode | self.mask;
if !kernel::set_console_mode(&output_handle, new_mode)
{
return false;
}
true
}
}
/// This command is used for switching to alternate screen and back to main screen.
/// check https://docs.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes for more info
#[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand;
impl ICommand for ToAlternateScreenBufferCommand
{
fn new() -> Box<ToAlternateScreenBufferCommand>
{
Box::from(ToAlternateScreenBufferCommand {})
}
fn execute(&mut self) -> bool
{
let mut chi_buffer: [CHAR_INFO;160] = unsafe {mem::zeroed() };
let handle = kernel::get_output_handle();
// create a new screen buffer to copy to.
let new_handle = kernel::create_console_screen_buffer();
// Make the new screen buffer the active screen buffer.
kernel::set_active_screen_buffer(new_handle);
// Set the source rectangle.
let mut srct_read_rect = SMALL_RECT
{
Top: 0,
Left: 0 ,
Bottom: 1,
Right: 79,
};
// The temporary buffer size is 2 rows x 80 columns.
let coord_buffer_size = COORD
{
X: 2,
Y: 80
};
// The top left destination cell of the temporary buffer is
// row 0, col 0.
let coord_buffer_coord = COORD
{
X: 0,
Y: 0,
};
// Copy the block from the screen buffer to the temp. buffer.
kernel::read_console_output(&handle, &mut chi_buffer, coord_buffer_size, coord_buffer_coord, &mut srct_read_rect);
// Set the destination rectangle.
let mut srct_write_rect = SMALL_RECT
{
Top: 10,
Left: 0,
Bottom: 11,
Right: 19,
};
// Copy from the temporary buffer to the new screen buffer.
kernel::write_console_output(&new_handle, &mut chi_buffer, coord_buffer_size, coord_buffer_coord, &mut srct_write_rect);
true
}
fn undo(&mut self) -> bool
{
let handle = kernel::get_output_handle();
kernel::set_active_screen_buffer(handle);
true
}
}

51
src/state/context.rs Normal file
View File

@ -0,0 +1,51 @@
//! This module is used for registering, storing an restoring the terminal state changes.
use std::ops::Drop;
use std::collections::HashMap;
use super::commands::IContextCommand;
/// Struct that stores the changed states of the terminal.
pub struct Context
{
changed_states: HashMap<i16, (Box<IContextCommand>) >,
}
impl Context
{
/// Create new Context where the terminals states can be handled.
pub fn new() -> Context
{
println!("Context has been created");
Context { changed_states: HashMap::new() }
}
/// Restore all changes that are made to the terminal.
pub fn restore_changes(&mut self)
{
for (x, state) in self.changed_states.iter_mut()
{
state.undo();
}
}
/// Register new changed state with the given key.
pub fn register_change(&mut self, change: Box<IContextCommand>, key: i16)
{
if !self.changed_states.contains_key(&key)
{
self.changed_states.insert(key, change);
}
}
/// Undo an specific state by the given state key.
pub fn undo_state(&mut self, state_key: i16)
{
if self.changed_states.contains_key(&state_key)
{
self.changed_states.remove(&state_key);
}
}
}

9
src/state/mod.rs Normal file
View File

@ -0,0 +1,9 @@
//! This module is used for managing the state changes of the terminal.
//!
//! If `crossterm` changes some core state of the terminal like: enabling ANSI or enabling raw mode it should be reverted when the current process ends.
//! It would be a little lame to let the terminal in raw mode after the the current process ends for the user of this library.
mod context;
pub mod commands;
pub use self::context::{Context};

View File

@ -1,23 +1,23 @@
use std::io;
use std::io::Write;
use std::string::String;
//! This is an ANSI specific implementation for styling related action.
//! This module is used for windows 10 terminals and unix terminals by default.
use Construct;
use super::ITerminalColor;
use super::super::{Color, ColorType};
use super::base_color::ITerminalColor;
use std::io::{self, Write};
/// This struct is an ansi implementation for color related actions.
#[derive(Debug)]
pub struct ANSIColor;
pub struct AnsiColor;
impl Construct for ANSIColor {
fn new() -> Box<ANSIColor> {
Box::from(ANSIColor {})
impl Construct for AnsiColor {
fn new() -> Box<AnsiColor> {
Box::from(AnsiColor {})
}
}
impl ITerminalColor for ANSIColor {
impl ITerminalColor for AnsiColor {
fn set_fg(&self, fg_color: Color) {
let mut some_writer = io::stdout();
write!(&mut some_writer, csi!("{}m"), self.color_value(fg_color, ColorType::Foreground));
@ -47,7 +47,8 @@ impl ITerminalColor for ANSIColor {
},
}
let rgb_val;
#[cfg(unix)]
let rgb_val: String;
let color_val = match color {
Color::Black => "5;0",
@ -65,7 +66,9 @@ impl ITerminalColor for ANSIColor {
Color::DarkCyan => "5;6",
Color::Grey => "5;15",
Color::White => "5;7",
#[cfg(unix)]
Color::Rgb{r,g,b} => { rgb_val = format!("2;{};{};{}", r,g,b); rgb_val.as_str()},
#[cfg(unix)]
Color::AnsiValue(val) => { rgb_val = format!("5;{}",val); rgb_val.as_str() }
};

View File

@ -1,18 +1,13 @@
//! With this module you can perform actions that are color related.
//! Like styling the font, foreground color and background color.
//! Like styling the font, foreground color and background.
use std::fmt;
use std::io;
use super::*;
use shared::functions;
use { Construct, Context };
use style::{Color, ObjectStyle, StyledObject};
use Construct;
use crossterm_style::{ObjectStyle, StyledObject};
use super::base_color::ITerminalColor;
use super::super::Color;
#[cfg(unix)]
use super::ANSIColor;
#[cfg(windows)]
use super::WinApiColor;
use std::ops::Drop;
use std::{ fmt, io };
/// Struct that stores an specific platform implementation for color related actions.
pub struct TerminalColor {
@ -20,24 +15,28 @@ pub struct TerminalColor {
}
impl TerminalColor {
/// Instantiate an color implementation whereon color related actions can be performed.
pub fn init(&mut self) {
if let None = self.terminal_color {
self.terminal_color = get_color_options();
}
/// Create new instance whereon color related actions can be performed.
pub fn new() -> TerminalColor {
#[cfg(target_os = "windows")]
let color = functions::get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new());
#[cfg(not(target_os = "windows"))]
let color = Some(AnsiColor::new() as Box<ITerminalColor>);
TerminalColor { terminal_color: color }
}
/// Set the forground color to the given color.
/// Set the foreground color to the given color.
///
/// #Example
///
/// ```rust
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_style::{ get, Color};
/// use self::crossterm::style::{ color, Color};
///
/// // Get colored terminal instance
/// let mut colored_terminal = get();
/// let mut colored_terminal = color();
///
/// // Set foreground color of the font
/// colored_terminal.set_fg(Color::Red);
@ -46,7 +45,6 @@ impl TerminalColor {
///
/// ```
pub fn set_fg(&mut self, color: Color) {
&self.init();
if let Some(ref terminal_color) = self.terminal_color {
terminal_color.set_fg(color);
}
@ -60,10 +58,10 @@ impl TerminalColor {
///
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_style::{ get, Color};
/// use self::crossterm::style::{ color, Color};
///
/// // Get colored terminal instance
/// let mut colored_terminal = get();
/// let mut colored_terminal = color();
///
/// // Set background color of the font
/// colored_terminal.set_bg(Color::Red);
@ -72,7 +70,6 @@ impl TerminalColor {
///
/// ```
pub fn set_bg(&mut self, color: Color) {
&self.init();
if let Some(ref terminal_color) = self.terminal_color {
terminal_color.set_bg(color);
}
@ -84,16 +81,15 @@ impl TerminalColor {
/// ```rust
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_style::get;
/// use self::crossterm::style::color;
///
/// // Get colored terminal instance
/// let mut colored_terminal = get();
/// let mut colored_terminal = color();
///
/// colored_terminal.reset();
///
/// ```
pub fn reset(&mut self) {
&self.init();
if let Some(ref terminal_color) = self.terminal_color {
terminal_color.reset();
}
@ -117,14 +113,6 @@ impl TerminalColor {
}
}
/// Get an concrete ITerminalColor implementation based on the current operating system.
fn get_color_options() -> Option<Box<ITerminalColor>> {
#[cfg(unix)]
return Some(ANSIColor::new());
#[cfg(windows)]
return Some(WinApiColor::new());
}
/// Get an TerminalColor implementation whereon color related actions can be performed.
///
/// # Example
@ -132,20 +120,18 @@ fn get_color_options() -> Option<Box<ITerminalColor>> {
/// ```rust
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_style::{get, Color};
/// use self::crossterm::style::{color, Color};
///
/// // Get colored terminal instance
/// let mut colored_terminal = get();
/// let mut colored_terminal = color();
///
/// // preform some actions on the colored terminal
/// colored_terminal.set_fg(Color::Red);
/// colored_terminal.set_bg(Color::Blue);
/// colored_terminal.reset();
/// ```
pub fn get() -> Box<TerminalColor> {
Box::from(TerminalColor {
terminal_color: get_color_options(),
})
pub fn color() -> Box<TerminalColor> {
Box::from(TerminalColor::new())
}
/// Wraps an displayable object so it can be formatted with colors and attributes.
@ -157,7 +143,7 @@ pub fn get() -> Box<TerminalColor> {
/// ```rust
/// extern crate crossterm;
///
/// use self::crossterm::crossterm_style::{paint,Color};
/// use self::crossterm::style::{paint,Color};
///
/// fn main()
/// {

View File

@ -1,18 +1,24 @@
///!
///! This trait defines the actions that can be preformed with the termial color.
///! This trait can be inplemented so that an concrete inplementation of the ITerminalColor can forfill
pub mod color;
#[cfg(target_os = "windows")]
mod winapi_color;
mod ansi_color;
#[cfg(target_os = "windows")]
use self::winapi_color::WinApiColor;
use self::ansi_color::AnsiColor;
use super::{Color, ColorType};
///! This trait defines the actions that can be preformed with the terminal color.
///! This trait can be implemented so that an concrete implementation of the ITerminalColor can forfill
///! the wishes to work on an specific platform.
///!
///! ## For example:
///!
///! This trait is inplemented for winapi (Windows specific) and ansi (Unix specific),
///! so that the color related actions can be preformed on both unix and windows systems.
///!
use super::super::{Color, ColorType};
///! This trait is implemented for `WINAPI` (Windows specific) and `ANSI` (Unix specific),
///! so that color related actions can be preformed on both unix and windows systems.
pub trait ITerminalColor {
/// Set the forground color to the given color.
/// Set the foreground color to the given color.
fn set_fg(&self, fg_color: Color);
/// Set the background color to the given color.
fn set_bg(&self, fg_color: Color);

View File

@ -0,0 +1,128 @@
use Construct;
use super::ITerminalColor;
use super::super::{ColorType, Color};
use winapi::um::wincon;
use kernel::windows_kernel::kernel;
/// This struct is an windows implementation for color related actions.
#[derive(Debug)]
pub struct WinApiColor {
original_console_color: u16,
}
impl Construct for WinApiColor {
fn new() -> Box<WinApiColor> {
Box::from(WinApiColor {
original_console_color: kernel::get_original_console_color(),
})
}
}
impl ITerminalColor for WinApiColor {
fn set_fg(&self, fg_color: Color) {
let color_value = &self.color_value(fg_color, ColorType::Foreground);
let csbi = kernel::get_console_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.wAttributes;
let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | 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;
}
kernel::set_console_text_attribute(color);
}
fn set_bg(&self, bg_color: Color) {
let color_value = &self.color_value(bg_color, ColorType::Background);
let csbi = kernel::get_console_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.wAttributes;
let fg_color = attrs & 0x0007;
color = fg_color | color_value.parse::<u16>().unwrap();
// 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;
}
kernel::set_console_text_attribute(color);
}
fn reset(&self) {
kernel::set_console_text_attribute(self.original_console_color);
}
/// This will get the winapi color value from the Color and ColorType struct
fn color_value(&self, color: Color, color_type: ColorType) -> String {
use style::{Color, ColorType};
let winapi_color: u16;
let fg_green = wincon::FOREGROUND_GREEN;
let fg_red = wincon::FOREGROUND_RED;
let fg_blue = wincon::FOREGROUND_BLUE;
let fg_intensity = wincon::FOREGROUND_INTENSITY;
let bg_green = wincon::BACKGROUND_GREEN;
let bg_red = wincon::BACKGROUND_RED;
let bg_blue = wincon::BACKGROUND_BLUE;
let bg_intensity = wincon::BACKGROUND_INTENSITY;
match color_type {
ColorType::Foreground => {
winapi_color = match color {
Color::Black => 0,
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::Grey => fg_intensity,
Color::White => fg_intensity | fg_red | fg_green | fg_blue,
};
}
ColorType::Background => {
winapi_color = match color {
Color::Black => 0,
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::Grey => bg_intensity,
Color::White => bg_intensity | bg_red | bg_green | bg_blue,
};
}
};
winapi_color.to_string()
}
}

View File

@ -1,7 +1,10 @@
//! This module is used for styling the terminal text.
//! Under styling we can think of coloring the font and applying attributes to it.
mod color;
mod styles;
pub use self::color::color::*;
pub use self::color::color::{color, paint, TerminalColor };
pub use self::styles::objectstyle::ObjectStyle;
pub use self::styles::styledobject::StyledObject;

4
src/style/styles/mod.rs Normal file
View File

@ -0,0 +1,4 @@
//! This module contains the modules that are responsible for storing the styling displayable objects or simply set text.
pub mod objectstyle;
pub mod styledobject;

View File

@ -1,6 +1,9 @@
use crossterm_style::{Color, StyledObject};
use std::fmt::Display;
//! This module contains the `object style` that can be applied to an `styled object`.
use std::fmt::Display;
use style::{Color, StyledObject};
#[cfg(unix)]
use super::super::Attribute;
/// Struct that contains the style properties that can be applied to an displayable object.

View File

@ -1,12 +1,14 @@
use std;
use std::fmt;
//! This module contains the logic to style an object that contains some context witch can be styled.
use std::{ self, fmt };
use std::io::Write;
#[cfg(unix)]
use super::super::Attribute;
use crossterm_style::{Color, ObjectStyle};
use style::{Color, ObjectStyle};
/// Struct that contains both the style and the content wits will be styled.
/// Struct that contains both the style and the content wits can be styled.
pub struct StyledObject<D> {
pub object_style: ObjectStyle,
pub content: D,
@ -19,7 +21,7 @@ impl<D> StyledObject<D> {
///
/// ```rust
/// extern crate crossterm;
/// use self::crossterm::crossterm_style::{paint,Color};
/// use self::crossterm::style::{paint,Color};
///
/// // create an styled object with the foreground color red.
/// let styledobject = paint("I am colored red").with(Color::Red);
@ -44,7 +46,7 @@ impl<D> StyledObject<D> {
///
/// ```rust
/// extern crate crossterm;
/// use self::crossterm::crossterm_style::{paint,Color};
/// use self::crossterm::style::{paint,Color};
///
/// // create an styled object with the background color red.
/// let styledobject = paint("I am colored red").on(Color::Red);
@ -70,7 +72,7 @@ impl<D> StyledObject<D> {
/// ```rust
///
/// extern crate crossterm;
/// use self::crossterm::crossterm_style::{paint,Attribute};
/// use self::crossterm::style::{paint,Attribute};
///
/// println!("{}", paint("Bold").attr(Attribute::Bold));
///
@ -110,7 +112,7 @@ macro_rules! impl_fmt
impl<D: fmt::$name> fmt::$name for StyledObject<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
let mut colored_terminal = super::super::get();
let mut colored_terminal = super::super::color();
let mut reset = true;
if let Some(bg) = self.object_style.bg_color

View File

@ -1,23 +1,25 @@
//! 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 Construct;
use shared::functions;
use super::{ClearType, ITerminal};
use std::io;
use std::io::Write;
use Construct;
use super::base_terminal::{ClearType, ITerminal};
use kernel::linux_kernel::terminal::*;
/// This struct is an ansi implementation for terminal related actions.
pub struct UnixTerminal;
pub struct AnsiTerminal ;
impl Construct for UnixTerminal {
fn new() -> Box<UnixTerminal> {
Box::from(UnixTerminal {})
impl Construct for AnsiTerminal {
fn new() -> Box<AnsiTerminal> {
Box::from(AnsiTerminal {})
}
}
impl ITerminal for UnixTerminal {
impl ITerminal for AnsiTerminal {
fn clear(&self, clear_type: ClearType) {
let mut some_writer = io::stdout();
match clear_type {
ClearType::All => {
write!(&mut some_writer, csi!("2J"));
@ -37,8 +39,8 @@ impl ITerminal for UnixTerminal {
};
}
fn terminal_size(&self) -> Option<(u16, u16)> {
terminal_size()
fn terminal_size(&self) -> (u16, u16) {
functions::get_terminal_size()
}
fn scroll_up(&self, count: i16) {

51
src/terminal/mod.rs Normal file
View File

@ -0,0 +1,51 @@
//! Module that contains all the actions related to the terminal.
//!
//! We can think of:
//! - alternate screen
//! - raw mode
//! - clearing resizing scrolling the terminal.
mod terminal;
#[cfg(target_os = "windows")]
mod winapi_terminal;
mod ansi_terminal;
mod screen;
mod raw;
#[cfg(target_os = "windows")]
use self::winapi_terminal::WinApiTerminal;
use self::ansi_terminal::AnsiTerminal;
pub use self::terminal::{ Terminal, terminal};
/// Enum that can be used for the kind of clearing that can be done in the terminal.
pub enum ClearType {
All,
FromCursorDown,
FromCursorUp,
CurrentLine,
UntilNewLine,
}
///! This trait defines the actions that can be preformed with the terminal.
///! This trait can be implemented so that an concrete implementation of the ITerminal can forfill
///! the wishes to work on an specific platform.
///!
///! ## For example:
///!
///! This trait is implemented for `WINAPI` (Windows specific) and `ANSI` (Unix specific),
///! so that cursor related actions can be preformed on both unix and windows systems.
pub trait ITerminal {
/// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y)
fn terminal_size(&self) -> (u16, u16);
/// Scroll `n` lines up in the current terminal.
fn scroll_up(&self, count: i16);
/// Scroll `n` lines down in the current terminal.
fn scroll_down(&self, count: i16);
/// Resize terminal to the given width and height.
fn set_size(&self,width: i16, height: i16);
}

82
src/terminal/raw.rs Normal file
View File

@ -0,0 +1,82 @@
//! 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.
//!
//! With these modes you can easier design the terminal screen.
//!
//! # Example
//!
//! ```rust
//! to be implemented
//!
//! ```
#[cfg(not(windows))]
use state::commands::unix_command::EnableRawModeCommand;
#[cfg(windows)]
use state::commands::win_commands::EnableRawModeCommand;
use Context;
use state::commands::IContextCommand;
use std::io::{ self, Write};
/// A wrapper for the raw terminal state. Which can be used to write to.
pub struct RawTerminal<'a, W: Write>
{
output: W,
context : &'a mut Context
}
/// Trait withs contains a method for switching into raw mode.
pub trait IntoRawMode: Write + Sized
{
fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result<RawTerminal<Self>>;
}
impl<W: Write> IntoRawMode for W
{
/// Switch to raw mode.
///
/// Raw mode means that input (stdin) won't be printed it will instead have to be written manually by
/// the program. The input isn't canonicalised or line buffered (that is, you can
/// read from input(stdin) one byte of a time).
fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result<RawTerminal<Self>>
{
let (mut command, _) = EnableRawModeCommand::new(context);
let success = command.execute();
if success
{
Ok(RawTerminal { output: self, context: context})
}else { panic!("cannot move into raw mode") }
}
}
impl<'a, W: Write> Write for RawTerminal<'a, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
}
/// If an instance of `RawTerminal` will be dropped all terminal changes that are made will be undone.
impl <'a, W: Write> Drop for RawTerminal<'a, W>
{
fn drop(&mut self)
{
self.context.restore_changes();
}
}

109
src/terminal/screen.rs Normal file
View File

@ -0,0 +1,109 @@
//! This module contains all the logic for switching between alternate screen and main screen.
use shared::functions;
use Context;
use state::commands::*;
use std::{ fmt, ops };
use std::io::{self, Write};
pub struct ToMainScreen;
impl fmt::Display for ToMainScreen
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
get_to_alternate_screen_command().undo();
Ok(())
}
}
/// Struct that switches to alternate screen buffer on display.
pub struct ToAlternateScreen;
impl fmt::Display for ToAlternateScreen
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
get_to_alternate_screen_command().execute();
Ok(())
}
}
/// Struct that can be used for writing to an alternate screen.
///
/// #Example
///
/// ```rust
/// extern crate crossterm;
/// use self::crossterm::terminal::screen;
/// use std::{time, thread};
/// ...
///
/// // Initialize and switch to the alternate screen from an std output handle.
/// // Now you can write to this screen.
/// let mut screen = screen::AlternateScreen::from(stdout());
/// // Write some text to the alternate screen.
/// write!(screen, "Welcome to the alternate screen. Wait 4 seconds to switch back").unwrap();
/// thread::sleep(time::Duration::from_secs(4));
/// // switch back to main screen.
/// write!(screen, "{}", screen::ToMainScreen);
/// write!(screen, "{}", "We are back again at the main screen");
///
/// ...
///
/// ```
pub struct AlternateScreen<W: Write> {
/// The output target.
output: W,
context: Context
}
impl<W: Write> AlternateScreen<W> {
pub fn from(mut output: W) -> Self {
write!(output, "{}", ToAlternateScreen);
AlternateScreen { output: output, context: Context::new()}
}
}
impl<W: Write> ops::Deref for AlternateScreen<W> {
type Target = W;
fn deref(&self) -> &W {
&self.output
}
}
impl<W: Write> ops::DerefMut for AlternateScreen<W> {
fn deref_mut(&mut self) -> &mut W {
&mut self.output
}
}
impl<W: Write> Write for AlternateScreen<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
}
impl<W: Write> Drop for AlternateScreen<W>
{
fn drop(&mut self)
{
write!(self, "{}", ToMainScreen).expect("switch to main screen");
}
}
/// Get the alternate screen command to enable and disable alternate screen based on the current platform
fn get_to_alternate_screen_command() -> Box<ICommand>
{
#[cfg(target_os = "windows")]
let command = functions::get_module::<Box<ICommand>>(win_commands::ToAlternateScreenBufferCommand::new(), shared_commands::ToAlternateScreenBufferCommand::new()).unwrap();
#[cfg(not(target_os = "windows"))]
let command = shared_commands::ToAlternateScreenBufferCommand::new();
command
}

View File

@ -1,13 +1,11 @@
//! With this module you can perform actions that are terminal related.
//! Like clearing and scrolling in the terminal or getting the size of the terminal.
use Construct;
use super::base_terminal::{ClearType, ITerminal};
use super::*;
use shared::functions;
use {Construct, Context};
#[cfg(unix)]
use super::UnixTerminal;
#[cfg(windows)]
use super::WinApiTerminal;
use std::ops::Drop;
/// Struct that stores an specific platform implementation for terminal related actions.
pub struct Terminal {
@ -15,11 +13,15 @@ pub struct Terminal {
}
impl Terminal {
/// Instantiate an color implementation whereon color related actions can be performed.
pub fn init(&mut self) {
if let None = self.terminal {
self.terminal = get_terminal();
}
/// Create new terminal instance whereon terminal related actions can be performed.
pub fn new() -> Terminal {
#[cfg(target_os = "windows")]
let terminal = functions::get_module::<Box<ITerminal>>(WinApiTerminal::new(), AnsiTerminal::new());
#[cfg(not(target_os = "windows"))]
let terminal = Some(AnsiTerminal::new() as Box<ITerminal>);
Terminal { terminal: terminal }
}
/// Clear the current cursor by specifying the clear type
@ -29,24 +31,23 @@ impl Terminal {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// // clear all cells in terminal.
/// term.clear(crossterm_terminal::ClearType::All);
/// term.clear(terminal::ClearType::All);
/// // clear all cells from the cursor position downwards in terminal.
/// term.clear(crossterm_terminal::ClearType::FromCursorDown);
/// term.clear(terminal::ClearType::FromCursorDown);
/// // clear all cells from the cursor position upwards in terminal.
/// term.clear(crossterm_terminal::ClearType::FromCursorUp);
/// term.clear(terminal::ClearType::FromCursorUp);
/// // clear current line cells in terminal.
/// term.clear(crossterm_terminal::ClearType::CurrentLine);
/// term.clear(terminal::ClearType::CurrentLine);
/// // clear all cells from cursor position until new line in terminal.
/// term.clear(crossterm_terminal::ClearType::UntilNewLine);
/// term.clear(terminal::ClearType::UntilNewLine);
///
/// ```
pub fn clear(&mut self, clear_type: ClearType) {
&self.init();
if let Some(ref terminal) = self.terminal {
terminal.clear(clear_type);
}
@ -59,22 +60,19 @@ impl Terminal {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// let size = term.terminal_size();
/// println!("{:?}", size);
///
/// ```
pub fn terminal_size(&mut self) -> Option<(u16, u16)> {
&self.init();
pub fn terminal_size(&mut self) -> (u16, u16) {
if let Some(ref terminal) = self.terminal {
let a = terminal.terminal_size();
a
} else {
None
return terminal.terminal_size()
}
(0,0)
}
/// Scroll `n` lines up in the current terminal.
@ -84,16 +82,15 @@ impl Terminal {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// // scroll up by 5 lines
/// let size = term.scroll_up(5);
///
/// ```
pub fn scroll_up(&mut self, count: i16) {
&self.init();
if let Some(ref terminal) = self.terminal {
terminal.scroll_up(count);
}
@ -106,16 +103,15 @@ impl Terminal {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// // scroll down by 5 lines
/// let size = term.scroll_down(5);
///
/// ```
pub fn scroll_down(&mut self, count: i16) {
&self.init();
if let Some(ref terminal) = self.terminal {
terminal.scroll_down(count);
}
@ -128,9 +124,9 @@ impl Terminal {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// // Set of the size to X: 10 and Y: 10
/// let size = term.set_size(10,10);
@ -138,23 +134,12 @@ impl Terminal {
/// ```
pub fn set_size(&mut self, width: i16, height: i16)
{
&self.init();
if let Some (ref terminal) = self.terminal
{
if let Some (ref terminal) = self.terminal {
terminal.set_size(width,height);
}
}
}
/// Get the concrete ITerminal implementation based on the current operating system.
fn get_terminal() -> Option<Box<ITerminal>> {
#[cfg(unix)]
return Some(UnixTerminal::new());
#[cfg(windows)]
return Some(WinApiTerminal::new());
}
/// Get an Terminal implementation whereon terminal related actions can be performed.
///
/// Check `/examples/terminal` in the libary for more spesific examples.
@ -164,16 +149,16 @@ fn get_terminal() -> Option<Box<ITerminal>> {
/// ```rust
///
/// extern crate crossterm;
/// use crossterm::crossterm_terminal;
/// use crossterm::terminal;
///
/// let mut term = crossterm_terminal::get();
/// let mut term = terminal::terminal();
///
/// // scroll down by 5 lines
/// let size = term.scroll_down(5);
///
/// ```
pub fn get() -> Box<Terminal> {
Box::from(Terminal {
terminal: get_terminal(),
})
///
pub fn terminal() -> Box<Terminal>
{
Box::from(Terminal::new())
}

View File

@ -0,0 +1,246 @@
//! This is an `WINAPI` specific implementation for terminal related action.
//! This module is used for windows 10 terminals and unix terminals by default.
use {Construct};
use cursor::cursor;
use super::{ClearType, ITerminal};
use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,};
use kernel::windows_kernel::{kernel, terminal};
/// This struct is an windows implementation for terminal related actions.
pub struct WinApiTerminal;
impl Construct for WinApiTerminal {
fn new() -> Box<WinApiTerminal> {
Box::from(WinApiTerminal {})
}
}
impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType) {
let csbi = kernel::get_console_screen_buffer_info();
let pos = cursor().pos();
match clear_type
{
ClearType::All => clear_entire_screen(csbi),
ClearType::FromCursorDown => clear_after_cursor(pos,csbi),
ClearType::FromCursorUp => clear_before_cursor(pos, csbi),
ClearType::CurrentLine => clear_current_line(pos, csbi),
ClearType::UntilNewLine => clear_until_line(pos, csbi),
};
}
fn terminal_size(&self) -> (u16, u16) {
terminal::terminal_size()
}
fn scroll_up(&self, count: i16) {
// yet to be inplemented
}
fn scroll_down(&self, count: i16) {
let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window;
// Set srctWindow to the current window size and location.
srct_window = csbi.srWindow;
// Check whether the window is too close to the screen buffer top
if srct_window.Bottom < csbi.dwSize.Y - count {
srct_window.Top += count; // move top down
srct_window.Bottom += count; // move bottom down
let success = kernel::set_console_info(true, &mut srct_window);
if success {
panic!("Something went wrong when scrolling down");
}
}
}
/// Set the current terminal size
fn set_size(&self, width: i16, height: i16) {
if width <= 0
{
panic!("Cannot set the terminal width lower than 1");
}
if height <= 0
{
panic!("Cannot set the terminal height lower then 1")
}
// Get the position of the current console window
let csbi = kernel::get_console_screen_buffer_info();
let mut success = false;
// 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 mut size = COORD { X: csbi.dwSize.X, Y: csbi.dwSize.Y };
if csbi.dwSize.X < csbi.srWindow.Left + width
{
if csbi.srWindow.Left >= i16::max_value() - width
{
panic!("Argument out of range when setting terminal width.");
}
size.X = csbi.srWindow.Left + width;
resize_buffer = true;
}
if csbi.dwSize.Y < csbi.srWindow.Top + height {
if csbi.srWindow.Top >= i16::max_value() - height
{
panic!("Argument out of range when setting terminal height");
}
size.Y = csbi.srWindow.Top + height;
resize_buffer = true;
}
if resize_buffer {
success = kernel::set_console_screen_buffer_size(size);
if !success
{
panic!("Something went wrong when setting screen buffer size.");
}
}
let mut fsr_window: SMALL_RECT = csbi.srWindow;
// Preserve the position, but change the size.
fsr_window.Bottom = fsr_window.Top + height;
fsr_window.Right = fsr_window.Left + width;
let success = kernel::set_console_info(true, &fsr_window);
if success {
// If we resized the buffer, un-resize it.
if resize_buffer {
kernel::set_console_screen_buffer_size(csbi.dwSize);
}
let bounds = kernel::get_largest_console_window_size();
if width > bounds.X
{
panic!("Argument width: {} out of range when setting terminal width.", width);
}
if height > bounds.Y
{
panic!("Argument height: {} out of range when setting terminal height", height);
}
}
}
}
pub fn clear_after_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO) {
let (mut x,mut y) = pos;
// if cursor position is at the outer right position
if x as i16 > csbi.dwSize.X
{
y += 1;
x = 0;
}
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear(start_location,cells_to_write);
}
pub fn clear_before_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO) {
let (xpos,ypos) = pos;
// 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 { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X as u32 * ypos as u32) + (xpos as u32 + 1);
clear(start_location, cells_to_write);
}
pub fn clear_entire_screen(csbi: CONSOLE_SCREEN_BUFFER_INFO) {
// position x at start
let x = 0;
// position y at start
let y = 0;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear( start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor().goto(0, 0);
}
pub fn clear_current_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO)
{
// position x at start
let x = 0;
// position y at start
let y = pos.1;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32;
clear(start_location, cells_to_write);
// put the cursor back at 1 cell on current row
cursor().goto(0, y);
}
pub fn clear_until_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO)
{
let (x,y) = pos;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X - x as i16) as u32;
clear(start_location, cells_to_write);
// put the cursor back at original cursor position
cursor().goto(x,y);
}
fn clear(
start_loaction: COORD,
cells_to_write: u32
) {
let mut cells_written = 0;
let mut success = false;
success = kernel::fill_console_output_character(&mut cells_written, start_loaction, cells_to_write);
if !success
{
panic!("Could not clear screen after cursor");
}
cells_written = 0;
success = kernel::fill_console_output_attribute(&mut cells_written, start_loaction, cells_to_write);
if !success {
panic!("Couldnot reset attributes after cursor");
}
}