Refactored namespaces. Added comments where needed. Alternatescreen is

working for windows 10 terminals. Refactored usings. Rearanged usings.
Raw Mode / alternate screeen windows yet to be tested. Added examples.
Refactored code
This commit is contained in:
T 2018-03-10 17:33:06 +01:00
parent 03bca7fe76
commit 4c14ad836b
48 changed files with 1267 additions and 1044 deletions

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

@ -11,36 +11,18 @@
//! //!
//! - Run program with: `cargo run` //! - Run program with: `cargo run`
// Import crossterm crate.
extern crate crossterm; extern crate crossterm;
//
//// Add the usings for the crossterms modules to play with crossterm
//use self::crossterm::crossterm_style::{paint, Color };
use self::crossterm::crossterm_cursor::cursor;
use self::crossterm::crossterm_terminal;
//
//// Import the example modules.
//pub mod color;
//pub mod cursor;
//pub mod terminal;
use std::io::{self, Error, ErrorKind, Write, stdout, stdin, BufRead};
//use std::{time, thread}; mod terminal;
//
use crossterm::crossterm_terminal::screen::{AlternateScreen, ToMainScreen, ToAlternateScreen};
use crossterm::crossterm_terminal::IntoRawMode;
use terminal::alternate_screen;
use crossterm::Context; use crossterm::Context;
use std::io::{Write, stdout};
use crossterm::cursor;
use std::{time, thread};
fn main() { fn main() {
let mut context = Context::new(); alternate_screen::manually_switch_to_alternate_screen();
let mut screen = stdout(); // cursor::cursor().goto(10,10).print("@");
write!(screen, "{}", ToAlternateScreen);
write!(screen, "Welcome to the alternate screen.\n\nPlease wait patiently until we arrive back at the main screen in a about three seconds.").unwrap();
//screen.flush().unwrap();
thread::sleep(time::Duration::from_secs(3));
} }

View File

@ -4,7 +4,7 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::crossterm_style::{paint, Color}; use self::crossterm::style::{paint, Color};
/// print some red font | demonstration. /// print some red font | demonstration.
pub fn paint_foreground() pub fn paint_foreground()
@ -120,14 +120,13 @@ pub fn print_font_with_attributes()
} }
/// Print all supported rgb colors | demonstration. /// Print all supported rgb colors | demonstration.
#[cfg(unix)]#[cfg(unix)] #[cfg(unix)]
pub fn print_supported_colors() pub fn print_supported_colors()
{ {
let count = crossterm::crossterm_style::color().get_available_color_count().unwrap(); let count = crossterm::style::color().get_available_color_count().unwrap();
for i in 0..count for i in 0..count
{ {
println!("{}", paint(format!("Color: {}",i)).with(Color::AnsiValue(i as u8))); println!("{}", paint(format!("Color: {}",i)).with(Color::AnsiValue(i as u8)));
} }
} }

View File

@ -4,7 +4,7 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::crossterm_cursor::{cursor, TerminalCursor}; use self::crossterm::cursor::{cursor, TerminalCursor};
/// Set the cursor to position X: 10, Y: 5 in the terminal. /// Set the cursor to position X: 10, Y: 5 in the terminal.
pub fn goto() pub fn goto()

View File

