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]
|
[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"] }
|
||||||
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 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));
|
||||||
|
}
|
||||||
}
|
}
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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) {
|
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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
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.
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")]
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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(),
|
||||||
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
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