Alternate screen working for windows, created reallife example with firstdepthsearch and started commenting all types not done yet with that.

This commit is contained in:
TimonPost 2018-07-08 22:13:32 +02:00
parent 26a1960159
commit e62d8cff9d
36 changed files with 1214 additions and 709 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ readme = "README.md"
[dependencies] [dependencies]
rand = "0.4.2" rand = "0.4.2"
lazy_static = "1.0.1"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] } winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }

View File

@ -18,14 +18,10 @@ mod cursor;
mod program_examples; mod program_examples;
mod terminal; mod terminal;
fn main() {
use crossterm::Context; use crossterm::Context;
use program_examples::first_depth_search;
{ fn main() {
let mut context = Context::new(); first_depth_search::run();
// println!("End")
terminal::alternate_screen::print_wait_screen_on_alternate_window(context.clone());
println!("count: {}", std::rc::Rc::strong_count(&context));
}
} }

View File

@ -0,0 +1,156 @@
//! Implementation of the first depth search algorithm
use super::variables::{Direction, Position};
use super::messages::END_MESSAGE;
use super::map::Map;
use crossterm::style::Color;
use crossterm::Crossterm;
use super::rand;
use super::rand::distributions::{IndependentSample, Range};
use std::io::{stdout, Write};
use std::{thread, time};
pub struct FirstDepthSearch<'crossterm>
{
direction: Direction,
map: Map,
stack: Vec<Position>,
root_pos: Position,
is_terminated: bool,
crossterm: &'crossterm Crossterm
}
impl<'crossterm> FirstDepthSearch<'crossterm>
{
pub fn new(map: Map, start_pos: Position, crossterm: &'crossterm Crossterm) -> FirstDepthSearch<'crossterm>
{
FirstDepthSearch
{
direction: Direction::Up,
map: map,
stack: Vec::new(),
root_pos: start_pos,
is_terminated: false,
crossterm: crossterm,
}
}
pub fn start(&mut self)
{
self.is_terminated = false;
// push first position on the stack
self.stack.push(self.root_pos);
let mut cursor = self.crossterm.cursor();
cursor.hide();
// loop until there are now items left in the stack.
loop {
if self.stack.len() == 0
{
break;
}
self.choose_random_neighbor();
if self.is_terminated
{
break;
}
self.update_position();
let cell = self.crossterm.paint(" ").on(Color::Blue);
let pos = self.root_pos.clone();
let x = pos.x as u16;
let y = pos.y as u16;
cursor.goto(x,y).print(cell);
::std::io::stdout().flush();
thread::sleep(time::Duration::from_millis(2));
}
}
/// With this function we are choosing an random neighbor that we havent visited yet.
fn choose_random_neighbor(&mut self)
{
let mut available_directions: Vec<Direction> = Vec::with_capacity(4);
// check every direction if the direction is not visited we can add it to the list.
// note that if the y or x is 0 that we don't want to subtract because we get an subtract overflow.
if self.root_pos.y != 0 && !self.map.is_cell_visited(self.root_pos.x, self.root_pos.y - 1)
{
available_directions.push(Direction::Up)
}
if !&self.map.is_cell_visited(self.root_pos.x, self.root_pos.y + 1)
{
available_directions.push(Direction::Down)
}
if self.root_pos.x != 0 && !self.map.is_cell_visited(self.root_pos.x - 1, self.root_pos.y)
{
available_directions.push(Direction::Left)
}
if !&self.map.is_cell_visited(self.root_pos.x + 1, self.root_pos.y)
{
available_directions.push(Direction::Right)
}
let directions_count = available_directions.len();
// if there are no directions left we need to backtrack until we find directions to go to.
if directions_count != 0
{
let step = Range::new(0, directions_count);
let mut rng = rand::thread_rng();
let choice = step.ind_sample(&mut rng);
// set the current direction to the new random generated direction.
self.direction = available_directions[choice];
}
else {
self.find_first_possible_direction();
}
}
/// Find direction to go to if there is no direction pop the current position of the stack for back tracking to the previous position.
fn find_first_possible_direction(&mut self)
{
// if there are no elements left in the stack that means we have visited all cell and we van terminate the program.
if let Some(previous_cell) = &self.stack.pop()
{
// update root pos to previous cell and continue searching for new neighbours
self.root_pos = *previous_cell;
self.choose_random_neighbor();
}
else {
self.is_terminated = true;
}
}
/// update the root position to the new direction we went in
fn update_position(&mut self)
{
match self.direction
{
Direction::Up => self.root_pos.y -= 1,
Direction::Down => self.root_pos.y += 1,
Direction::Left => self.root_pos.x -= 1,
Direction::Right => self.root_pos.x += 1,
_ => panic!()
};
self.map.set_visited(self.root_pos.x, self.root_pos.y);
self.stack.push(self.root_pos);
}
}

View File

@ -1,49 +1,75 @@
use super::variables::{Cell, Position, Size }; use super::variables::{Cell, Position, Size };
use crossterm::terminal::terminal; use crossterm::terminal::terminal;
use crossterm::Environment; use crossterm::cursor::cursor;
use crossterm::style::{ObjectStyle, StyledObject}; use crossterm::Crossterm;
use crossterm::style::{ObjectStyle, StyledObject, Color};
use crossterm::Context; use crossterm::Context;
use std::rc::Rc; use std::rc::Rc;
use std::fmt::Display; use std::fmt::Display;
pub struct Map<D: Display> pub struct Map
{ {
map: Vec<Vec<Cell<D>>>, pub map: Vec<Vec<Cell>>,
wall_style: StyledObject<D>, pub size: Size,
map_style: StyledObject<D>,
} }
impl<D: Display> Map<D> impl Map
{ {
pub fn new(context: Rc<Context>) -> Map<D> pub fn new(map_size: Size, wall_cell_char: char, map_cell_char: char) -> Map
{ {
Map { map: Vec::new(), wall_style: ObjectStyle::apply_ to("", context.clone() )} let mut map: Vec<Vec<Cell>> = Vec::new();
// initialize the map shown on the screen. Each cell of terminal should have a value that could be changed by the algorithm
// create n rows with n cells.
for y in 0..map_size.height
{
let mut row: Vec<Cell> = Vec::new();
for x in 0..map_size.width
{
if (y == 0 || y == map_size.height - 1) || (x == 0 || x == map_size.width - 1)
{
row.push(Cell::new(Position::new(x, y), Color::Black, wall_cell_char, true));
} else {
row.push(Cell::new(Position::new(x, y), Color::Black, map_cell_char, false));
}
}
map.push(row);
} }
pub fn init(&self, environment: &mut Environment, map_size: Size) -> Map<D> Map { map: map, size: Size::new(map_size.width, map_size.height)}
{ }
let mut map: Vec<Vec<Cell<D>>> = Vec::new();
// render the map on the screen.
for y in 0..map[0].len() pub fn render_map(&mut self, crossterm: &mut Crossterm)
{ {
for x in 0..map[1].len() let mut cursor = crossterm.cursor();
{
if (y == 0 || y == map.len() - 1) || (x == 0 || x == map[0].len()) for row in self.map.iter_mut()
{ {
map[y][x] = Cell::new(Position::new(x,y), wall_style.apply_to(environment.context(), "")); for column in row.iter_mut()
{
// we only have to render the walls
if (column.position.y == 0 || column.position.y == self.size.height - 1) || (column.position.x == 0 || column.position.x == self.size.width - 1)
{
let cell_style = crossterm.paint(column.look).on(column.color);
cursor.goto(column.position.x as u16, column.position.y as u16)
.print(cell_style);
} }
else{
map[y][x] = Cell::new(Position::new(x,y), map_style);
} }
} }
} }
Map { map } // check if position in the map at the given coords is visted.
} pub fn is_cell_visited(&self, x: usize, y: usize) -> bool
fn render_map()
{ {
self.map[y][x].visited
}
// change an position in the map to visited.
pub fn set_visited(&mut self, x: usize, y: usize)
{
self.map[y][x].visited = true;
} }
} }

View File

@ -0,0 +1,25 @@
use super::variables::Position;
pub const WELCOME_MESSAGE: [&str; 6] =
[
"__ __ .__ __ ",
"/ \\ / \\ ____ | | | | ______ _____ ____ ",
"\\ \\/\\/ // __ \\| | | |/ / _ \\ / \\_/ __ \\ ",
" \\ /\\ ___/| |_| < <_> ) Y Y \\ ___/ ",
" \\__/\\ / \\___ >____/__|_ \\____/|__|_| /\\___ > ",
" \\/ \\/ \\/ \\/ \\/ "
];
pub const END_MESSAGE: [&str; 5] =
[
"-----------------------",
" ",
" No routes (DONE) ",
" ",
"-----------------------",
];
pub fn print_stack_count(position: Position)
{
}

View File

@ -1,17 +1,81 @@
extern crate rand;
mod map; mod map;
mod variables; mod variables;
mod messages;
mod algorithm;
use crossterm; use crossterm::Crossterm;
use crossterm::terminal::ClearType;
use crossterm::style::Color; use crossterm::style::Color;
use crossterm::screen;
use self::variables::Size; use self::variables::{Size, Position };
use self::messages::WELCOME_MESSAGE;
fn run() use std::iter::Iterator;
use std::{thread, time};
/// run the program
pub fn run()
{ {
let mut env = crossterm::Environment::new(); // create new Crossterm instance.
let map_size = Size::new(20,20); let mut crossterm = Crossterm::new();
let wall_style = env.paint("").with(Color::Blue).on(Color::Black);
let map_style = env.paint(" ").with(Color::White).on(Color::White);
map::Map::init(&mut env, map_size, wall_style, map_style); print_welcome_screen(&crossterm);
start_algorithm(&mut crossterm);
print_end_screen(&crossterm);
}
fn start_algorithm(crossterm: &mut Crossterm)
{
// we first want to switch to alternate screen. On the alternate screen we are going to run or firstdepthsearch algorithm
let alternate_screen = screen::AlternateScreen::from(crossterm.context());
// setup the map size and the position to start searching for a path.
let map_size = Size::new(100,40);
let start_pos = Position::new(10,10);
// create and render the map. Or map border is going to have an █ look and inside the map is just a space.
let mut map = map::Map::new(map_size, '█', ' ');
map.render_map(crossterm);
// create the algorithm and start the
let mut algorithm = algorithm::FirstDepthSearch::new(map, start_pos, &crossterm);
algorithm.start();
}
fn print_end_screen(crossterm: &Crossterm)
{
}
fn print_welcome_screen(crossterm: &Crossterm)
{
// create the handle for the cursor and terminal.
let mut cursor = crossterm.cursor();
let mut terminal = crossterm.terminal();
// clear the screen and print the welcome message.
terminal.clear(ClearType::All);
terminal.write(WELCOME_MESSAGE.join("\n"));
cursor.hide();
cursor.goto(0,10);
terminal.write(
"The first depth search algorithm will start in: Seconds"
);
// print some progress example.
for i in (1..5).rev() {
// print the current counter at the line of `Seconds to Go: {counter}`
cursor
.goto(48, 10)
.print(terminal.paint(format!("{}", i)).with(Color::Red).on(Color::Blue));
// 1 second delay
thread::sleep(time::Duration::from_secs(1));
}
} }

View File

@ -1,19 +1,22 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::terminal::{terminal, ClearType}; use self::crossterm::terminal::{terminal, ClearType};
use self::crossterm::Context;
use self::crossterm::style::{Color, StyledObject, ObjectStyle }; use self::crossterm::style::{Color, StyledObject, ObjectStyle };
use self::crossterm::Context;
#[derive(Copy, Clone)] use std::fmt::Debug;
use std::fmt;
#[derive(Copy, Clone,Debug)]
pub enum Direction pub enum Direction
{ {
Up, Up = 0,
Down, Down = 1,
Left, Left = 2,
Right Right = 3
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub struct Position pub struct Position
{ {
pub x: usize, pub x: usize,
@ -31,33 +34,30 @@ impl Position
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Size pub struct Size
{ {
pub width: u16, pub width: usize,
pub height: u16 pub height: usize
} }
impl Size impl Size
{ {
pub fn new(width: u16, height: u16) -> Size pub fn new(width: usize, height: usize) -> Size
{ {
Size {width,height} Size {width,height}
} }
} }
use std::fmt::Display; pub struct Cell
pub struct Cell<'a, D: Display>
{ {
position: Position, pub position: Position,
style: &'a StyledObject<D>, pub color: Color,
visited: bool pub look: char,
pub visited: bool
} }
use std::rc::Rc; impl Cell
impl<'a, D: Display> Cell<'a, D>
{ {
pub fn new(position: Position, style: &'a StyledObject<D>) -> Cell<D> pub fn new(position: Position, color: Color, look: char, visited: bool) -> Cell
{ {
Cell { position: position, style: style, visited: false } Cell { position, color, look, visited }
} }
} }

View File

@ -1 +1 @@
//pub mod first_depth_search; pub mod first_depth_search;

View File

@ -21,7 +21,7 @@ impl ITerminalCursor for AnsiCursor {
fn goto(&self, x: u16, y: u16) { fn goto(&self, x: u16, y: u16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{};{}H"), y + 1, x + 1)); screen.write_string(format!(csi!("{};{}H"), y + 1, x + 1));
} }
} }
@ -32,56 +32,56 @@ impl ITerminalCursor for AnsiCursor {
fn move_up(&self, count: u16) { fn move_up(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}A"), count)); screen.write_string(format!(csi!("{}A"), count));
} }
} }
fn move_right(&self, count: u16) { fn move_right(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}C"), count)); screen.write_string(format!(csi!("{}C"), count));
} }
} }
fn move_down(&self, count: u16) { fn move_down(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}B"), count)); screen.write_string(format!(csi!("{}B"), count));
} }
} }
fn move_left(&self, count: u16) { fn move_left(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}D"), count)); screen.write_string(format!(csi!("{}D"), count));
} }
} }
fn save_position(&mut self) { fn save_position(&self) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("s")); screen.write_str(csi!("s"));
} }
} }
fn reset_position(&self) { fn reset_position(&self) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("u")); screen.write_str(csi!("u"));
} }
} }
fn hide(&self) { fn hide(&self) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("?25l")); screen.write_str(csi!("?25l"));
} }
} }
fn show(&self) { fn show(&self) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("?25h")); screen.write_str(csi!("?25h"));
} }
} }
@ -89,9 +89,9 @@ impl ITerminalCursor for AnsiCursor {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
if blink { if blink {
screen.write_ansi_str(csi!("?12h")); screen.write_str(csi!("?12h"));
} else { } else {
screen.write_ansi_str(csi!("?12l")); screen.write_str(csi!("?12l"));
} }
} }
} }

