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:
parent
26a1960159
commit
e62d8cff9d
1050
.idea/workspace.xml
1050
.idea/workspace.xml
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.4.2"
|
||||
lazy_static = "1.0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
|
||||
|
@ -18,14 +18,10 @@ mod cursor;
|
||||
mod program_examples;
|
||||
mod terminal;
|
||||
|
||||
use crossterm::Context;
|
||||
use program_examples::first_depth_search;
|
||||
|
||||
fn main() {
|
||||
use crossterm::Context;
|
||||
|
||||
{
|
||||
let mut context = Context::new();
|
||||
|
||||
terminal::alternate_screen::print_wait_screen_on_alternate_window(context.clone());
|
||||
|
||||
println!("count: {}", std::rc::Rc::strong_count(&context));
|
||||
}
|
||||
first_depth_search::run();
|
||||
// println!("End")
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,49 +1,75 @@
|
||||
use super::variables::{Cell, Position, Size };
|
||||
use crossterm::terminal::terminal;
|
||||
use crossterm::Environment;
|
||||
use crossterm::style::{ObjectStyle, StyledObject};
|
||||
use crossterm::cursor::cursor;
|
||||
use crossterm::Crossterm;
|
||||
use crossterm::style::{ObjectStyle, StyledObject, Color};
|
||||
use crossterm::Context;
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
pub struct Map<D: Display>
|
||||
pub struct Map
|
||||
{
|
||||
map: Vec<Vec<Cell<D>>>,
|
||||
wall_style: StyledObject<D>,
|
||||
map_style: StyledObject<D>,
|
||||
pub map: Vec<Vec<Cell>>,
|
||||
pub size: Size,
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
pub fn init(&self, environment: &mut Environment, map_size: Size) -> Map<D>
|
||||
{
|
||||
let mut map: Vec<Vec<Cell<D>>> = Vec::new();
|
||||
|
||||
for y in 0..map[0].len()
|
||||
// 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
|
||||
{
|
||||
for x in 0..map[1].len()
|
||||
{
|
||||
if (y == 0 || y == map.len() - 1) || (x == 0 || x == map[0].len())
|
||||
let mut row: Vec<Cell> = Vec::new();
|
||||
|
||||
for x in 0..map_size.width
|
||||
{
|
||||
map[y][x] = Cell::new(Position::new(x,y), wall_style.apply_to(environment.context(), "■"));
|
||||
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));
|
||||
}
|
||||
}
|
||||
else{
|
||||
map[y][x] = Cell::new(Position::new(x,y), map_style);
|
||||
}
|
||||
}
|
||||
map.push(row);
|
||||
}
|
||||
|
||||
Map { map }
|
||||
Map { map: map, size: Size::new(map_size.width, map_size.height)}
|
||||
}
|
||||
|
||||
fn render_map()
|
||||
// render the map on the screen.
|
||||
pub fn render_map(&mut self, crossterm: &mut Crossterm)
|
||||
{
|
||||
let mut cursor = crossterm.cursor();
|
||||
|
||||
for row in self.map.iter_mut()
|
||||
{
|
||||
for column in row.iter_mut()
|
||||
{
|
||||
// we only have to render the walls
|
||||
if (column.position.y == 0 || column.position.y == self.size.height - 1) || (column.position.x == 0 || column.position.x == self.size.width - 1)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if position in the map at the given coords is visted.
|
||||
pub fn is_cell_visited(&self, x: usize, y: usize) -> bool
|
||||
{
|
||||
self.map[y][x].visited
|
||||
}
|
||||
|
||||
// change an position in the map to visited.
|
||||
pub fn set_visited(&mut self, x: usize, y: usize)
|
||||
{
|
||||
self.map[y][x].visited = true;
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
@ -1,17 +1,81 @@
|
||||
extern crate rand;
|
||||
|
||||
mod map;
|
||||
mod variables;
|
||||
mod messages;
|
||||
mod algorithm;
|
||||
|
||||
use crossterm;
|
||||
use crossterm::Crossterm;
|
||||
use crossterm::terminal::ClearType;
|
||||
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();
|
||||
let map_size = Size::new(20,20);
|
||||
let wall_style = env.paint("■").with(Color::Blue).on(Color::Black);
|
||||
let map_style = env.paint(" ").with(Color::White).on(Color::White);
|
||||
// create new Crossterm instance.
|
||||
let mut crossterm = Crossterm::new();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
@ -1,19 +1,22 @@
|
||||
extern crate crossterm;
|
||||
|
||||
use self::crossterm::terminal::{terminal, ClearType};
|
||||
use self::crossterm::Context;
|
||||
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
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
Up = 0,
|
||||
Down = 1,
|
||||
Left = 2,
|
||||
Right = 3
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Position
|
||||
{
|
||||
pub x: usize,
|
||||
@ -31,33 +34,30 @@ impl Position
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Size
|
||||
{
|
||||
pub width: u16,
|
||||
pub height: u16
|
||||
pub width: usize,
|
||||
pub height: usize
|
||||
}
|
||||
|
||||
impl Size
|
||||
{
|
||||
pub fn new(width: u16, height: u16) -> Size
|
||||
pub fn new(width: usize, height: usize) -> Size
|
||||
{
|
||||
Size {width,height}
|
||||
}
|
||||
}
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
pub struct Cell<'a, D: Display>
|
||||
pub struct Cell
|
||||
{
|
||||
position: Position,
|
||||
style: &'a StyledObject<D>,
|
||||
visited: bool
|
||||
pub position: Position,
|
||||
pub color: Color,
|
||||
pub look: char,
|
||||
pub visited: bool
|
||||
}
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
impl<'a, D: Display> Cell<'a, D>
|
||||
impl Cell
|
||||
{
|
||||
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 }
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
//pub mod first_depth_search;
|
||||
pub mod first_depth_search;
|
||||
|
@ -21,7 +21,7 @@ impl ITerminalCursor for AnsiCursor {
|
||||
fn goto(&self, x: u16, y: u16) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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();
|
||||
{
|
||||
screen.write_ansi_str(csi!("s"));
|
||||
screen.write_str(csi!("s"));
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_position(&self) {
|
||||
let mut screen = self.context.screen_manager.lock().unwrap();
|
||||
{
|
||||
screen.write_ansi_str(csi!("u"));
|
||||
screen.write_str(csi!("u"));
|
||||
}
|
||||
}
|
||||
|
||||
fn hide(&self) {
|
||||
let mut screen = self.context.screen_manager.lock().unwrap();
|
||||
{
|
||||
screen.write_ansi_str(csi!("?25l"));
|
||||
screen.write_str(csi!("?25l"));
|
||||
}
|
||||
}
|
||||
|
||||
fn show(&self) {
|
||||
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();
|
||||
{
|
||||
if blink {
|
||||
screen.write_ansi_str(csi!("?12h"));
|
||||
screen.write_str(csi!("?12h"));
|
||||
} else {
|
||||
screen.write_ansi_str(csi!("?12l"));
|
||||
screen.write_str(csi!("?12l"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use std::rc::Rc;
|
||||
/// Struct that stores an specific platform implementation for cursor related actions.
|
||||
pub struct TerminalCursor {
|
||||
context: Rc<Context>,
|
||||
terminal_cursor: Option<Box<ITerminalCursor>>,
|
||||
terminal_cursor: Box<ITerminalCursor>,
|
||||
}
|
||||
|
||||
impl TerminalCursor {
|
||||
@ -24,10 +24,10 @@ impl TerminalCursor {
|
||||
let cursor = functions::get_module::<Box<ITerminalCursor>>(
|
||||
WinApiCursor::new(context.screen_manager.clone()),
|
||||
AnsiCursor::new(context.clone()),
|
||||
);
|
||||
).unwrap();
|
||||
|
||||
#[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 {
|
||||
terminal_cursor: cursor,
|
||||
@ -57,9 +57,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.goto(x, y);
|
||||
}
|
||||
self.terminal_cursor.goto(x, y);
|
||||
self
|
||||
}
|
||||
|
||||
@ -85,11 +83,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn pos(&mut self) -> (u16, u16) {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.pos()
|
||||
} else {
|
||||
(0, 0)
|
||||
}
|
||||
self.terminal_cursor.pos()
|
||||
}
|
||||
|
||||
/// Move the current cursor position `n` times up.
|
||||
@ -114,9 +108,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.move_up(count);
|
||||
}
|
||||
self.terminal_cursor.move_up(count);
|
||||
self
|
||||
}
|
||||
|
||||
@ -140,9 +132,7 @@ impl TerminalCursor {
|
||||
/// }
|
||||
/// ```
|
||||
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.move_right(count);
|
||||
}
|
||||
self.terminal_cursor.move_right(count);
|
||||
self
|
||||
}
|
||||
|
||||
@ -168,9 +158,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.move_down(count);
|
||||
}
|
||||
self.terminal_cursor.move_down(count);
|
||||
self
|
||||
}
|
||||
|
||||
@ -196,9 +184,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.move_left(count);
|
||||
}
|
||||
self.terminal_cursor.move_left(count);
|
||||
self
|
||||
}
|
||||
|
||||
@ -246,10 +232,12 @@ impl TerminalCursor {
|
||||
|
||||
let mut mutex = &self.context.screen_manager;
|
||||
{
|
||||
let mut screen_manager = mutex.lock().unwrap();
|
||||
screen_manager.write_val(string);
|
||||
// let mut screen_manager = mutex.lock().unwrap();
|
||||
// screen_manager.write_string(string);
|
||||
|
||||
screen_manager.flush();
|
||||
println!("{}",string);
|
||||
|
||||
// screen_manager.flush();
|
||||
}
|
||||
}
|
||||
self
|
||||
@ -272,9 +260,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn save_position(&mut self) {
|
||||
if let Some(ref mut terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.save_position();
|
||||
}
|
||||
self.terminal_cursor.save_position();
|
||||
}
|
||||
|
||||
/// Return to saved cursor position
|
||||
@ -294,9 +280,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn reset_position(&mut self) {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.reset_position();
|
||||
}
|
||||
self.terminal_cursor.reset_position();
|
||||
}
|
||||
|
||||
/// Hide de cursor in the console.
|
||||
@ -314,9 +298,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn hide(&self) {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.hide();
|
||||
}
|
||||
self.terminal_cursor.hide();
|
||||
}
|
||||
|
||||
/// Show the cursor in the console.
|
||||
@ -334,9 +316,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn show(&self) {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.show();
|
||||
}
|
||||
self.terminal_cursor.show();
|
||||
}
|
||||
|
||||
/// Enable or disable blinking of the terminal.
|
||||
@ -358,9 +338,7 @@ impl TerminalCursor {
|
||||
///
|
||||
/// ```
|
||||
pub fn blink(&self, blink: bool) {
|
||||
if let Some(ref terminal_cursor) = self.terminal_cursor {
|
||||
terminal_cursor.blink(blink);
|
||||
}
|
||||
self.terminal_cursor.blink(blink);
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,4 +371,4 @@ impl TerminalCursor {
|
||||
/// ```
|
||||
pub fn cursor(context: Rc<Context>) -> Box<TerminalCursor> {
|
||||
Box::from(TerminalCursor::new(context.clone()))
|
||||
}
|
||||
}
|
30
src/cursor/default_cursor.rs
Normal file
30
src/cursor/default_cursor.rs
Normal 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) {}
|
||||
//}
|
@ -10,6 +10,7 @@
|
||||
|
||||
pub mod cursor;
|
||||
|
||||
mod default_cursor;
|
||||
mod ansi_cursor;
|
||||
#[cfg(target_os = "windows")]
|
||||
mod winapi_cursor;
|
||||
@ -44,7 +45,7 @@ pub trait ITerminalCursor {
|
||||
/// Move the cursor `n` times left.
|
||||
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.
|
||||
fn save_position(&mut self);
|
||||
fn save_position(&self);
|
||||
/// Return to saved cursor position
|
||||
fn reset_position(&self);
|
||||
/// Hide the terminal cursor.
|
||||
|
@ -49,7 +49,7 @@ impl ITerminalCursor for WinApiCursor {
|
||||
self.goto(xpos - count, ypos);
|
||||
}
|
||||
|
||||
fn save_position(&mut self) {
|
||||
fn save_position(&self) {
|
||||
cursor::save_cursor_pos(&self.screen_manager);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ pub fn try_enable_ansi_support() -> bool {
|
||||
set_ansi_enabled(success);
|
||||
has_been_tried_to_enable(true);
|
||||
});
|
||||
|
||||
windows_supportable()
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,21 @@ pub fn get_console_screen_buffer_info(
|
||||
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)
|
||||
{
|
||||
let handle = get_current_handle(screen_manager);
|
||||
|
@ -13,7 +13,7 @@ pub mod manager;
|
||||
pub mod style;
|
||||
pub mod terminal;
|
||||
|
||||
pub use shared::environment::Environment;
|
||||
pub use shared::environment::Crossterm;
|
||||
pub use shared::screen;
|
||||
pub use state::context::Context;
|
||||
|
||||
@ -28,3 +28,6 @@ extern crate libc;
|
||||
extern crate termios;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
@ -14,15 +14,16 @@ pub struct AnsiScreenManager {
|
||||
|
||||
impl IScreenManager for AnsiScreenManager {
|
||||
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool) {
|
||||
panic!();
|
||||
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);
|
||||
self.flush();
|
||||
}
|
||||
|
||||
fn write_ansi_str(&mut self, string: &str) {
|
||||
fn write_str(&mut self, string: &str) {
|
||||
write!(self.output, "{}", string);
|
||||
self.flush();
|
||||
}
|
||||
|
@ -39,23 +39,19 @@ impl ScreenManager {
|
||||
}
|
||||
|
||||
/// Write an ANSI code as String.
|
||||
pub fn write_ansi(&mut self, string: String) {
|
||||
self.screen_manager.write_ansi(string);
|
||||
pub fn write_string(&mut self, string: String) {
|
||||
self.screen_manager.write_string(string);
|
||||
}
|
||||
|
||||
/// Write an ANSI code as &str
|
||||
pub fn write_ansi_str(&mut self, string: &str) {
|
||||
self.screen_manager.write_ansi_str(string);
|
||||
pub fn write_str(&mut self, string: &str) {
|
||||
self.screen_manager.write_str(string);
|
||||
}
|
||||
|
||||
/// Can be used to get an specific implementation used for the current platform.
|
||||
pub fn as_any(&mut self) -> &mut 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 {
|
||||
|
@ -19,9 +19,9 @@ pub trait IScreenManager {
|
||||
/// Toggle the value if alternatescreen is on.
|
||||
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool);
|
||||
/// 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.
|
||||
fn write_ansi_str(&mut self, string: &str);
|
||||
fn write_str(&mut self, string: &str);
|
||||
/// Write buffer to console.
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
|
||||
/// Flush the current output.
|
||||
|
@ -18,9 +18,9 @@ impl IScreenManager for WinApiScreenManager {
|
||||
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> {
|
||||
if self.is_alternate_screen {
|
||||
|
@ -1,3 +1,6 @@
|
||||
///
|
||||
|
||||
|
||||
use super::super::cursor;
|
||||
use super::super::style;
|
||||
use super::super::terminal::terminal;
|
||||
@ -6,55 +9,53 @@ use Context;
|
||||
use std::fmt::Display;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
static START: Once = ONCE_INIT;
|
||||
use std::sync::Arc;
|
||||
use std::convert::From;
|
||||
|
||||
pub struct Environment {
|
||||
context: Rc<Context>,
|
||||
terminal: Box<terminal::Terminal>,
|
||||
cursor: Box<cursor::TerminalCursor>,
|
||||
color: Box<style::TerminalColor>,
|
||||
///
|
||||
pub struct Crossterm {
|
||||
context: Rc<Context>
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn new() -> Environment {
|
||||
return Environment {
|
||||
context: Context::new(),
|
||||
terminal: unsafe { mem::zeroed() },
|
||||
cursor: unsafe { mem::zeroed() },
|
||||
color: unsafe { mem::zeroed() },
|
||||
};
|
||||
impl From<Rc<Context>> for Crossterm
|
||||
{
|
||||
fn from(context: Rc<Context>) -> Self {
|
||||
return Crossterm {
|
||||
context: context
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Crossterm {
|
||||
pub fn new() -> Crossterm {
|
||||
return Crossterm { context: Context::new() };
|
||||
}
|
||||
|
||||
pub fn terminal(&mut self) -> &Box<terminal::Terminal> {
|
||||
START.call_once(|| {
|
||||
self.terminal = terminal::terminal(self.context.clone());
|
||||
});
|
||||
|
||||
&self.terminal
|
||||
pub fn terminal(&self) -> terminal::Terminal
|
||||
{
|
||||
return terminal::Terminal::new(self.context.clone());
|
||||
}
|
||||
|
||||
pub fn cursor(&mut self) -> &Box<cursor::TerminalCursor> {
|
||||
START.call_once(|| {
|
||||
self.cursor = cursor::cursor(self.context.clone());
|
||||
});
|
||||
|
||||
&self.cursor
|
||||
pub fn cursor(&self) -> cursor::TerminalCursor
|
||||
{
|
||||
return cursor::TerminalCursor::new(self.context.clone())
|
||||
}
|
||||
|
||||
pub fn color(&mut self) -> &Box<style::TerminalColor> {
|
||||
START.call_once(|| {
|
||||
self.color = style::color(self.context.clone());
|
||||
});
|
||||
|
||||
&self.color
|
||||
pub fn color(&self) -> style::TerminalColor
|
||||
{
|
||||
return style::TerminalColor::new(self.context.clone());
|
||||
}
|
||||
|
||||
pub fn paint<D: Display>(&mut self, value: D) -> style::StyledObject<D> {
|
||||
pub fn paint<D: Display>(&self, value: D) -> style::StyledObject<D> {
|
||||
self.terminal().paint(value)
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Rc<Context> {
|
||||
return self.context.clone();
|
||||
pub fn write<D: Display>(&mut self, value: D)
|
||||
{
|
||||
self.terminal().write(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> Rc<Context> {
|
||||
self.context.clone()
|
||||
}
|
||||
}
|
@ -45,23 +45,21 @@ pub fn exit_terminal() {
|
||||
/// Get an module specific implementation based on the current platform.
|
||||
pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T> {
|
||||
let mut term: Option<T> = None;
|
||||
let mut does_support = true;
|
||||
let mut does_support = false;
|
||||
|
||||
if cfg!(target_os = "windows") {
|
||||
// #[cfg(windows)]
|
||||
// use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
|
||||
// #[cfg(windows)]
|
||||
// use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
|
||||
//
|
||||
// // Try to enable ansi on windows if not than use WINAPI.
|
||||
// does_support = try_enable_ansi_support();
|
||||
|
||||
// Try to enable ansi on windows if not than use WINAPI.
|
||||
// does_support = try_enable_ansi_support();
|
||||
|
||||
does_support = false;
|
||||
if !does_support {
|
||||
term = Some(winapi_impl);
|
||||
}
|
||||
}
|
||||
|
||||
if does_support {
|
||||
println!("Does support");
|
||||
term = Some(unix_impl);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ use Context;
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::rc::Rc;
|
||||
use std::convert::From;
|
||||
|
||||
pub struct AlternateScreen {
|
||||
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
|
||||
fn get_to_alternate_screen_command(context: Rc<Context>) -> u16 {
|
||||
#[cfg(target_os = "windows")]
|
||||
|
@ -38,9 +38,9 @@ impl ToAlternateScreenBufferCommand {
|
||||
|
||||
impl IStateCommand for ToAlternateScreenBufferCommand {
|
||||
fn execute(&mut self) -> bool {
|
||||
println!("asdfasdf");
|
||||
let mut screen = self.context.screen_manager.lock().unwrap();
|
||||
{
|
||||
screen.write_str(csi!("?1049h"));
|
||||
screen.toggle_is_alternate_screen(true);
|
||||
return true;
|
||||
}
|
||||
@ -49,7 +49,7 @@ impl IStateCommand for ToAlternateScreenBufferCommand {
|
||||
fn undo(&mut self) -> bool {
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ impl EnableAnsiCommand {
|
||||
|
||||
impl IStateCommand for EnableAnsiCommand {
|
||||
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.
|
||||
if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled() {
|
||||
return ansi_support::windows_supportable();
|
||||
@ -46,7 +45,6 @@ impl IStateCommand for EnableAnsiCommand {
|
||||
if !kernel::set_console_mode(&output_handle, dw_mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -157,8 +155,6 @@ impl IStateCommand for ToAlternateScreenBufferCommand {
|
||||
fn execute(&mut self) -> bool {
|
||||
use super::super::super::manager::WinApiScreenManager;
|
||||
|
||||
let mut chi_buffer: [CHAR_INFO; 160] = unsafe { mem::zeroed() };
|
||||
|
||||
let handle = kernel::get_output_handle();
|
||||
|
||||
// create a new screen buffer to copy to.
|
||||
|
@ -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};
|
||||
|
||||
@ -46,7 +92,6 @@ use std::io::Write;
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
panic!();
|
||||
let mut changes = self.state_manager.lock().unwrap();
|
||||
changes.restore_changes();
|
||||
}
|
||||
|
@ -23,10 +23,10 @@ impl StateManager {
|
||||
/// Restore all changes that are made to the terminal.
|
||||
pub fn restore_changes(&mut self) {
|
||||
for (id, item) in self.changed_states.iter_mut() {
|
||||
let mut item = item.lock().unwrap();
|
||||
item.undo();
|
||||
|
||||
println!("undo!");
|
||||
// let mut item = item.lock().unwrap();
|
||||
// item.undo();
|
||||
//
|
||||
// println!("undo!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ impl ITerminalColor for AnsiColor {
|
||||
let mx_guard = &self.screen_manager;
|
||||
let mut screen = mx_guard.lock().unwrap();
|
||||
|
||||
screen.write_ansi(format!(
|
||||
screen.write_string(format!(
|
||||
csi!("{}m"),
|
||||
self.color_value(fg_color, ColorType::Foreground)
|
||||
));
|
||||
@ -35,7 +35,7 @@ impl ITerminalColor for AnsiColor {
|
||||
let mx_guard = &self.screen_manager;
|
||||
let mut screen = mx_guard.lock().unwrap();
|
||||
|
||||
screen.write_ansi(format!(
|
||||
screen.write_string(format!(
|
||||
csi!("{}m"),
|
||||
self.color_value(bg_color, ColorType::Background)
|
||||
));
|
||||
@ -44,7 +44,7 @@ impl ITerminalColor for AnsiColor {
|
||||
fn reset(&mut self) {
|
||||
let mut screen = self.screen_manager.lock().unwrap();
|
||||
{
|
||||
screen.write_ansi_str(csi!("0m"));
|
||||
screen.write_str(csi!("0m"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ use {Context, ScreenManager};
|
||||
|
||||
/// Struct that stores an specific platform implementation for color related actions.
|
||||
pub struct TerminalColor {
|
||||
color: Option<Box<ITerminalColor>>,
|
||||
color: Box<ITerminalColor>,
|
||||
screen_manager: Rc<Mutex<ScreenManager>>,
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ impl TerminalColor {
|
||||
let color = functions::get_module::<Box<ITerminalColor>>(
|
||||
WinApiColor::new(context.screen_manager.clone()),
|
||||
AnsiColor::new(context.screen_manager.clone()),
|
||||
);
|
||||
).unwrap();
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
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) {
|
||||
if let Some(ref mut terminal_color) = self.color {
|
||||
terminal_color.set_fg(color);
|
||||
}
|
||||
self.color.set_fg(color);
|
||||
}
|
||||
|
||||
/// Set the background color to the given color.
|
||||
@ -83,9 +81,7 @@ impl TerminalColor {
|
||||
///
|
||||
/// ```
|
||||
pub fn set_bg(&mut self, color: Color) {
|
||||
if let Some(ref mut terminal_color) = self.color {
|
||||
terminal_color.set_bg(color);
|
||||
}
|
||||
self.color.set_bg(color);
|
||||
}
|
||||
|
||||
/// Reset the terminal colors and attributes to default.
|
||||
@ -106,9 +102,7 @@ impl TerminalColor {
|
||||
///
|
||||
/// ```
|
||||
pub fn reset(&mut self) {
|
||||
if let Some(ref mut terminal_color) = self.color {
|
||||
terminal_color.reset();
|
||||
}
|
||||
self.color.reset();
|
||||
}
|
||||
|
||||
/// Get available color count.
|
||||
|
@ -10,15 +10,12 @@ use std::sync::Mutex;
|
||||
|
||||
/// This struct is an windows implementation for color related actions.
|
||||
pub struct WinApiColor {
|
||||
|
||||
original_console_color: u16,
|
||||
screen_manager: Rc<Mutex<ScreenManager>>,
|
||||
}
|
||||
|
||||
impl WinApiColor {
|
||||
pub fn new(screen_manager: Rc<Mutex<ScreenManager>>) -> Box<WinApiColor> {
|
||||
Box::from(WinApiColor {
|
||||
original_console_color: kernel::get_original_console_color(&screen_manager),
|
||||
screen_manager: screen_manager,
|
||||
})
|
||||
}
|
||||
@ -68,7 +65,8 @@ impl ITerminalColor for WinApiColor {
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -32,9 +32,7 @@ impl Default for ObjectStyle {
|
||||
|
||||
impl ObjectStyle {
|
||||
/// Apply an `StyledObject` to the passed displayable object.
|
||||
pub fn apply_to<D>(&self, val: D, context: Rc<Context>) -> StyledObject<D>
|
||||
where
|
||||
D: Display,
|
||||
pub fn apply_to<D: Display>(&self, val: D, context: Rc<Context>) -> StyledObject<D>
|
||||
{
|
||||
StyledObject {
|
||||
object_style: self.clone(),
|
||||
|
@ -15,13 +15,13 @@ use super::super::super::manager::WinApiScreenManager;
|
||||
use style::{Color, ObjectStyle};
|
||||
|
||||
/// 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 content: D,
|
||||
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`
|
||||
///
|
||||
/// #Example
|
||||
@ -168,7 +168,7 @@ impl <D:Display> Display for StyledObject<D>
|
||||
let mutex = &self.context.screen_manager;
|
||||
{
|
||||
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;
|
||||
}
|
||||
@ -183,8 +183,7 @@ impl <D:Display> Display for StyledObject<D>
|
||||
use std::fmt::Write;
|
||||
let mut string = String::new();
|
||||
write!(string, "{}", self.content).unwrap();
|
||||
|
||||
screen_manager.write_val(string)
|
||||
screen_manager.write_string(string)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,19 +22,19 @@ impl ITerminal for AnsiTerminal {
|
||||
{
|
||||
match clear_type {
|
||||
ClearType::All => {
|
||||
screen_manager.write_ansi_str(csi!("2J"));
|
||||
screen_manager.write_str(csi!("2J"));
|
||||
}
|
||||
ClearType::FromCursorDown => {
|
||||
screen_manager.write_ansi_str(csi!("J"));
|
||||
screen_manager.write_str(csi!("J"));
|
||||
}
|
||||
ClearType::FromCursorUp => {
|
||||
screen_manager.write_ansi_str(csi!("1J"));
|
||||
screen_manager.write_str(csi!("1J"));
|
||||
}
|
||||
ClearType::CurrentLine => {
|
||||
screen_manager.write_ansi_str(csi!("2K"));
|
||||
screen_manager.write_str(csi!("2K"));
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ use std::rc::Rc;
|
||||
|
||||
/// Struct that stores an specific platform implementation for terminal related actions.
|
||||
pub struct Terminal {
|
||||
terminal: Option<Box<ITerminal>>,
|
||||
terminal: Box<ITerminal>,
|
||||
context: Rc<Context>,
|
||||
}
|
||||
|
||||
@ -23,10 +23,10 @@ impl Terminal {
|
||||
let terminal = functions::get_module::<Box<ITerminal>>(
|
||||
WinApiTerminal::new(context.clone()),
|
||||
AnsiTerminal::new(context.clone()),
|
||||
);
|
||||
).unwrap();
|
||||
|
||||
#[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,
|
||||
@ -60,9 +60,7 @@ impl Terminal {
|
||||
///
|
||||
/// ```
|
||||
pub fn clear(&mut self, clear_type: ClearType) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
terminal.clear(clear_type);
|
||||
}
|
||||
self.terminal.clear(clear_type);
|
||||
}
|
||||
|
||||
/// Get the terminal size (x,y).
|
||||
@ -83,10 +81,7 @@ impl Terminal {
|
||||
///
|
||||
/// ```
|
||||
pub fn terminal_size(&mut self) -> (u16, u16) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
return terminal.terminal_size();
|
||||
}
|
||||
(0, 0)
|
||||
return self.terminal.terminal_size();
|
||||
}
|
||||
|
||||
/// Scroll `n` lines up in the current terminal.
|
||||
@ -107,9 +102,7 @@ impl Terminal {
|
||||
///
|
||||
/// ```
|
||||
pub fn scroll_up(&mut self, count: i16) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
terminal.scroll_up(count);
|
||||
}
|
||||
self.terminal.scroll_up(count);
|
||||
}
|
||||
|
||||
/// Scroll `n` lines up in the current terminal.
|
||||
@ -130,9 +123,7 @@ impl Terminal {
|
||||
///
|
||||
/// ```
|
||||
pub fn scroll_down(&mut self, count: i16) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
terminal.scroll_down(count);
|
||||
}
|
||||
self.terminal.scroll_down(count);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
terminal.set_size(width, height);
|
||||
}
|
||||
self.terminal.set_size(width, height);
|
||||
}
|
||||
|
||||
/// Wraps an displayable object so it can be formatted with colors and attributes.
|
||||
@ -193,9 +182,7 @@ impl Terminal {
|
||||
|
||||
/// Exit the current process.fy
|
||||
pub fn exit(&self) {
|
||||
if let Some(ref terminal) = self.terminal {
|
||||
terminal.exit();
|
||||
}
|
||||
self.terminal.exit();
|
||||
}
|
||||
|
||||
/// Write any displayable content to the current terminal screen.
|
||||
@ -208,7 +195,7 @@ impl Terminal {
|
||||
let mut string = String::new();
|
||||
write!(string, "{}", value).unwrap();
|
||||
|
||||
screen_manager.write_val(string);
|
||||
screen_manager.write_string(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
49
test.md
Normal file
49
test.md
Normal 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();
|
Loading…
Reference in New Issue
Block a user