@ -0,0 +1,70 @@
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,136 +1,3 @@
//! pub mod alternate_screen;
//! Terminal Examples pub mod raw_mode;
//! pub mod terminal;
extern crate crossterm;
use crossterm::crossterm_terminal::{ Terminal, ClearType, terminal};
use crossterm::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).
crossterm_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).
crossterm_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).
crossterm_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).
crossterm_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

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,45 +0,0 @@
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;
pub enum CommandType
{
Unix,
Windows,
}
pub trait ICommand
{
fn new() -> Box<Self> where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
pub trait IContextCommand
{
fn new(context: &mut Context) -> (Box<Self>, i16) where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
fn generate_key() -> i16 {
// let mut rng = rand::thread_rng();
// if rng.gen() { // random bool
// println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
// }
rand::random::<i16>()
}

View File

@ -1,72 +0,0 @@
use super::commands::{IContextCommand, ICommand};
use std::ops::Drop;
use std::collections::HashMap;
pub struct Context
{
changed_states: HashMap<i16, Box<IContextCommand>>,
}
impl Context
{
pub fn new() -> Context
{
Context { changed_states: HashMap::new() }
}
/// restore to default terminal state
pub fn restore_changes(&mut self)
{
// println!("restore change");
for (x, state) in self.changed_states.iter_mut()
{
state.undo();
// println!("State is removed, total state");
}
}
/// Register new changed state
pub fn register_change(&mut self, change: Box<IContextCommand>, key: i16)
{
// println!("register change");
if !self.changed_states.contains_key(&key)
{
self.changed_states.insert(key, change);
// println!("State is registerd, total states: {}", self.changed_states.len());
}
}
/// Undo state
pub fn undo_state(&mut self, state_key: i16)
{
// println!("undo specific");
if self.changed_states.contains_key(&state_key)
{
self.changed_states.remove(&state_key);
}
}
}
//
//fn state_wrapper() -> Context {
// // Initialize it to a null value
// static mut SINGLETON: *const StateWrapper = 0 as *const StateWrapper;
// static ONCE: Once = ONCE_INIT;
//
// unsafe {
// ONCE.call_once(|| {
// // Make it
// let singleton = StateWrapper {
// state: Arc::new(Mutex::new(State::new())),
// };
//
// // Put it in the heap so it can outlive this call
// SINGLETON = mem::transmute(Box::new(singleton));
// });
//
// // Now we give out a copy of the data that is safe to use concurrently.
// (*SINGLETON).clone()
// }
//}

View File

@ -1,4 +0,0 @@
mod context;
pub mod commands;
pub use self::context::{Context};

View File

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

View File

@ -1,53 +0,0 @@
#[cfg(not(windows))]
use crossterm_state::commands::unix_command::EnableRawModeCommand;
#[cfg(windows)]
use crossterm_state::commands::win_commands::EnableRawModeCommand;
use { Construct, Context };
use crossterm_state::commands::{ICommand, IContextCommand};
use std::io::{ self, Write};
pub struct RawTerminal<'a, W: Write>
{
output: W,
key: i16,
context: &'a mut Context
}
impl<'a, W: Write> Drop for RawTerminal<'a,W> {
fn drop(&mut self) {
self.context.undo_state(self.key);
}
}
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
{
fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result<RawTerminal<Self>>
{
let (mut command, key) = EnableRawModeCommand::new(context);
let success = command.execute();
if success
{
Ok(RawTerminal {output: self, key: key, 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()
}
}

View File

@ -1,3 +1,6 @@
//! 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 Construct;
use shared::functions; use shared::functions;
use super::ITerminalCursor; use super::ITerminalCursor;

View File

@ -1,6 +1,8 @@
//! With this module you can perform actions that are cursor related. //! With this module you can perform actions that are cursor related.
//! Like changing and displaying the position of the cursor in terminal. //! 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 super::*;
use shared::functions; use shared::functions;
use {Construct, Context}; use {Construct, Context};
@ -11,22 +13,19 @@ use std::ops::Drop;
/// Struct that stores an specific platform implementation for cursor related actions. /// Struct that stores an specific platform implementation for cursor related actions.
pub struct TerminalCursor { pub struct TerminalCursor {
terminal_cursor: Option<Box<ITerminalCursor>>, terminal_cursor: Option<Box<ITerminalCursor>>,
context: Context
} }
impl TerminalCursor impl TerminalCursor
{ {
/// Create new cursor instance whereon cursor related actions can be performed. /// Create new cursor instance whereon cursor related actions can be performed.
pub fn new() -> TerminalCursor { pub fn new() -> TerminalCursor {
let mut context = Context::new();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let cursor = get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new(), &mut context); let cursor = functions::get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new());
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let cursor = Some(AnsiCursor::new() as Box<ITerminalCursor>); let cursor = Some(AnsiCursor::new() as Box<ITerminalCursor>);
TerminalCursor { terminal_cursor: cursor, context: context } TerminalCursor { terminal_cursor: cursor }
} }
/// Goto some position (x,y) in the terminal. /// Goto some position (x,y) in the terminal.
@ -37,9 +36,9 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// crossterm_cursor::cursor().goto(10,10); /// cursor::cursor().goto(10,10);
/// ///
/// ``` /// ```
pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor { pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor {
@ -57,9 +56,9 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// let pos = crossterm_cursor::cursor().pos(); /// let pos = cursor::cursor().pos();
/// println!("{:?}", pos); /// println!("{:?}", pos);
/// ///
/// ``` /// ```
@ -79,13 +78,13 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// // Move 1 time up /// // Move 1 time up
/// crossterm_cursor::cursor().move_up(1); /// cursor::cursor().move_up(1);
/// ///
/// // Move 2 times up /// // Move 2 times up
/// crossterm_cursor::cursor().move_up(2); /// cursor::cursor().move_up(2);
/// ///
/// ``` /// ```
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor {
@ -103,14 +102,14 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// ///
/// // move 1 time right /// // move 1 time right
/// crossterm_cursor::cursor().move_right(1); /// cursor::cursor().move_right(1);
/// ///
/// // move 2 times right /// // move 2 times right
/// crossterm_cursor::cursor().move_right(2); /// cursor::cursor().move_right(2);
/// ///
/// ``` /// ```
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor {
@ -128,13 +127,13 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// // move 1 time down /// // move 1 time down
/// crossterm_cursor::cursor().move_down(1); /// cursor::cursor().move_down(1);
/// ///
/// // move 2 times down /// // move 2 times down
/// crossterm_cursor::cursor().move_down(2); /// cursor::cursor().move_down(2);
/// ///
/// ``` /// ```
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor {
@ -152,13 +151,13 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// // move 1 time left /// // move 1 time left
/// crossterm_cursor::cursor().move_left(1); /// cursor::cursor().move_left(1);
/// ///
/// // move 2 time left /// // move 2 time left
/// crossterm_cursor::cursor().move_left(2); /// cursor::cursor().move_left(2);
/// ///
/// ``` /// ```
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor {
@ -185,17 +184,17 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// use std; /// use std;
/// use std::io::Write; /// use std::io::Write;
/// ///
/// // of course we can just do this. /// // of course we can just do this.
/// crossterm_cursor::cursor().goto(10,10); /// cursor::cursor().goto(10,10);
/// print!("@"); /// print!("@");
/// std::io::stdout().flush(); /// std::io::stdout().flush();
/// ///
/// // but now we can chain the methods so it looks cleaner and it automatically flushes the buffer. /// // but now we can chain the methods so it looks cleaner and it automatically flushes the buffer.
/// crossterm_cursor::cursor() /// cursor::cursor()
/// .goto(10,10) /// .goto(10,10)
/// .print("@"); /// .print("@");
/// ///
@ -219,9 +218,9 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor::cursor; /// use self::crossterm::cursor;
/// ///
/// cursor().safe_position(); /// cursor::cursor().safe_position();
/// ///
/// ``` /// ```
pub fn save_position(&mut self) pub fn save_position(&mut self)
@ -241,7 +240,7 @@ impl TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor::cursor; /// use self::crossterm::cursor::cursor;
/// ///
/// cursor().reset_position(); /// cursor().reset_position();
/// ///
@ -254,14 +253,6 @@ impl TerminalCursor
} }
} }
impl Drop for TerminalCursor
{
fn drop(&mut self)
{
self.context.restore_changes();
}
}
/// Get an TerminalCursor implementation whereon cursor related actions can be performed. /// Get an TerminalCursor implementation whereon cursor related actions can be performed.
/// ///
/// Check `/examples/cursor` in the libary for more spesific examples. /// Check `/examples/cursor` in the libary for more spesific examples.
@ -272,14 +263,14 @@ impl Drop for TerminalCursor
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_cursor; /// use self::crossterm::cursor;
/// ///
/// // Get cursor and goto pos X: 5, Y: 10 /// // Get cursor and goto pos X: 5, Y: 10
/// let mut cursor = crossterm_cursor::cursor(); /// let mut cursor = cursor::cursor();
/// cursor.goto(5,10); /// cursor.goto(5,10);
/// ///
/// //Or you can do it in one line. /// //Or you can do it in one line.
/// crossterm_cursor::cursor().goto(5,10); /// cursor::cursor().goto(5,10);
/// ///
/// ``` /// ```
pub fn cursor() -> Box<TerminalCursor> { pub fn cursor() -> Box<TerminalCursor> {

View File

@ -1,10 +1,10 @@
//! This trait defines the actions that can be preformed with the termial cursor. //! This trait defines the actions that can be preformed with the terminal cursor.
//! This trait can be inplemented so that an concrete inplementation of the ITerminalCursor can forfill //! This trait can be implemented so that an concrete implementation of the ITerminalCursor can for fill
//! the wishes to work on an specific platform. //! the wishes to work on an specific platform.
//! //!
//! ## For example: //! ## For example:
//! //!
//! This trait is inplemented for winapi (Windows specific) and ansi (Unix specific), //! 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. //! so that the cursor related actions can be preformed on both unix and windows systems.
//! //!
@ -19,6 +19,14 @@ use self::ansi_cursor::AnsiCursor;
pub use self::cursor::{ cursor, TerminalCursor }; 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 { pub trait ITerminalCursor {
/// Goto some location (x,y) in the terminal. /// Goto some location (x,y) in the terminal.
fn goto(&self, x: u16, y: u16); fn goto(&self, x: u16, y: u16);

View File

@ -1,3 +1,6 @@
//! 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 Construct;
use super::ITerminalCursor; use super::ITerminalCursor;
use kernel::windows_kernel::{kernel, cursor}; use kernel::windows_kernel::{kernel, cursor};
@ -13,12 +16,10 @@ impl Construct for WinApiCursor {
impl ITerminalCursor for WinApiCursor { impl ITerminalCursor for WinApiCursor {
/// Set the current cursor position to X and Y
fn goto(&self, x: u16, y: u16) { fn goto(&self, x: u16, y: u16) {
kernel::set_console_cursor_position(x as i16, y as i16); kernel::set_console_cursor_position(x as i16, y as i16);
} }
/// Get current cursor position (X,Y)
fn pos(&self) -> (u16, u16) { fn pos(&self) -> (u16, u16) {
cursor::pos() cursor::pos()
} }

View File

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

View File

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

View File

@ -1,13 +1,15 @@
//! This module contains all `unix` specific terminal related logic.
use { libc, Context }; use { libc, Context };
use termios::Termios; use termios::Termios;
pub use self::libc::{termios}; pub use self::libc::{termios};
use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort, ioctl, c_int}; use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort, ioctl, c_int};
use crossterm_state::commands::{ NoncanonicalModeCommand, IContextCommand} ; use state::commands::{ NoncanonicalModeCommand, IContextCommand} ;
use std::{ io, mem }; use std::{ io, mem };
use std::io::Error; use std::io::Error;
/// A representation of the size of the current terminal /// A representation of the size of the current terminal.
#[repr(C)] #[repr(C)]
#[derive(Debug)] #[derive(Debug)]
pub struct UnixSize { pub struct UnixSize {
@ -19,7 +21,7 @@ pub struct UnixSize {
y: c_ushort, y: c_ushort,
} }
/// Gets the current terminal size /// Get the current terminal size.
pub fn terminal_size() -> (u16,u16) { pub fn terminal_size() -> (u16,u16) {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize { let us = UnixSize {
@ -37,7 +39,7 @@ pub fn terminal_size() -> (u16,u16) {
} }
} }
/// Get the current cursor position /// Get the current cursor position.
pub fn pos() -> (u16,u16) pub fn pos() -> (u16,u16)
{ {
use std::io::Error; use std::io::Error;
@ -100,6 +102,7 @@ pub fn pos() -> (u16,u16)
} }
} }
/// Set the terminal mode to the given mode.
pub fn set_terminal_mode(termios: &Termios) -> io::Result<()> pub fn set_terminal_mode(termios: &Termios) -> io::Result<()>
{ {
extern "C" { extern "C" {
@ -108,6 +111,7 @@ pub fn set_terminal_mode(termios: &Termios) -> io::Result<()>
is_true(unsafe { tcsetattr(0, 0, termios) }).and(Ok(())) 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) { pub fn make_raw(termios: &mut Termios) {
extern "C" { extern "C" {
pub fn cfmakeraw(termptr: *mut Termios); pub fn cfmakeraw(termptr: *mut Termios);
@ -115,6 +119,7 @@ pub fn make_raw(termios: &mut Termios) {
unsafe { cfmakeraw(termios) } unsafe { cfmakeraw(termios) }
} }
/// Get the current terminal mode.
pub fn get_terminal_mode() -> io::Result<Termios> pub fn get_terminal_mode() -> io::Result<Termios>
{ {
extern "C" { extern "C" {
@ -127,6 +132,7 @@ pub fn get_terminal_mode() -> io::Result<Termios>
} }
} }
/// Is the return value true?
fn is_true(value: i32) -> Result<(), Error> fn is_true(value: i32) -> Result<(), Error>
{ {
match value match value

View File

@ -1,15 +1,17 @@
use { Context, Contstruct }; //! This module handles the enabling `ANSI escape codes` for windows terminals.
use crossterm_state::commands::IContextCommand;
use Context;
use state::commands::ICommand;
static mut HAS_BEEN_TRYED_TO_ENABLE: bool = false; static mut HAS_BEEN_TRYED_TO_ENABLE: bool = false;
static mut IS_ANSI_ON_WINDOWS_ENABLED: Option<bool> = None; static mut IS_ANSI_ON_WINDOWS_ENABLED: Option<bool> = None;
static mut DOES_WINDOWS_SUPPORT_ANSI: Option<bool> = None; static mut DOES_WINDOWS_SUPPORT_ANSI: Option<bool> = None;
/// Try enable ANSI escape codes and return the result. /// Try enable `ANSI escape codes` and return the result.
pub fn try_enable_ansi_support(context: &mut Context) -> bool pub fn try_enable_ansi_support() -> bool
{ {
use crossterm_state::commands::win_commands::EnableAnsiCommand; use state::commands::win_commands::EnableAnsiCommand;
let (mut command, key) = EnableAnsiCommand::new(context); let mut command = EnableAnsiCommand::new();
let success = command.execute(); let success = command.execute();
set_is_windows_ansi_supportable(success); set_is_windows_ansi_supportable(success);

View File

@ -1,4 +1,6 @@
use super::{ kernel, cursor }; //! 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. /// This stores the cursor pos, at program level. So it can be recalled later.
static mut SAVED_CURSOR_POS:(u16,u16) = (0,0); static mut SAVED_CURSOR_POS:(u16,u16) = (0,0);

View File

@ -1,3 +1,5 @@
//! 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::winnt::HANDLE;
use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE }; use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE };
use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::handleapi::INVALID_HANDLE_VALUE;

View File

@ -1,3 +1,5 @@
//! This module contains the `windows` specific (unsafe) logic.
pub mod kernel; pub mod kernel;
pub mod cursor; pub mod cursor;
pub mod terminal; pub mod terminal;

View File

@ -1,14 +1,19 @@
//! 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] #[macro_use]
mod shared; mod shared;
mod kernel; mod kernel;
mod crossterm_state; mod state;
pub mod crossterm_cursor; pub mod cursor;
pub mod crossterm_style; pub mod style;
pub mod crossterm_terminal; pub mod terminal;
use shared::traits::{Construct}; use shared::traits::{Construct};
pub use crossterm_state::{ Context}; pub use state::{ Context};
#[cfg(windows)] #[cfg(windows)]
extern crate winapi; extern crate winapi;
@ -18,14 +23,3 @@ extern crate libc;
extern crate termios; extern crate termios;
extern crate rand; extern crate rand;
// private mod
//
// public mod
//
// own usings
//
// std usings
//
// extern crate

View File

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

View File

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

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

@ -1,7 +1,10 @@
//! This module contains the commands that can be used for both unix and windows systems.
use super::ICommand; use super::ICommand;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
/// This command is used for switching to alternate screen and back to main screen.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand; pub struct ToAlternateScreenBufferCommand;

View File

@ -1,10 +1,13 @@
use crossterm_state::{Context}; //! This module contains the commands that can be used for unix systems.
use Context;
use super::IContextCommand; use super::IContextCommand;
use kernel::unix_kernel::terminal; use kernel::unix_kernel::terminal;
use termios::{Termios, tcsetattr, TCSAFLUSH, ICANON, ECHO, CREAD}; use termios::{Termios, tcsetattr, TCSAFLUSH, ICANON, ECHO, CREAD};
const FD_STDIN: ::std::os::unix::io::RawFd = 1; const FD_STDIN: ::std::os::unix::io::RawFd = 1;
/// This command is used for switching to NoncanonicalMode.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct NoncanonicalModeCommand pub struct NoncanonicalModeCommand
{ {
@ -64,6 +67,7 @@ impl IContextCommand for NoncanonicalModeCommand
} }
} }
/// This command is used for enabling and disabling raw mode for the terminal.
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct EnableRawModeCommand pub struct EnableRawModeCommand
{ {

View File

@ -1,29 +1,29 @@
//! This module contains the commands that can be used for windows systems.
use super::{ICommand, IContextCommand}; use super::{ICommand, IContextCommand};
use super::super::Context; use super::super::Context;
use kernel::windows_kernel::{kernel, ansi_support}; use kernel::windows_kernel::{kernel, ansi_support};
use winapi::um::winnt::{HANDLE};
use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::DWORD;
use winapi::um::wincon; use winapi::um::wincon;
use winapi::um::wincon::{ENABLE_VIRTUAL_TERMINAL_PROCESSING ,SMALL_RECT, COORD, CHAR_INFO}; use winapi::um::wincon::{ENABLE_VIRTUAL_TERMINAL_PROCESSING ,SMALL_RECT, COORD, CHAR_INFO};
use std::mem; use std::mem;
use std::fmt::Write;
/// check https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences for more info. /// 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)] #[derive(Clone, Copy)]
pub struct EnableAnsiCommand pub struct EnableAnsiCommand
{ {
mask: DWORD, mask: DWORD,
key: i16
} }
impl IContextCommand for EnableAnsiCommand impl ICommand for EnableAnsiCommand
{ {
fn new(context: &mut Context) -> (Box<EnableAnsiCommand>, i16) { fn new() -> Box<EnableAnsiCommand> {
// println!("new EnableRawModeCommand winapi"); // println!("new EnableRawModeCommand winapi");
let key = super::generate_key(); let key = super::generate_key();
let command = EnableAnsiCommand { mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING, key: key }; let command = EnableAnsiCommand { mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING };
context.register_change(Box::from(command), key); Box::from(command)
(Box::from(command),key)
} }
fn execute(&mut self) -> bool fn execute(&mut self) -> bool
@ -80,6 +80,8 @@ impl IContextCommand for EnableAnsiCommand
} }
} }
/// 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)] #[derive(Clone, Copy)]
pub struct EnableRawModeCommand pub struct EnableRawModeCommand
{ {
@ -144,6 +146,7 @@ impl IContextCommand for EnableRawModeCommand
} }
} }
/// 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 /// check https://docs.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes for more info
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand; pub struct ToAlternateScreenBufferCommand;

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,3 +1,6 @@
//! 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 Construct;
use super::ITerminalColor; use super::ITerminalColor;
use super::super::{Color, ColorType}; use super::super::{Color, ColorType};

View File

@ -1,9 +1,10 @@
//! With this module you can perform actions that are color related. //! 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 super::*; use super::*;
use shared::functions; use shared::functions;
use { Construct, Context }; use { Construct, Context };
use crossterm_style::{Color, ObjectStyle, StyledObject}; use style::{Color, ObjectStyle, StyledObject};
use std::ops::Drop; use std::ops::Drop;
use std::{ fmt, io }; use std::{ fmt, io };
@ -11,21 +12,18 @@ use std::{ fmt, io };
/// Struct that stores an specific platform implementation for color related actions. /// Struct that stores an specific platform implementation for color related actions.
pub struct TerminalColor { pub struct TerminalColor {
terminal_color: Option<Box<ITerminalColor>>, terminal_color: Option<Box<ITerminalColor>>,
context: Context
} }
impl TerminalColor { impl TerminalColor {
/// Create new instance whereon color related actions can be performed. /// Create new instance whereon color related actions can be performed.
pub fn new() -> TerminalColor { pub fn new() -> TerminalColor {
let mut context = Context::new();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let color = functions::get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new(), &mut context); let color = functions::get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new());
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let color = Some(AnsiColor::new() as Box<ITerminalColor>); let color = Some(AnsiColor::new() as Box<ITerminalColor>);
TerminalColor { terminal_color: color, context: context} TerminalColor { terminal_color: color }
} }
/// Set the foreground color to the given color. /// Set the foreground color to the given color.
@ -35,7 +33,7 @@ impl TerminalColor {
/// ```rust /// ```rust
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_style::{ color, Color}; /// use self::crossterm::style::{ color, Color};
/// ///
/// // Get colored terminal instance /// // Get colored terminal instance
/// let mut colored_terminal = color(); /// let mut colored_terminal = color();
@ -60,7 +58,7 @@ impl TerminalColor {
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_style::{ color, Color}; /// use self::crossterm::style::{ color, Color};
/// ///
/// // Get colored terminal instance /// // Get colored terminal instance
/// let mut colored_terminal = color(); /// let mut colored_terminal = color();
@ -83,7 +81,7 @@ impl TerminalColor {
/// ```rust /// ```rust
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_style::color; /// use self::crossterm::style::color;
/// ///
/// // Get colored terminal instance /// // Get colored terminal instance
/// let mut colored_terminal = color(); /// let mut colored_terminal = color();
@ -115,14 +113,6 @@ impl TerminalColor {
} }
} }
impl Drop for TerminalColor
{
fn drop(&mut self)
{
self.context.restore_changes();
}
}
/// Get an TerminalColor implementation whereon color related actions can be performed. /// Get an TerminalColor implementation whereon color related actions can be performed.
/// ///
/// # Example /// # Example
@ -130,7 +120,7 @@ impl Drop for TerminalColor
/// ```rust /// ```rust
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_style::{color, Color}; /// use self::crossterm::style::{color, Color};
/// ///
/// // Get colored terminal instance /// // Get colored terminal instance
/// let mut colored_terminal = color(); /// let mut colored_terminal = color();
@ -153,7 +143,7 @@ pub fn color() -> Box<TerminalColor> {
/// ```rust /// ```rust
/// extern crate crossterm; /// extern crate crossterm;
/// ///
/// use self::crossterm::crossterm_style::{paint,Color}; /// use self::crossterm::style::{paint,Color};
/// ///
/// fn main() /// fn main()
/// { /// {

View File

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

View File

@ -18,10 +18,8 @@ impl Construct for WinApiColor {
} }
} }
impl ITerminalColor for WinApiColor { impl ITerminalColor for WinApiColor {
/// This will set the foreground color by the given winapi color.
fn set_fg(&self, fg_color: Color) { fn set_fg(&self, fg_color: Color) {
let color_value = &self.color_value(fg_color, ColorType::Foreground); let color_value = &self.color_value(fg_color, ColorType::Foreground);
@ -34,7 +32,7 @@ impl ITerminalColor for WinApiColor {
let bg_color = attrs & 0x0070; let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | bg_color; color = color_value.parse::<u16>().unwrap() | bg_color;
// background intensity is a seperate value in attrs, // background intensity is a separate value in attrs,
// wee need to check if this was applied to the current bg color. // wee need to check if this was applied to the current bg color.
if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 { if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 {
color = color | wincon::BACKGROUND_INTENSITY as u16; color = color | wincon::BACKGROUND_INTENSITY as u16;
@ -43,7 +41,6 @@ impl ITerminalColor for WinApiColor {
kernel::set_console_text_attribute(color); kernel::set_console_text_attribute(color);
} }
/// This will set the background color by the given winapi color value.
fn set_bg(&self, bg_color: Color) { fn set_bg(&self, bg_color: Color) {
let color_value = &self.color_value(bg_color, ColorType::Background); let color_value = &self.color_value(bg_color, ColorType::Background);
@ -64,7 +61,6 @@ impl ITerminalColor for WinApiColor {
kernel::set_console_text_attribute(color); kernel::set_console_text_attribute(color);
} }
/// This will reset the colors to the given winapi color value.
fn reset(&self) { fn reset(&self) {
kernel::set_console_text_attribute(self.original_console_color); kernel::set_console_text_attribute(self.original_console_color);
} }
@ -72,7 +68,7 @@ impl ITerminalColor for WinApiColor {
/// This will get the winapi color value from the Color and ColorType struct /// This will get the winapi color value from the Color and ColorType struct
fn color_value(&self, color: Color, color_type: ColorType) -> String { fn color_value(&self, color: Color, color_type: ColorType) -> String {
use crossterm_style::{Color, ColorType}; use style::{Color, ColorType};
let winapi_color: u16; let winapi_color: u16;

View File

@ -1,3 +1,6 @@
//! 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 color;
mod styles; mod styles;

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,5 +1,7 @@
//! This module contains the `object style` that can be applied to an `styled object`.
use std::fmt::Display; use std::fmt::Display;
use crossterm_style::{Color, StyledObject}; use style::{Color, StyledObject};
#[cfg(unix)] #[cfg(unix)]
use super::super::Attribute; use super::super::Attribute;

View File

@ -1,12 +1,14 @@
use std::{ fmt, self }; //! 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; use std::io::Write;
#[cfg(unix)] #[cfg(unix)]
use super::super::Attribute; 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 struct StyledObject<D> {
pub object_style: ObjectStyle, pub object_style: ObjectStyle,
pub content: D, pub content: D,
@ -19,7 +21,7 @@ impl<D> StyledObject<D> {
/// ///
/// ```rust /// ```rust
/// extern crate crossterm; /// 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. /// // create an styled object with the foreground color red.
/// let styledobject = paint("I am colored red").with(Color::Red); /// let styledobject = paint("I am colored red").with(Color::Red);
@ -44,7 +46,7 @@ impl<D> StyledObject<D> {
/// ///
/// ```rust /// ```rust
/// extern crate crossterm; /// 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. /// // create an styled object with the background color red.
/// let styledobject = paint("I am colored red").on(Color::Red); /// let styledobject = paint("I am colored red").on(Color::Red);
@ -70,7 +72,7 @@ impl<D> StyledObject<D> {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use self::crossterm::crossterm_style::{paint,Attribute}; /// use self::crossterm::style::{paint,Attribute};
/// ///
/// println!("{}", paint("Bold").attr(Attribute::Bold)); /// println!("{}", paint("Bold").attr(Attribute::Bold));
/// ///

View File

@ -1,3 +1,6 @@
//! 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 Construct;
use shared::functions; use shared::functions;
use super::{ClearType, ITerminal}; use super::{ClearType, ITerminal};

View File

@ -1,4 +1,10 @@
mod raw_terminal; //! 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; mod terminal;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -6,13 +12,13 @@ mod winapi_terminal;
mod ansi_terminal; mod ansi_terminal;
pub mod screen; pub mod screen;
pub mod raw;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use self::winapi_terminal::WinApiTerminal; use self::winapi_terminal::WinApiTerminal;
use self::ansi_terminal::AnsiTerminal; use self::ansi_terminal::AnsiTerminal;
pub use self::terminal::{ Terminal, terminal}; pub use self::terminal::{ Terminal, terminal};
pub use self::raw_terminal::{RawTerminal, IntoRawMode};
/// Enum that can be used for the kind of clearing that can be done in the terminal. /// Enum that can be used for the kind of clearing that can be done in the terminal.
pub enum ClearType { pub enum ClearType {
@ -23,7 +29,15 @@ pub enum ClearType {
UntilNewLine, UntilNewLine,
} }
pub trait ITerminal{ ///! 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 /// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType); fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y) /// Get the terminal size (x,y)

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

View File

@ -1,23 +1,12 @@
//! This module contains all the logic for switching between alternate screen and main screen.
use shared::functions; use shared::functions;
use { Context, Construct }; use Context;
use crossterm_state::commands::*; use state::commands::*;
use std::{ fmt, ops }; use std::{ fmt, ops };
use std::io::{self, Write}; use std::io::{self, Write};
/// let context = ScreenContext::new();
/// ToMainScreen {}.execute(&mut context);
/// ToAlternateScreen {}.execute(context);
///
///
/// ToMainScreen {}.execute(&mut context);
///
/// context.to_main();
/// let alternate_screen = context.to_alternate(stdout());
///
/// let alternate = AlternateScreen::from(stdout, context);
/// ToMainScreen [} .execute(ScreenContext::new()))
/// ToAlternateScreen {}. execute(ScreenContext::new());
pub struct ToMainScreen; pub struct ToMainScreen;
impl fmt::Display for ToMainScreen impl fmt::Display for ToMainScreen
@ -28,7 +17,7 @@ impl fmt::Display for ToMainScreen
} }
} }
/// Switch to the alternate screen buffer of the terminal. /// Struct that switches to alternate screen buffer on display.
pub struct ToAlternateScreen; pub struct ToAlternateScreen;
impl fmt::Display for ToAlternateScreen impl fmt::Display for ToAlternateScreen
@ -39,13 +28,36 @@ impl fmt::Display for ToAlternateScreen
} }
} }
/// 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> { pub struct AlternateScreen<W: Write> {
/// The output target. /// The output target.
output: W, output: W,
context: Context context: Context
} }
impl<W: Write> AlternateScreen< W> { impl<W: Write> AlternateScreen<W> {
pub fn from(mut output: W) -> Self { pub fn from(mut output: W) -> Self {
write!(output, "{}", ToAlternateScreen); write!(output, "{}", ToAlternateScreen);
AlternateScreen { output: output, context: Context::new()} AlternateScreen { output: output, context: Context::new()}
@ -84,12 +96,11 @@ impl<W: Write> Drop for AlternateScreen<W>
} }
} }
/// Get the alternate screen command to enable and disable alternate screen based on the current platform
fn get_to_alternate_screen_command() -> Box<ICommand> fn get_to_alternate_screen_command() -> Box<ICommand>
{ {
let mut context = Context::new();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let command = functions::get_module::<Box<ICommand>>(win_commands::ToAlternateScreenBufferCommand::new(), shared_commands::ToAlternateScreenBufferCommand::new(), &mut context).unwrap(); let command = functions::get_module::<Box<ICommand>>(win_commands::ToAlternateScreenBufferCommand::new(), shared_commands::ToAlternateScreenBufferCommand::new()).unwrap();
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let command = shared_commands::ToAlternateScreenBufferCommand::new(); let command = shared_commands::ToAlternateScreenBufferCommand::new();

View File

@ -1,5 +1,6 @@
//! With this module you can perform actions that are terminal related. //! 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. //! Like clearing and scrolling in the terminal or getting the size of the terminal.
use super::*; use super::*;
use shared::functions; use shared::functions;
use {Construct, Context}; use {Construct, Context};
@ -9,21 +10,18 @@ use std::ops::Drop;
/// Struct that stores an specific platform implementation for terminal related actions. /// Struct that stores an specific platform implementation for terminal related actions.
pub struct Terminal { pub struct Terminal {
terminal: Option<Box<ITerminal>>, terminal: Option<Box<ITerminal>>,
context: Context
} }
impl Terminal { impl Terminal {
/// Create new terminal instance whereon terminal related actions can be performed. /// Create new terminal instance whereon terminal related actions can be performed.
pub fn new() -> Terminal { pub fn new() -> Terminal {
let mut context = Context::new();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
let terminal = functions::get_module::<Box<ITerminal>>(WinApiTerminal::new(), AnsiTerminal::new(), &mut context); let terminal = functions::get_module::<Box<ITerminal>>(WinApiTerminal::new(), AnsiTerminal::new());
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let terminal = Some(AnsiTerminal::new() as Box<ITerminal>); let terminal = Some(AnsiTerminal::new() as Box<ITerminal>);
Terminal { terminal: terminal, context: context } Terminal { terminal: terminal }
} }
/// Clear the current cursor by specifying the clear type /// Clear the current cursor by specifying the clear type
@ -33,20 +31,20 @@ impl Terminal {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// // clear all cells in 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. /// // 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. /// // 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. /// // 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. /// // 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) { pub fn clear(&mut self, clear_type: ClearType) {
@ -62,9 +60,9 @@ impl Terminal {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// let size = term.terminal_size(); /// let size = term.terminal_size();
/// println!("{:?}", size); /// println!("{:?}", size);
@ -84,9 +82,9 @@ impl Terminal {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// // scroll up by 5 lines /// // scroll up by 5 lines
/// let size = term.scroll_up(5); /// let size = term.scroll_up(5);
@ -105,9 +103,9 @@ impl Terminal {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// // scroll down by 5 lines /// // scroll down by 5 lines
/// let size = term.scroll_down(5); /// let size = term.scroll_down(5);
@ -126,9 +124,9 @@ impl Terminal {
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// // Set of the size to X: 10 and Y: 10 /// // Set of the size to X: 10 and Y: 10
/// let size = term.set_size(10,10); /// let size = term.set_size(10,10);
@ -142,14 +140,6 @@ impl Terminal {
} }
} }
impl Drop for Terminal
{
fn drop(&mut self)
{
self.context.restore_changes();
}
}
/// Get an Terminal implementation whereon terminal related actions can be performed. /// Get an Terminal implementation whereon terminal related actions can be performed.
/// ///
/// Check `/examples/terminal` in the libary for more spesific examples. /// Check `/examples/terminal` in the libary for more spesific examples.
@ -159,9 +149,9 @@ impl Drop for Terminal
/// ```rust /// ```rust
/// ///
/// extern crate crossterm; /// extern crate crossterm;
/// use crossterm::crossterm_terminal; /// use crossterm::terminal;
/// ///
/// let mut term = crossterm_terminal::terminal(); /// let mut term = terminal::terminal();
/// ///
/// // scroll down by 5 lines /// // scroll down by 5 lines
/// let size = term.scroll_down(5); /// let size = term.scroll_down(5);

View File

@ -1,6 +1,9 @@
//! 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 {Construct};
use crossterm_cursor::cursor; use cursor::cursor;
use super::base_terminal::{ClearType, ITerminal}; use super::{ClearType, ITerminal};
use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,}; use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,};
use kernel::windows_kernel::{kernel, terminal}; use kernel::windows_kernel::{kernel, terminal};
@ -14,7 +17,7 @@ impl Construct for WinApiTerminal {
} }
impl ITerminal for WinApiTerminal { impl ITerminal for WinApiTerminal {
/// Clear the screen to the given cleartype.
fn clear(&self, clear_type: ClearType) { fn clear(&self, clear_type: ClearType) {
let csbi = kernel::get_console_screen_buffer_info(); let csbi = kernel::get_console_screen_buffer_info();
let pos = cursor().pos(); let pos = cursor().pos();
@ -29,17 +32,14 @@ impl ITerminal for WinApiTerminal {
}; };
} }
/// Get the terminal size
fn terminal_size(&self) -> (u16, u16) { fn terminal_size(&self) -> (u16, u16) {
terminal::terminal_size() terminal::terminal_size()
} }
/// Scroll up n` rows
fn scroll_up(&self, count: i16) { fn scroll_up(&self, count: i16) {
// yet to be inplemented // yet to be inplemented
} }
/// Scroll down `n` rows
fn scroll_down(&self, count: i16) { fn scroll_down(&self, count: i16) {
let csbi = kernel::get_console_screen_buffer_info(); let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window; let mut srct_window;