View File

@ -14,7 +14,7 @@ use std::rc::Rc;
/// 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 {
context: Rc<Context>, context: Rc<Context>,
terminal_cursor: Option<Box<ITerminalCursor>>, terminal_cursor: Box<ITerminalCursor>,
} }
impl TerminalCursor { impl TerminalCursor {
@ -24,10 +24,10 @@ impl TerminalCursor {
let cursor = functions::get_module::<Box<ITerminalCursor>>( let cursor = functions::get_module::<Box<ITerminalCursor>>(
WinApiCursor::new(context.screen_manager.clone()), WinApiCursor::new(context.screen_manager.clone()),
AnsiCursor::new(context.clone()), AnsiCursor::new(context.clone()),
); ).unwrap();
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let cursor = Some(AnsiCursor::new(context.clone()) as Box<ITerminalCursor>); let cursor = AnsiCursor::new(context.clone()) as Box<ITerminalCursor>;
TerminalCursor { TerminalCursor {
terminal_cursor: cursor, terminal_cursor: cursor,
@ -57,9 +57,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor { pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.goto(x, y);
terminal_cursor.goto(x, y);
}
self self
} }
@ -85,11 +83,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn pos(&mut self) -> (u16, u16) { pub fn pos(&mut self) -> (u16, u16) {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.pos()
terminal_cursor.pos()
} else {
(0, 0)
}
} }
/// Move the current cursor position `n` times up. /// Move the current cursor position `n` times up.
@ -114,9 +108,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.move_up(count);
terminal_cursor.move_up(count);
}
self self
} }
@ -140,9 +132,7 @@ impl TerminalCursor {
/// } /// }
/// ``` /// ```
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.move_right(count);
terminal_cursor.move_right(count);
}
self self
} }
@ -168,9 +158,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.move_down(count);
terminal_cursor.move_down(count);
}
self self
} }
@ -196,9 +184,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor { pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.move_left(count);
terminal_cursor.move_left(count);
}
self self
} }
@ -246,10 +232,12 @@ impl TerminalCursor {
let mut mutex = &self.context.screen_manager; let mut mutex = &self.context.screen_manager;
{ {
let mut screen_manager = mutex.lock().unwrap(); // let mut screen_manager = mutex.lock().unwrap();
screen_manager.write_val(string); // screen_manager.write_string(string);
screen_manager.flush(); println!("{}",string);
// screen_manager.flush();
} }
} }
self self
@ -272,9 +260,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn save_position(&mut self) { pub fn save_position(&mut self) {
if let Some(ref mut terminal_cursor) = self.terminal_cursor { self.terminal_cursor.save_position();
terminal_cursor.save_position();
}
} }
/// Return to saved cursor position /// Return to saved cursor position
@ -294,9 +280,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn reset_position(&mut self) { pub fn reset_position(&mut self) {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.reset_position();
terminal_cursor.reset_position();
}
} }
/// Hide de cursor in the console. /// Hide de cursor in the console.
@ -314,9 +298,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn hide(&self) { pub fn hide(&self) {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.hide();
terminal_cursor.hide();
}
} }
/// Show the cursor in the console. /// Show the cursor in the console.
@ -334,9 +316,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn show(&self) { pub fn show(&self) {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.show();
terminal_cursor.show();
}
} }
/// Enable or disable blinking of the terminal. /// Enable or disable blinking of the terminal.
@ -358,9 +338,7 @@ impl TerminalCursor {
/// ///
/// ``` /// ```
pub fn blink(&self, blink: bool) { pub fn blink(&self, blink: bool) {
if let Some(ref terminal_cursor) = self.terminal_cursor { self.terminal_cursor.blink(blink);
terminal_cursor.blink(blink);
}
} }
} }

View File

@ -0,0 +1,30 @@
////! This is an WINAPI specific implementation for cursor related action.
////! This module is used for windows terminals that do not support ANSI escape codes.
////! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position ect.
//
///// This struct is an windows implementation for cursor related actions.
//pub struct DefaultCursor;
//
//impl ITerminalCursor for DefaultCursor {
// fn goto(&self, x: u16, y: u16) { }
//
// fn pos(&self) -> (u16, u16) { return (0,0) }
//
// fn move_up(&self, count: u16) { }
//
// fn move_right(&self, count: u16) { }
//
// fn move_down(&self, count: u16) { }
//
// fn move_left(&self, count: u16) { }
//
// fn save_position(&self) { }
//
// fn reset_position(&self) { }
//
// fn hide(&self) { }
//
// fn show(&self) { }
//
// fn blink(&self, blink: bool) {}
//}

View File

@ -10,6 +10,7 @@
pub mod cursor; pub mod cursor;
mod default_cursor;
mod ansi_cursor; mod ansi_cursor;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod winapi_cursor; mod winapi_cursor;
@ -44,7 +45,7 @@ pub trait ITerminalCursor {
/// Move the cursor `n` times left. /// Move the cursor `n` times left.
fn move_left(&self, count: u16); fn move_left(&self, count: u16);
/// Save cursor position so that its saved position can be recalled later. Note that this position is stored program based not per instance of the cursor struct. /// Save cursor position so that its saved position can be recalled later. Note that this position is stored program based not per instance of the cursor struct.
fn save_position(&mut self); fn save_position(&self);
/// Return to saved cursor position /// Return to saved cursor position
fn reset_position(&self); fn reset_position(&self);
/// Hide the terminal cursor. /// Hide the terminal cursor.

View File

@ -49,7 +49,7 @@ impl ITerminalCursor for WinApiCursor {
self.goto(xpos - count, ypos); self.goto(xpos - count, ypos);
} }
fn save_position(&mut self) { fn save_position(&self) {
cursor::save_cursor_pos(&self.screen_manager); cursor::save_cursor_pos(&self.screen_manager);
} }

View File

@ -19,7 +19,6 @@ pub fn try_enable_ansi_support() -> bool {
set_ansi_enabled(success); set_ansi_enabled(success);
has_been_tried_to_enable(true); has_been_tried_to_enable(true);
}); });
windows_supportable() windows_supportable()
} }

View File

@ -111,6 +111,21 @@ pub fn get_console_screen_buffer_info(
csbi csbi
} }
/// Create a new console screen buffer info struct.
pub fn get_std_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
let success;
unsafe { success = GetConsoleScreenBufferInfo(get_output_handle(), &mut csbi) }
if success == 0 {
panic!("Cannot get console screen buffer info");
}
csbi
}
pub fn get_buffer_info_and_hande(screen_manager: &Rc<Mutex<ScreenManager>>) -> (CONSOLE_SCREEN_BUFFER_INFO, HANDLE) pub fn get_buffer_info_and_hande(screen_manager: &Rc<Mutex<ScreenManager>>) -> (CONSOLE_SCREEN_BUFFER_INFO, HANDLE)
{ {
let handle = get_current_handle(screen_manager); let handle = get_current_handle(screen_manager);

View File

@ -13,7 +13,7 @@ pub mod manager;
pub mod style; pub mod style;
pub mod terminal; pub mod terminal;
pub use shared::environment::Environment; pub use shared::environment::Crossterm;
pub use shared::screen; pub use shared::screen;
pub use state::context::Context; pub use state::context::Context;
@ -28,3 +28,6 @@ extern crate libc;
extern crate termios; extern crate termios;
#[cfg(windows)] #[cfg(windows)]
extern crate winapi; extern crate winapi;
#[macro_use]
extern crate lazy_static;

View File

@ -14,15 +14,16 @@ pub struct AnsiScreenManager {
impl IScreenManager for AnsiScreenManager { impl IScreenManager for AnsiScreenManager {
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool) { fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool) {
panic!();
self.is_alternate_screen = is_alternate_screen; self.is_alternate_screen = is_alternate_screen;
} }
fn write_ansi(&mut self, string: String) { fn write_string(&mut self, string: String) {
write!(self.output, "{}", string); write!(self.output, "{}", string);
self.flush(); self.flush();
} }
fn write_ansi_str(&mut self, string: &str) { fn write_str(&mut self, string: &str) {
write!(self.output, "{}", string); write!(self.output, "{}", string);
self.flush(); self.flush();
} }

View File

@ -39,23 +39,19 @@ impl ScreenManager {
} }
/// Write an ANSI code as String. /// Write an ANSI code as String.
pub fn write_ansi(&mut self, string: String) { pub fn write_string(&mut self, string: String) {
self.screen_manager.write_ansi(string); self.screen_manager.write_string(string);
} }
/// Write an ANSI code as &str /// Write an ANSI code as &str
pub fn write_ansi_str(&mut self, string: &str) { pub fn write_str(&mut self, string: &str) {
self.screen_manager.write_ansi_str(string); self.screen_manager.write_str(string);
} }
/// Can be used to get an specific implementation used for the current platform. /// Can be used to get an specific implementation used for the current platform.
pub fn as_any(&mut self) -> &mut Any { pub fn as_any(&mut self) -> &mut Any {
self.screen_manager.as_any() self.screen_manager.as_any()
} }
pub fn write_val(&mut self, value: String) {
self.screen_manager.write(value.as_bytes());
}
} }
impl Write for ScreenManager { impl Write for ScreenManager {

View File

@ -19,9 +19,9 @@ pub trait IScreenManager {
/// Toggle the value if alternatescreen is on. /// Toggle the value if alternatescreen is on.
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool); fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool);
/// Write ansi code as String to the current stdout. /// Write ansi code as String to the current stdout.
fn write_ansi(&mut self, string: String); fn write_string(&mut self, string: String);
/// Write a &str to the current stdout. /// Write a &str to the current stdout.
fn write_ansi_str(&mut self, string: &str); fn write_str(&mut self, string: &str);
/// Write buffer to console. /// Write buffer to console.
fn write(&mut self, buf: &[u8]) -> io::Result<usize>; fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
/// Flush the current output. /// Flush the current output.

View File

@ -18,9 +18,9 @@ impl IScreenManager for WinApiScreenManager {
self.is_alternate_screen = is_alternate_screen; self.is_alternate_screen = is_alternate_screen;
} }
fn write_ansi(&mut self, string: String) {} fn write_string(&mut self, string: String) { &self.write(string.as_bytes()); }
fn write_ansi_str(&mut self, string: &str) {} fn write_str(&mut self, string: &str) { &self.write(string.as_bytes()); }
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.is_alternate_screen { if self.is_alternate_screen {

View File

@ -1,3 +1,6 @@
///
use super::super::cursor; use super::super::cursor;
use super::super::style; use super::super::style;
use super::super::terminal::terminal; use super::super::terminal::terminal;
@ -6,55 +9,53 @@ use Context;
use std::fmt::Display; use std::fmt::Display;
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Once, ONCE_INIT}; use std::sync::Arc;
static START: Once = ONCE_INIT; use std::convert::From;
pub struct Environment { ///
context: Rc<Context>, pub struct Crossterm {
terminal: Box<terminal::Terminal>, context: Rc<Context>
cursor: Box<cursor::TerminalCursor>,
color: Box<style::TerminalColor>,
} }
impl Environment { impl From<Rc<Context>> for Crossterm
pub fn new() -> Environment { {
return Environment { fn from(context: Rc<Context>) -> Self {
context: Context::new(), return Crossterm {
terminal: unsafe { mem::zeroed() }, context: context
cursor: unsafe { mem::zeroed() }, }
color: unsafe { mem::zeroed() }, }
};
} }
pub fn terminal(&mut self) -> &Box<terminal::Terminal> { impl Crossterm {
START.call_once(|| { pub fn new() -> Crossterm {
self.terminal = terminal::terminal(self.context.clone()); return Crossterm { context: Context::new() };
});
&self.terminal
} }
pub fn cursor(&mut self) -> &Box<cursor::TerminalCursor> { pub fn terminal(&self) -> terminal::Terminal
START.call_once(|| { {
self.cursor = cursor::cursor(self.context.clone()); return terminal::Terminal::new(self.context.clone());
});
&self.cursor
} }
pub fn color(&mut self) -> &Box<style::TerminalColor> { pub fn cursor(&self) -> cursor::TerminalCursor
START.call_once(|| { {
self.color = style::color(self.context.clone()); return cursor::TerminalCursor::new(self.context.clone())
});
&self.color
} }
pub fn paint<D: Display>(&mut self, value: D) -> style::StyledObject<D> { pub fn color(&self) -> style::TerminalColor
{
return style::TerminalColor::new(self.context.clone());
}
pub fn paint<D: Display>(&self, value: D) -> style::StyledObject<D> {
self.terminal().paint(value) self.terminal().paint(value)
} }
pub fn write<D: Display>(&mut self, value: D)
{
self.terminal().write(value)
}
pub fn context(&self) -> Rc<Context> { pub fn context(&self) -> Rc<Context> {
return self.context.clone(); self.context.clone()
} }
} }

View File

@ -45,23 +45,21 @@ pub fn exit_terminal() {
/// Get an 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) -> 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 = false;
if cfg!(target_os = "windows") { if cfg!(target_os = "windows") {
// #[cfg(windows)] // #[cfg(windows)]
// 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.
// does_support = try_enable_ansi_support(); // does_support = try_enable_ansi_support();
does_support = false;
if !does_support { if !does_support {
term = Some(winapi_impl); term = Some(winapi_impl);
} }
} }
if does_support { if does_support {
println!("Does support");
term = Some(unix_impl); term = Some(unix_impl);
} }

View File

@ -14,6 +14,7 @@ use Context;
use std::io::{self, Write}; use std::io::{self, Write};
use std::rc::Rc; use std::rc::Rc;
use std::convert::From;
pub struct AlternateScreen { pub struct AlternateScreen {
context: Rc<Context>, context: Rc<Context>,
@ -88,6 +89,22 @@ impl Drop for AlternateScreen {
} }
} }
use super::super::shared::environment::Crossterm;
impl From<Crossterm> for AlternateScreen
{
fn from(crossterm: Crossterm) -> Self {
let command_id = get_to_alternate_screen_command(crossterm.context());
let screen = AlternateScreen {
context: crossterm.context(),
command_id: command_id,
};
screen.to_alternate();
return screen;
}
}
// Get the alternate screen command to enable and disable alternate screen based on the current platform // Get the alternate screen command to enable and disable alternate screen based on the current platform
fn get_to_alternate_screen_command(context: Rc<Context>) -> u16 { fn get_to_alternate_screen_command(context: Rc<Context>) -> u16 {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]

View File

@ -38,9 +38,9 @@ impl ToAlternateScreenBufferCommand {
impl IStateCommand for ToAlternateScreenBufferCommand { impl IStateCommand for ToAlternateScreenBufferCommand {
fn execute(&mut self) -> bool { fn execute(&mut self) -> bool {
println!("asdfasdf");
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_str(csi!("?1049h"));
screen.toggle_is_alternate_screen(true); screen.toggle_is_alternate_screen(true);
return true; return true;
} }
@ -49,7 +49,7 @@ impl IStateCommand for ToAlternateScreenBufferCommand {
fn undo(&mut self) -> bool { fn undo(&mut self) -> bool {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("?1049l")); screen.write_str(csi!("?1049l"));
screen.toggle_is_alternate_screen(false); screen.toggle_is_alternate_screen(false);
return true; return true;
} }

View File

@ -30,7 +30,6 @@ impl EnableAnsiCommand {
impl IStateCommand for EnableAnsiCommand { impl IStateCommand for EnableAnsiCommand {
fn execute(&mut self) -> bool { fn execute(&mut self) -> bool {
panic!();
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded. // 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() { if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled() {
return ansi_support::windows_supportable(); return ansi_support::windows_supportable();
@ -46,7 +45,6 @@ impl IStateCommand for EnableAnsiCommand {
if !kernel::set_console_mode(&output_handle, dw_mode) { if !kernel::set_console_mode(&output_handle, dw_mode) {
return false; return false;
} }
return true; return true;
} }
} }
@ -157,8 +155,6 @@ impl IStateCommand for ToAlternateScreenBufferCommand {
fn execute(&mut self) -> bool { fn execute(&mut self) -> bool {
use super::super::super::manager::WinApiScreenManager; use super::super::super::manager::WinApiScreenManager;
let mut chi_buffer: [CHAR_INFO; 160] = unsafe { mem::zeroed() };
let handle = kernel::get_output_handle(); let handle = kernel::get_output_handle();
// create a new screen buffer to copy to. // create a new screen buffer to copy to.

View File

@ -1,4 +1,50 @@
//! This module contains the code for the context of the terminal. //! What is the `Context` all about? This `Context` has several reasons why it is introduced into `crossterm version 0.2.3`.
//! These points are related to the features like `Alternatescreen` and managing the terminal state.
//!
//! - At first `Terminal state`:
//!
//! Because this is a terminal manipulating library there will be made changes to terminal when running an process.
//! If you stop the process you want the terminal back in its original state.
//! Therefore, I need to track the changes made to the terminal.
//!
//! - At second `Handle to the console`
//!
//! In Rust we can call `stdout()` to get an handle to the current default console handle.
//! For example when in unix systems you want to print something to the main screen you can use the following code:
//!
//! ```
//! write!(std::io::stdout(), "{}", "some text").
//! ```
//!
//! But things change when we are in alternate screen modes.
//! We can not simply use `stdout()` to get a handle to the alternate screen, since this call returns the current default console handle (mainscreen).
//!
//! Instead we need to store an handle to the screen output.
//! This handle could be used to put into alternate screen modes and back into main screen modes.
//! Through this stored handle Crossterm can execute its command on the current screen whether it be alternate screen or main screen.
//!
//! For unix systems we store the handle gotten from `stdout()` for windows systems that are not supporting ANSI escape codes we store WinApi `HANDLE` struct witch will provide access to the current screen.
//!
//! So to recap this `Context` struct is a wrapper for a type that manges terminal state changes.
//! When this `Context` goes out of scope all changes made will be undone.
//! Also is this `Context` is a wrapper for access to the current console screen.
//!
//!
//! Because Crossterm needs access to the above to types quite often I have chosen to add those two in one struct called `Context` so that this type could be shared throughout library.
//! Check this link for more info: [cleanup of the changes](https://stackoverflow.com/questions/48732387/how-can-i-run-clean-up-code-in-a-rust-library).
//!
//! Now the user has to pass an context type to the modules of Crossterm like this:
//!
//! ```
//! let context = Context::new();
//!
//! let cursor = cursor(&context);
//! let terminal = terminal(&context);
//! let color = color(&context);
//! ```
//!
//! Check the documentation of `AlternateScreen` for more info about how to properly manage the `Context` of the terminal.
//! If you don't use alternate screen functionalist's please checkout the `Crossterm` documentation whits will make things easier for you.
use {ScreenManager, StateManager}; use {ScreenManager, StateManager};
@ -46,7 +92,6 @@ use std::io::Write;
impl Drop for Context { impl Drop for Context {
fn drop(&mut self) { fn drop(&mut self) {
panic!();
let mut changes = self.state_manager.lock().unwrap(); let mut changes = self.state_manager.lock().unwrap();
changes.restore_changes(); changes.restore_changes();
} }

View File

@ -23,10 +23,10 @@ impl StateManager {
/// Restore all changes that are made to the terminal. /// Restore all changes that are made to the terminal.
pub fn restore_changes(&mut self) { pub fn restore_changes(&mut self) {
for (id, item) in self.changed_states.iter_mut() { for (id, item) in self.changed_states.iter_mut() {
let mut item = item.lock().unwrap(); // let mut item = item.lock().unwrap();
item.undo(); // item.undo();
//
println!("undo!"); // println!("undo!");
} }
} }

View File

@ -25,7 +25,7 @@ impl ITerminalColor for AnsiColor {
let mx_guard = &self.screen_manager; let mx_guard = &self.screen_manager;
let mut screen = mx_guard.lock().unwrap(); let mut screen = mx_guard.lock().unwrap();
screen.write_ansi(format!( screen.write_string(format!(
csi!("{}m"), csi!("{}m"),
self.color_value(fg_color, ColorType::Foreground) self.color_value(fg_color, ColorType::Foreground)
)); ));
@ -35,7 +35,7 @@ impl ITerminalColor for AnsiColor {
let mx_guard = &self.screen_manager; let mx_guard = &self.screen_manager;
let mut screen = mx_guard.lock().unwrap(); let mut screen = mx_guard.lock().unwrap();
screen.write_ansi(format!( screen.write_string(format!(
csi!("{}m"), csi!("{}m"),
self.color_value(bg_color, ColorType::Background) self.color_value(bg_color, ColorType::Background)
)); ));
@ -44,7 +44,7 @@ impl ITerminalColor for AnsiColor {
fn reset(&mut self) { fn reset(&mut self) {
let mut screen = self.screen_manager.lock().unwrap(); let mut screen = self.screen_manager.lock().unwrap();
{ {
screen.write_ansi_str(csi!("0m")); screen.write_str(csi!("0m"));
} }
} }

View File

@ -11,7 +11,7 @@ use {Context, ScreenManager};
/// 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 {
color: Option<Box<ITerminalColor>>, color: Box<ITerminalColor>,
screen_manager: Rc<Mutex<ScreenManager>>, screen_manager: Rc<Mutex<ScreenManager>>,
} }
@ -22,7 +22,7 @@ impl TerminalColor {
let color = functions::get_module::<Box<ITerminalColor>>( let color = functions::get_module::<Box<ITerminalColor>>(
WinApiColor::new(context.screen_manager.clone()), WinApiColor::new(context.screen_manager.clone()),
AnsiColor::new(context.screen_manager.clone()), AnsiColor::new(context.screen_manager.clone()),
); ).unwrap();
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let color = Some(AnsiColor::new(context.screen_manager.clone()) as Box<ITerminalColor>); let color = Some(AnsiColor::new(context.screen_manager.clone()) as Box<ITerminalColor>);
@ -55,9 +55,7 @@ impl TerminalColor {
/// ///
/// ``` /// ```
pub fn set_fg(&mut self, color: Color) { pub fn set_fg(&mut self, color: Color) {
if let Some(ref mut terminal_color) = self.color { self.color.set_fg(color);
terminal_color.set_fg(color);
}
} }
/// Set the background color to the given color. /// Set the background color to the given color.
@ -83,9 +81,7 @@ impl TerminalColor {
/// ///
/// ``` /// ```
pub fn set_bg(&mut self, color: Color) { pub fn set_bg(&mut self, color: Color) {
if let Some(ref mut terminal_color) = self.color { self.color.set_bg(color);
terminal_color.set_bg(color);
}
} }
/// Reset the terminal colors and attributes to default. /// Reset the terminal colors and attributes to default.
@ -106,9 +102,7 @@ impl TerminalColor {
/// ///
/// ``` /// ```
pub fn reset(&mut self) { pub fn reset(&mut self) {
if let Some(ref mut terminal_color) = self.color { self.color.reset();
terminal_color.reset();
}
} }
/// Get available color count. /// Get available color count.

View File

@ -10,15 +10,12 @@ use std::sync::Mutex;
/// This struct is an windows implementation for color related actions. /// This struct is an windows implementation for color related actions.
pub struct WinApiColor { pub struct WinApiColor {
original_console_color: u16,
screen_manager: Rc<Mutex<ScreenManager>>, screen_manager: Rc<Mutex<ScreenManager>>,
} }
impl WinApiColor { impl WinApiColor {
pub fn new(screen_manager: Rc<Mutex<ScreenManager>>) -> Box<WinApiColor> { pub fn new(screen_manager: Rc<Mutex<ScreenManager>>) -> Box<WinApiColor> {
Box::from(WinApiColor { Box::from(WinApiColor {
original_console_color: kernel::get_original_console_color(&screen_manager),
screen_manager: screen_manager, screen_manager: screen_manager,
}) })
} }
@ -68,7 +65,8 @@ impl ITerminalColor for WinApiColor {
} }
fn reset(&mut self) { fn reset(&mut self) {
kernel::set_console_text_attribute(self.original_console_color, &self.screen_manager); self.set_bg(Color::Black);
self.set_fg(Color::White);
} }
/// 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

View File

@ -32,9 +32,7 @@ impl Default for ObjectStyle {
impl ObjectStyle { impl ObjectStyle {
/// Apply an `StyledObject` to the passed displayable object. /// Apply an `StyledObject` to the passed displayable object.
pub fn apply_to<D>(&self, val: D, context: Rc<Context>) -> StyledObject<D> pub fn apply_to<D: Display>(&self, val: D, context: Rc<Context>) -> StyledObject<D>
where
D: Display,
{ {
StyledObject { StyledObject {
object_style: self.clone(), object_style: self.clone(),

View File

@ -15,13 +15,13 @@ use super::super::super::manager::WinApiScreenManager;
use style::{Color, ObjectStyle}; use style::{Color, ObjectStyle};
/// Struct that contains both the style and the content wits can be styled. /// Struct that contains both the style and the content wits can be styled.
pub struct StyledObject<D> { pub struct StyledObject<D: Display> {
pub object_style: ObjectStyle, pub object_style: ObjectStyle,
pub content: D, pub content: D,
pub context: Rc<Context>, pub context: Rc<Context>,
} }
impl<D> StyledObject<D> where D: Display { impl<D: Display> StyledObject<D>{
/// Set the foreground of the styled object to the passed `Color` /// Set the foreground of the styled object to the passed `Color`
/// ///
/// #Example /// #Example
@ -168,7 +168,7 @@ impl <D:Display> Display for StyledObject<D>
let mutex = &self.context.screen_manager; let mutex = &self.context.screen_manager;
{ {
let mut screen = mutex.lock().unwrap(); let mut screen = mutex.lock().unwrap();
screen.write_ansi(format!(csi!("{}m"), *attr as i16)); screen.write_string(format!(csi!("{}m"), *attr as i16));
} }
reset = true; reset = true;
} }
@ -183,8 +183,7 @@ impl <D:Display> Display for StyledObject<D>
use std::fmt::Write; use std::fmt::Write;
let mut string = String::new(); let mut string = String::new();
write!(string, "{}", self.content).unwrap(); write!(string, "{}", self.content).unwrap();
screen_manager.write_string(string)
screen_manager.write_val(string)
} }
} }

View File

@ -22,19 +22,19 @@ impl ITerminal for AnsiTerminal {
{ {
match clear_type { match clear_type {
ClearType::All => { ClearType::All => {
screen_manager.write_ansi_str(csi!("2J")); screen_manager.write_str(csi!("2J"));
} }
ClearType::FromCursorDown => { ClearType::FromCursorDown => {
screen_manager.write_ansi_str(csi!("J")); screen_manager.write_str(csi!("J"));
} }
ClearType::FromCursorUp => { ClearType::FromCursorUp => {
screen_manager.write_ansi_str(csi!("1J")); screen_manager.write_str(csi!("1J"));
} }
ClearType::CurrentLine => { ClearType::CurrentLine => {
screen_manager.write_ansi_str(csi!("2K")); screen_manager.write_str(csi!("2K"));
} }
ClearType::UntilNewLine => { ClearType::UntilNewLine => {
screen_manager.write_ansi_str(csi!("K")); screen_manager.write_str(csi!("K"));
} }
}; };
} }
@ -47,21 +47,21 @@ impl ITerminal for AnsiTerminal {
fn scroll_up(&self, count: i16) { fn scroll_up(&self, count: i16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}S"), count)); screen.write_string(format!(csi!("{}S"), count));
} }
} }
fn scroll_down(&self, count: i16) { fn scroll_down(&self, count: i16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("{}T"), count)); screen.write_string(format!(csi!("{}T"), count));
} }
} }
fn set_size(&self, width: i16, height: i16) { fn set_size(&self, width: i16, height: i16) {
let mut screen = self.context.screen_manager.lock().unwrap(); let mut screen = self.context.screen_manager.lock().unwrap();
{ {
screen.write_ansi(format!(csi!("8;{};{}t"), width, height)); screen.write_string(format!(csi!("8;{};{}t"), width, height));
} }
} }

View File

@ -12,7 +12,7 @@ use std::rc::Rc;
/// 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: Box<ITerminal>,
context: Rc<Context>, context: Rc<Context>,
} }
@ -23,10 +23,10 @@ impl Terminal {
let terminal = functions::get_module::<Box<ITerminal>>( let terminal = functions::get_module::<Box<ITerminal>>(
WinApiTerminal::new(context.clone()), WinApiTerminal::new(context.clone()),
AnsiTerminal::new(context.clone()), AnsiTerminal::new(context.clone()),
); ).unwrap();
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
let terminal = Some(AnsiTerminal::new(context.clone()) as Box<ITerminal>); let terminal = AnsiTerminal::new(context.clone()) as Box<ITerminal>;
Terminal { Terminal {
terminal, terminal,
@ -60,9 +60,7 @@ impl Terminal {
/// ///
/// ``` /// ```
pub fn clear(&mut self, clear_type: ClearType) { pub fn clear(&mut self, clear_type: ClearType) {
if let Some(ref terminal) = self.terminal { self.terminal.clear(clear_type);
terminal.clear(clear_type);
}
} }
/// Get the terminal size (x,y). /// Get the terminal size (x,y).
@ -83,10 +81,7 @@ impl Terminal {
/// ///
/// ``` /// ```
pub fn terminal_size(&mut self) -> (u16, u16) { pub fn terminal_size(&mut self) -> (u16, u16) {
if let Some(ref terminal) = self.terminal { return self.terminal.terminal_size();
return terminal.terminal_size();
}
(0, 0)
} }
/// Scroll `n` lines up in the current terminal. /// Scroll `n` lines up in the current terminal.
@ -107,9 +102,7 @@ impl Terminal {
/// ///
/// ``` /// ```
pub fn scroll_up(&mut self, count: i16) { pub fn scroll_up(&mut self, count: i16) {
if let Some(ref terminal) = self.terminal { self.terminal.scroll_up(count);
terminal.scroll_up(count);
}
} }
/// Scroll `n` lines up in the current terminal. /// Scroll `n` lines up in the current terminal.
@ -130,9 +123,7 @@ impl Terminal {
/// ///
/// ``` /// ```
pub fn scroll_down(&mut self, count: i16) { pub fn scroll_down(&mut self, count: i16) {
if let Some(ref terminal) = self.terminal { self.terminal.scroll_down(count);
terminal.scroll_down(count);
}
} }
/// Set the terminal size. Note that not all terminals can be set to a very small scale. /// Set the terminal size. Note that not all terminals can be set to a very small scale.
@ -153,9 +144,7 @@ impl Terminal {
/// ///
/// ``` /// ```
pub fn set_size(&mut self, width: i16, height: i16) { pub fn set_size(&mut self, width: i16, height: i16) {
if let Some(ref terminal) = self.terminal { self.terminal.set_size(width, height);
terminal.set_size(width, height);
}
} }
/// Wraps an displayable object so it can be formatted with colors and attributes. /// Wraps an displayable object so it can be formatted with colors and attributes.
@ -193,9 +182,7 @@ impl Terminal {
/// Exit the current process.fy /// Exit the current process.fy
pub fn exit(&self) { pub fn exit(&self) {
if let Some(ref terminal) = self.terminal { self.terminal.exit();
terminal.exit();
}
} }
/// Write any displayable content to the current terminal screen. /// Write any displayable content to the current terminal screen.
@ -208,7 +195,7 @@ impl Terminal {
let mut string = String::new(); let mut string = String::new();
write!(string, "{}", value).unwrap(); write!(string, "{}", value).unwrap();
screen_manager.write_val(string); screen_manager.write_string(string);
} }
} }
} }

49
test.md Normal file
View File

@ -0,0 +1,49 @@
What is the `Context` all about? This `Context` has several reasons why it is introduced into `crossterm version 0.2.3`.
These points are related to the features like `Alternatescreen` and managing the terminal state.
- At first `Terminal state`:
Because this is a terminal manipulating library there will be made changes to terminal when running an process.
If you stop the process you want the terminal back in its original state.
Therefore, I need to track the changes made to the terminal.
- At second `Handle to the console`
In Rust we can call `stdout()` to get an handle to the current default console handle.
For example when in unix systems you want to print something to the main screen you can use the following code:
write!(std::io::stdout(), "{}", "some text").
But things change when we are in alternate screen modes.
We can not simply use `stdout()` to get a handle to the alternate screen, since this call returns the current default console handle (mainscreen).
Instead we need to store an handle to the screen output.
This handle could be used to put into alternate screen modes and back into main screen modes.
Through this stored handle Crossterm can execute its command on the current screen whether it be alternate screen or main screen.
For unix systems we store the handle gotten from `stdout()` for windows systems that are not supporting ANSI escape codes we store WinApi `HANDLE` struct witch will provide access to the current screen.
So to recap this `Context` struct is a wrapper for a type that manges terminal state changes.
When this `Context` goes out of scope all changes made will be undone.
Also is this `Context` is a wrapper for access to the current console screen.
Because Crossterm needs access to the above to types quite often I have chosen to add those two in one struct called `Context` so that this type could be shared throughout library.
Check this link for more info: [cleanup of the changes](https://stackoverflow.com/questions/48732387/how-can-i-run-clean-up-code-in-a-rust-library).
Now the user has to pass an context type to the modules of Crossterm like this:
let context = Context::new();
let cursor = cursor(&context);
let terminal = terminal(&context);
let color = color(&context);
Check the documentation of `AlternateScreen` for more info about how to properly manage the `Context` of the terminal.
If you don't use alternate screen functionalist's please checkout the `Crossterm` documentation whits will make things easier for you.
Because this looks a little odd I will provide a type withs will manage the `Context` for you. You can call the different modules like the following:
let crossterm = Crossterm::new();
let color = envoirment.color();
let cursor = envoirment.cursor();
let terminal = envoirment.terminal();