added unix cursor hide show, fixed windows and added alternate screen

functionality. Also refactored and cleaned up code.:
This commit is contained in:
TimonPost 2018-06-22 21:04:30 +02:00
parent bb2067ed7b
commit 62fbb6b6a9
36 changed files with 1014 additions and 843 deletions

File diff suppressed because it is too large Load Diff

View File

@ -32,5 +32,8 @@ use std::{time, thread};
fn main() {
// alternate_screen::switch_between_main_and_alternate_screen();
let context = Context::new();
raw_mode::print_wait_screen_on_alternate_window();
let mut scre = screen::AlternateScreen::from(context.clone());
write!(scre, "asdf");
scre.flush();
thread::sleep(time::Duration::from_secs(3));
}

View File

@ -12,7 +12,7 @@ use self::crossterm::Context;
pub fn paint_foreground()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
// Pass an string to the `paint()` method with you want to paint.
// This will give you an object back wits can be styled and displayed.
@ -30,7 +30,7 @@ pub fn paint_foreground()
pub fn paint_background()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
// Pass an string to the `paint()` method with you want to paint.
// This will give you an object back wits can be styled and displayed.
@ -48,7 +48,7 @@ pub fn paint_background()
pub fn paint_foreground_and_background()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
// Pass an string to the `paint()` method with you want to paint.
// This will give you an object back wits can be styled and displayed.
@ -74,7 +74,7 @@ pub fn paint_foreground_and_background()
pub fn print_all_foreground_colors()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
println!("Black : \t {}", terminal.paint("").with(Color::Black));
println!("Red : \t\t {}", terminal.paint("").with(Color::Red));
@ -97,7 +97,7 @@ pub fn print_all_foreground_colors()
pub fn print_all_background_colors()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
println!("Black : \t {}", terminal.paint(" ").on(Color::Black));
println!("Red : \t\t {}", terminal.paint(" ").on(Color::Red));
@ -125,7 +125,7 @@ pub fn print_all_background_colors()
pub fn print_font_with_attributes()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
println!("{}", terminal.paint("Normal text"));
println!("{}", terminal.paint("Bold text").bold());
@ -144,7 +144,7 @@ pub fn print_font_with_attributes()
pub fn print_supported_colors()
{
let context = Context::new();
let terminal = terminal::terminal(&context);
let terminal = terminal::terminal(context.clone());
let count = crossterm::style::color(context.screen_manager.clone()).get_available_color_count().unwrap();

View File

@ -12,7 +12,7 @@ pub fn goto()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Set the cursor to position X: 10, Y: 5 in the terminal
cursor.goto(10,5);
}
@ -23,7 +23,7 @@ pub fn pos()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// get the cursor position.
let (x,y) = cursor.pos();
}
@ -34,7 +34,7 @@ pub fn move_up()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Move the cursor to position 3 times to the up in the terminal
cursor.move_up(3);
}
@ -45,7 +45,7 @@ pub fn move_right()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Move the cursor to position 3 times to the right in the terminal
cursor.move_right(3);
}
@ -56,7 +56,7 @@ pub fn move_down()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Move the cursor to position 3 times to the down in the terminal
cursor.move_down(3);
}
@ -67,7 +67,7 @@ pub fn move_left()
let context = Context::new();
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Move the cursor to position 3 times to the left in the terminal
cursor.move_left(3);
}
@ -80,7 +80,7 @@ pub fn print()
// To print an some displayable content on an certain position.
// Get the cursor
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Set the cursor to position X: 10, Y: 5 in the terminal
cursor.goto(10,5);
// Print the @ symbol at position X: 10, Y: 5 in the terminal
@ -104,7 +104,7 @@ pub fn safe_and_reset_position()
{
let context = Context::new();
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
// Goto X: 5 Y: 5
cursor.goto(5,5);

View File

@ -7,18 +7,18 @@ use crossterm::terminal::{self, ClearType};
use std::io::{Write, stdout};
use std::{time, thread};
fn print_wait_screen(terminal: &Context)
use std::rc::Rc;
fn print_wait_screen(context: Rc<Context>)
{
terminal::terminal(&terminal).clear(ClearType::All);
terminal::terminal(context.clone()).clear(ClearType::All);
let mut cursor = cursor(&terminal);
let mut cursor = cursor(context.clone());
cursor.goto(0,0);
{
let mut screen_manager = terminal.screen_manager.lock().unwrap();
let mut screen_manager = context.screen_manager.lock().unwrap();
{
write!(screen_manager.stdout(),
write!(screen_manager,
"Welcome to the wait screen.\n\
Please wait a few seconds until we arrive back at the main screen.\n\
Progress: "
@ -45,10 +45,10 @@ pub fn print_wait_screen_on_alternate_window()
// because `AlternateScreen` switches back to main screen when switching back.
{
// create new alternate screen instance and switch to the alternate screen.
let mut screen = AlternateScreen::from(&context);
let mut screen = AlternateScreen::from(context.clone());
// Print the wait screen.
print_wait_screen(&context);
print_wait_screen(context.clone());
}
println!("Whe are back at the main screen");
@ -57,11 +57,11 @@ pub fn print_wait_screen_on_alternate_window()
pub fn switch_between_main_and_alternate_screen()
{
let context = Context::new();
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
{
// create new alternate screen instance and switch to the alternate screen.
let mut screen = AlternateScreen::from(&context);
let mut screen = AlternateScreen::from(context.clone());
cursor.goto(0,0);
write!(screen, "we are at the alternate screen!");
screen.flush();

View File

@ -7,15 +7,16 @@ use crossterm::terminal::{self, ClearType};
use std::io::{Write, stdout};
use std::{time, thread};
use std::rc::Rc;
use crossterm::raw::IntoRawMode;
// raw screen is not working correctly currently
fn print_wait_screen(context: &Context)
fn print_wait_screen(context: Rc<Context>)
{
terminal::terminal(&context).clear(ClearType::All);
terminal::terminal(context.clone()).clear(ClearType::All);
let mut cursor = cursor(&context);
let mut cursor = cursor(context.clone());
cursor.goto(0,0).print("Welcome to the wait screen.");
cursor.goto(0,1).print("Please wait a few seconds until we arrive back at the main screen.");
cursor.goto(0,2).print("Progress: ");
@ -41,11 +42,11 @@ pub fn print_wait_screen_on_alternate_window()
// create new alternate screen instance this call is also switching the screen to alternate screen.
// then convert the output of the program to raw mode.
// then print the wait screen on the alternate screen in raw mode.
let mut screen = AlternateScreen::from(&context);
let alternate_screen = screen.into_raw_mode(&context);
let mut screen = AlternateScreen::from(context.clone());
let alternate_screen = screen.into_raw_mode(context.clone());
// Print the wait screen.
print_wait_screen(&context);
print_wait_screen(context.clone());
screen.flush();
}

View File

@ -21,7 +21,7 @@ pub fn clear_all_lines()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context);
print_test_data();
@ -35,12 +35,12 @@ pub fn clear_from_cursor_down()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor(&context).goto(4,8);
cursor::cursor(context.clone()).goto(4,8);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorDown);
@ -52,12 +52,12 @@ pub fn clear_from_cursor_up()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor(&context).goto(4,4);
cursor::cursor(context.clone()).goto(4,4);
// Clear all cells from current cursor position down.
terminal.clear(ClearType::FromCursorUp);
@ -69,12 +69,12 @@ pub fn clear_current_line()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor(&context).goto(4,4);
cursor::cursor(context.clone()).goto(4,4);
// Clear current line cells.
terminal.clear(ClearType::CurrentLine);
@ -86,12 +86,12 @@ pub fn clear_until_new_line()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
print_test_data();
// Set terminal cursor position (see example for more info).
cursor::cursor(&context).goto(4,20);
cursor::cursor(context.clone()).goto(4,20);
// Clear all the cells until next line.
terminal.clear(ClearType::UntilNewLine);
@ -103,7 +103,7 @@ pub fn print_terminal_size()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
// Get terminal size
let terminal_size = terminal.terminal_size();
// Print results
@ -115,7 +115,7 @@ pub fn set_terminal_size()
{
let context = Context::new();
let mut terminal = terminal(&context);
let mut terminal = terminal(context);
terminal.set_size(10,10);
}
@ -127,7 +127,7 @@ pub fn scroll_down()
print_test_data();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
// Scroll down 10 lines.
terminal.scroll_down(10);
}
@ -140,7 +140,7 @@ pub fn scroll_up()
print_test_data();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
// Scroll up 10 lines.
terminal.scroll_up(10);
}
@ -151,7 +151,7 @@ pub fn resize_terminal()
let context = Context::new();
// Get terminal
let mut terminal = terminal(&context);
let mut terminal = terminal(context.clone());
// Get terminal size
terminal.set_size(10,10);
}

View File

@ -4,72 +4,91 @@
use Context;
use shared::functions;
use super::ITerminalCursor;
use super::*;
/// This struct is an ansi implementation for cursor related actions.
pub struct AnsiCursor;
pub struct AnsiCursor
{
context: Rc<Context>
}
impl AnsiCursor {
pub fn new() -> Box<AnsiCursor> {
Box::from(AnsiCursor {})
pub fn new(context: Rc<Context>) -> Box<AnsiCursor> {
Box::from(AnsiCursor { context })
}
}
impl ITerminalCursor for AnsiCursor {
fn goto(&self, x: u16, y: u16, context: &Context)
fn goto(&self, x: u16, y: u16)
{
let mut screen = context.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{};{}H"), y + 1, x +1));
}
}
fn pos(&self, context: &Context) -> (u16, u16) {
functions::get_cursor_position(&context)
fn pos(&self) -> (u16, u16) {
functions::get_cursor_position(self.context.clone())
}
fn move_up(&self, count: u16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn move_up(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}A"), count));
}
}
fn move_right(&self, count: u16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn move_right(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}C"), count));
}
}
fn move_down(&self, count: u16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn move_down(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}B"), count));
}
}
fn move_left(&self, count: u16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn move_left(&self, count: u16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}D"), count));
}
}
fn save_position(&mut self, context: &Context)
fn save_position(&mut self)
{
let mut screen = context.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("s"));
}
}
fn reset_position(&self, context: &Context)
fn reset_position(&self)
{
let mut screen = context.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("u"));
}
}
fn hide(&self)
{
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("?25l"));
}
}
fn show(&self)
{
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("?25h"));
}
}
}

View File

@ -8,23 +8,23 @@ use Context;
use super::super::shared::functions;
use std::fmt::Display;
use std::io::Write;
use std::rc::Rc;
/// Struct that stores an specific platform implementation for cursor related actions.
pub struct TerminalCursor<'context> {
context: &'context Context,
pub struct TerminalCursor {
context: Rc<Context>,
terminal_cursor: Option<Box<ITerminalCursor>>,
}
impl <'context> TerminalCursor<'context>
impl TerminalCursor
{
/// Create new cursor instance whereon cursor related actions can be performed.
pub fn new(context: &'context Context) -> TerminalCursor<'context> {
pub fn new(context: Rc<Context>) -> TerminalCursor {
#[cfg(target_os = "windows")]
let cursor = functions::get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new(), context);
let cursor = functions::get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new(context.clone()));
#[cfg(not(target_os = "windows"))]
let cursor = Some(AnsiCursor::new() as Box<ITerminalCursor>);
let cursor = Some(AnsiCursor::new(context.clone()) as Box<ITerminalCursor>);
TerminalCursor { terminal_cursor: cursor , context}
}
@ -39,20 +39,20 @@ impl <'context> TerminalCursor<'context>
/// use self::crossterm::Context;
/// use self::crossterm::cursor;
///
// pub fn goto()
// {
// let context = Context::new();
//
// // Get the cursor
// let mut cursor = cursor(&context);
// // Set the cursor to position X: 10, Y: 5 in the terminal
// cursor.goto(10,5);
// }
/// pub fn goto()
/// {
/// let context = Context::new();
///
/// // Get the cursor
/// let mut cursor = cursor(&context);
/// // Set the cursor to position X: 10, Y: 5 in the terminal
/// cursor.goto(10,5);
/// }
///
/// ```
pub fn goto(&mut self, x: u16, y: u16) -> &mut TerminalCursor<'context> {
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.context);
terminal_cursor.goto(x, y);
}
self
}
@ -80,7 +80,7 @@ impl <'context> TerminalCursor<'context>
/// ```
pub fn pos(&mut self) -> (u16, u16) {
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.pos(&self.context)
terminal_cursor.pos()
} else {
(0, 0)
}
@ -107,9 +107,9 @@ impl <'context> TerminalCursor<'context>
/// }
///
/// ```
pub fn move_up(&mut self, count: u16) -> &mut TerminalCursor<'context> {
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.context);
terminal_cursor.move_up(count);
}
self
}
@ -123,19 +123,19 @@ impl <'context> TerminalCursor<'context>
/// use self::crossterm::Context;
/// use self::crossterm::cursor;
///
// pub fn move_right()
// {
// let context = Context::new();
//
// // Get the cursor
// let mut cursor = cursor(&context);
// // Move the cursor to position 3 times to the right in the terminal
// cursor.move_right(3);
// }
/// pub fn move_right()
/// {
/// let context = Context::new();
///
/// // Get the cursor
/// let mut cursor = cursor(&context);
/// // Move the cursor to position 3 times to the right in the terminal
/// cursor.move_right(3);
/// }
/// ```
pub fn move_right(&mut self, count: u16) -> &mut TerminalCursor<'context> {
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.context);
terminal_cursor.move_right(count);
}
self
}
@ -161,9 +161,9 @@ impl <'context> TerminalCursor<'context>
/// }
///
/// ```
pub fn move_down(&mut self, count: u16) -> &mut TerminalCursor<'context> {
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.context);
terminal_cursor.move_down(count);
}
self
}
@ -189,9 +189,9 @@ impl <'context> TerminalCursor<'context>
/// }
///
/// ```
pub fn move_left(&mut self, count: u16) -> &mut TerminalCursor<'context> {
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.context);
terminal_cursor.move_left(count);
}
self
}
@ -232,12 +232,18 @@ impl <'context> TerminalCursor<'context>
/// .print("@");
///
/// ```
pub fn print<D: Display>(&mut self, value: D) -> &mut TerminalCursor<'context> {
let mut screen = self.context.screen_manager.lock().unwrap();
pub fn print<D: Display>(&mut self, value: D) -> &mut TerminalCursor {
{
write!(screen.stdout(), "{}", value);
// rust is line buffered so we need to flush the buffer in order to print it at the current cursor position.
screen.stdout().flush();
let mut mutex = &self.context.screen_manager;
{
let mut screen_manager = mutex.lock().unwrap();
use std::fmt::Write;
let mut string = String::new();
write!(string, "{}", value).unwrap();
screen_manager.write_ansi(string);
}
}
self
}
@ -261,7 +267,7 @@ impl <'context> TerminalCursor<'context>
pub fn save_position(&mut self)
{
if let Some(ref mut terminal_cursor) = self.terminal_cursor {
terminal_cursor.save_position(&self.context);
terminal_cursor.save_position();
}
}
@ -284,7 +290,7 @@ impl <'context> TerminalCursor<'context>
pub fn reset_position(&mut self)
{
if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.reset_position(&self.context);
terminal_cursor.reset_position();
}
}
}
@ -311,6 +317,6 @@ impl <'context> TerminalCursor<'context>
/// cursor::cursor(&context).goto(5,10);
///
/// ```
pub fn cursor<'context>(context: &'context Context) -> Box<TerminalCursor<'context>> {
Box::from(TerminalCursor::new(&context))
pub fn cursor(context: Rc<Context>) -> Box<TerminalCursor> {
Box::from(TerminalCursor::new(context.clone()))
}

View File

@ -18,9 +18,10 @@ mod ansi_cursor;
use self::winapi_cursor::WinApiCursor;
use self::ansi_cursor::AnsiCursor;
use Context;
pub use self::cursor::{ cursor, TerminalCursor };
use std::rc::Rc;
///! This trait defines the actions that can be preformed with the terminal cursor.
///! This trait can be implemented so that an concrete implementation of the ITerminalCursor can forfill
///! the wishes to work on an specific platform.
@ -31,19 +32,23 @@ pub use self::cursor::{ cursor, TerminalCursor };
///! so that cursor related actions can be preformed on both unix and windows systems.
pub trait ITerminalCursor {
/// Goto some location (x,y) in the context.
fn goto(&self, x: u16, y: u16, context: &Context);
fn goto(&self, x: u16, y: u16);
/// Get the location (x,y) of the current cusror in the context
fn pos(&self, context: &Context) -> (u16, u16);
fn pos(&self) -> (u16, u16);
/// Move cursor n times up
fn move_up(&self, count: u16, context: &Context);
fn move_up(&self, count: u16);
/// Move the cursor `n` times to the right.
fn move_right(&self, count: u16, context: &Context);
fn move_right(&self, count: u16);
/// Move the cursor `n` times down.
fn move_down(&self, count: u16, context: &Context);
fn move_down(&self, count: u16);
/// Move the cursor `n` times left.
fn move_left(&self, count: u16, context: &Context);
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, context: &Context);
fn save_position(&mut self);
/// Return to saved cursor position
fn reset_position(&self, context: &Context);
fn reset_position(&self);
/// Hide the terminal cursor.
fn hide(&self);
/// Show the terminal cursor
fn show(&self);
}

View File

@ -1,8 +1,6 @@
//! This is an WINAPI specific implementation for cursor related action.
//! This module is used for windows terminals that do not support ANSI escape codes.
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position ect.
use Context;
use super::ITerminalCursor;
use kernel::windows_kernel::{kernel, cursor};
@ -17,41 +15,51 @@ impl WinApiCursor {
impl ITerminalCursor for WinApiCursor {
fn goto(&self, x: u16, y: u16, context: &Context) {
fn goto(&self, x: u16, y: u16) {
kernel::set_console_cursor_position(x as i16, y as i16);
}
fn pos(&self, context: &Context) -> (u16, u16) {
fn pos(&self) -> (u16, u16) {
cursor::pos()
}
fn move_up(&self, count: u16, context: &Context) {
let (xpos,ypos) = self.pos(context);
self.goto(xpos, ypos - count, context);
fn move_up(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos, ypos - count);
}
fn move_right(&self, count: u16, context: &Context) {
let (xpos,ypos) = self.pos(context);
self.goto(xpos + count, ypos, context);
fn move_right(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos + count, ypos);
}
fn move_down(&self, count: u16, context: &Context) {
let (xpos,ypos) = self.pos(context);
self.goto(xpos, ypos + count,context);
fn move_down(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos, ypos + count);
}
fn move_left(&self, count: u16, context: &Context) {
let (xpos,ypos) = self.pos(context);
self.goto(xpos - count, ypos,context);
fn move_left(&self, count: u16) {
let (xpos,ypos) = self.pos();
self.goto(xpos - count, ypos);
}
fn save_position(&mut self, context: &Context)
fn save_position(&mut self)
{
cursor::save_cursor_pos();
}
fn reset_position(&self, context: &Context)
fn reset_position(&self)
{
cursor::reset_to_saved_position();
}
fn hide(&self)
{
kernel::cursor_visibility(false);
}
fn show(&self)
{
kernel::cursor_visibility(true);
}
}

View File

@ -1,24 +1,27 @@
//! This module handles the enabling `ANSI escape codes` for windows terminals.
use {Context, StateManager};
use state::commands::ICommand;
use IStateCommand;
use std::sync::{Once, ONCE_INIT};
static mut HAS_BEEN_TRYED_TO_ENABLE: bool = false;
static mut IS_ANSI_ON_WINDOWS_ENABLED: Option<bool> = None;
static mut DOES_WINDOWS_SUPPORT_ANSI: Option<bool> = None;
static START: Once = ONCE_INIT;
/// Try enable `ANSI escape codes` and return the result.
pub fn try_enable_ansi_support(context: &Context) -> bool
pub fn try_enable_ansi_support() -> bool
{
use state::commands::win_commands::EnableAnsiCommand;
let mut command = EnableAnsiCommand::new();
let success = command.execute(&context);
START.call_once(|| {
use state::commands::win_commands::EnableAnsiCommand;
let mut command = EnableAnsiCommand::new();
let success = command.execute();
set_is_windows_ansi_supportable(success);
set_ansi_enabled(success);
has_been_tried_to_enable(true);
set_is_windows_ansi_supportable(success);
set_ansi_enabled(success);
has_been_tried_to_enable(true);
success
});
windows_supportable()
}
/// Get whether ansi has been enabled.

View File

@ -1,18 +1,17 @@
//! This module is the core of all the `WINAPI` actions. All unsafe `WINAPI` function call are done here.
use winapi::um::winnt::HANDLE;
use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE };
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::{GetStdHandle};
use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode, };
use winapi::shared::minwindef::{TRUE};
use winapi::shared::minwindef::{TRUE, FALSE};
use winapi::um::wincon;
use winapi::um::wincon::
{
SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, CreateConsoleScreenBuffer,SetConsoleActiveScreenBuffer,
SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, CreateConsoleScreenBuffer,SetConsoleActiveScreenBuffer, SetConsoleCursorInfo,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
FillConsoleOutputCharacterA, FillConsoleOutputAttribute,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, CHAR_INFO, PSMALL_RECT
FillConsoleOutputCharacterA, FillConsoleOutputAttribute,WriteConsoleOutputCharacterA,WriteConsoleOutputAttribute,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, CHAR_INFO, PSMALL_RECT, CONSOLE_CURSOR_INFO
};
use super::{Empty};
@ -132,6 +131,22 @@ pub fn set_console_cursor_position(x: i16, y: i16)
}
}
pub fn cursor_visibility(visable: bool)
{
let handle = get_output_handle();
let cursor_info = CONSOLE_CURSOR_INFO
{
dwSize: 100,
bVisible: if visable { FALSE } else {TRUE}
};
unsafe
{
SetConsoleCursorInfo(handle, &cursor_info);
}
}
pub fn set_console_text_attribute(value: u16)
{
let output_handle = get_output_handle();
@ -282,6 +297,32 @@ pub fn write_console_output(write_buffer: &HANDLE, copy_buffer: &mut [CHAR_INFO;
}
}
pub fn write_char_buffer(handle: HANDLE, buf: &[u8])
{
use std::ffi::CString;
use std::str;
// get buffer info
let csbi = get_console_screen_buffer_info();
// get string from u8[] and parse it to an c_str
let mut data = str::from_utf8(buf).unwrap();
let c_str = CString::new(data);
let ptr: *const u16 = c_str.unwrap().as_ptr() as *const u16;
// get current position
let current_pos = COORD {X: csbi.dwCursorPosition.X, Y: csbi.dwCursorPosition.Y};
let mut cells_written: u32 = 0;
// write to console
unsafe
{
WriteConsoleOutputAttribute(handle, ptr, data.len() as u32, current_pos, &mut cells_written);
}
}
/// Parse integer to an bool
fn is_true(value: i32) -> bool
{

View File

@ -16,6 +16,7 @@ pub mod manager;
pub use shared::{screen, raw};
pub use state::context::Context;
use state::commands::IStateCommand;
use state::command_manager::CommandManager;
use state::state_manager::StateManager;
use manager::ScreenManager;

View File

@ -3,6 +3,7 @@
//! This module uses the stdout to write to the console.
use std::io::{self, Write};
use std::any::Any;
use super::IScreenManager;
@ -14,12 +15,11 @@ pub struct AnsiScreenManager
impl IScreenManager for AnsiScreenManager
{
type Output = Box<Write>;
fn stdout(&mut self) -> &mut Self::Output
{
return &mut self.output
}
// fn stdout(&mut self) -> &mut Self::Output
// {
// return &mut self.output
// }
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool)
{
@ -37,6 +37,19 @@ impl IScreenManager for AnsiScreenManager
write!(self.output, "{}", string);
self.flush();
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
fn as_any(&mut self) -> &mut Any
{
self
}
}
impl AnsiScreenManager {
@ -47,14 +60,3 @@ impl AnsiScreenManager {
}
}
}
impl Write for AnsiScreenManager
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stdout().flush()
}
}

View File

@ -1,39 +1,38 @@
//! This module provides an interface for working with the sceen. With that I mean that you can get or wirte to the handle of the current screen. stdout.
//! Because crossterm can work with alternate screen, we need a place that holds the handle to the current screen. And this module provides this place.
use super::{AnsiScreenManager, WinApiScreenManager, IScreenManager };
use super::super::shared::functions;
use std::any::Any;
use super::IScreenManager;
use super::ansi_manager::AnsiScreenManager;
use std::fmt::Display;
use std::io::{ self, Write };
use std::io::Write;
#[cfg(target_os = "windows")]
use winapi::um::winnt::HANDLE;
/// Struct that stores an specific platform implementation for screen related actions.
pub struct ScreenManager
{
screen_manager: Box<IScreenManager<Output=Box<Write>>>
screen_manager: Box<IScreenManager>
}
impl ScreenManager
{
/// Create new screen manager instance whereon screen related actions can be performed.
pub fn new() -> ScreenManager {
// #[cfg(target_os = "windows")]
// let cursor = functions::get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new());
//
// #[cfg(not(target_os = "windows"))]
// #[cfg(target_os = "windows")]
// let screen_manager = functions::get_module::<Box<IScreenManager>>(Box::from(WinApiScreenManager::new()), Box::from(AnsiScreenManager::new())).unwrap();
//
// #[cfg(not(target_os = "windows"))]
// let screen_manager = Box::new(AnsiScreenManager::new());
ScreenManager
{
screen_manager: Box::new(AnsiScreenManager::new()),
screen_manager: Box::from(WinApiScreenManager::new()),
}
}
/// Get the stdout of the current screen
pub fn stdout(&mut self) -> &mut Box<Write>
{
self.screen_manager.stdout()
}
pub fn toggle_is_alternate_screen(&mut self,is_alternate_screen: bool)
pub fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool)
{
self.screen_manager.toggle_is_alternate_screen(is_alternate_screen);
}
@ -49,4 +48,17 @@ impl ScreenManager
{
self.screen_manager.write_ansi_str(string);
}
pub fn as_any(&mut self) -> &mut Any { self.screen_manager.as_any() }
}
impl Write for ScreenManager
{
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.screen_manager.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.screen_manager.flush()
}
}

View File

@ -8,20 +8,26 @@ mod win_manager;
mod ansi_manager;
#[cfg(target_os = "windows")]
use self::win_manager::WinApiScreenManager;
use self::ansi_manager::AnsiScreenManager;
pub use self::win_manager::WinApiScreenManager;
pub use self::ansi_manager::AnsiScreenManager;
pub use self::manager::{ ScreenManager };
use std::io;
use std::any::Any;
pub trait IScreenManager
{
type Output;
/// get the stdout of the screen. This can be used to write to the
fn stdout(&mut self) -> &mut Self::Output;
// fn stdout(&mut self) -> &mut Self::Output;
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);
/// Write a &str to the current stdout.
fn write_ansi_str(&mut self, string: &str);
fn write(&mut self, buf: &[u8]) -> io::Result<usize>;
fn flush(&mut self) -> io::Result<()>;
fn as_any(&mut self) -> &mut Any;
}

View File

@ -2,21 +2,18 @@ use super::IScreenManager;
use kernel::windows_kernel::kernel;
use winapi::um::winnt::HANDLE;
use std::io::{self,Write};
use std::any::Any;
pub struct WinApiScreenManager
{
pub is_alternate_screen: bool,
output: Box<HANDLE>,
output: HANDLE,
alternate_handle: HANDLE
}
impl IScreenManager for WinApiScreenManager where
impl IScreenManager for WinApiScreenManager
{
type Output = HANDLE;
fn stdout(&mut self) -> &mut Self::Output
{
return &mut self.output
}
fn toggle_is_alternate_screen(&mut self, is_alternate_screen: bool)
{
self.is_alternate_screen = is_alternate_screen;
@ -33,24 +30,39 @@ impl IScreenManager for WinApiScreenManager where
// write!(self.output, "{}", string);
// self.flush();
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.is_alternate_screen
{
kernel::write_char_buffer(self.alternate_handle, buf);
}
else {
kernel::write_char_buffer(self.output, buf);
}
Ok(0)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
fn as_any(&mut self) -> &mut Any
{
self
}
}
impl WinApiScreenManager {
pub fn new() -> Self {
WinApiScreenManager {
output: (Box::from(kernel::get_output_handle())),
is_alternate_screen: false
output: kernel::get_output_handle(),
is_alternate_screen: false,
alternate_handle: kernel::get_output_handle(),
}
}
}
//impl<Output:Write> Write for AnsiScreenManager<Output>
//{
// fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
// self.output.write(buf)
// }
//
// fn flush(&mut self) -> io::Result<()> {
// self.stdout().flush()
// }
//}
pub fn set_alternate_handle(&mut self, alternate_handle: HANDLE)
{
self.alternate_handle = alternate_handle;
}
}

View File

@ -1,6 +1,7 @@
//! Some actions need to preformed platform independently since they can not be solved `ANSI escape codes`.
use Context;
use std::rc::Rc;
#[cfg(windows)]
use kernel::windows_kernel::terminal::terminal_size;
@ -20,7 +21,7 @@ pub fn get_terminal_size() -> (u16, u16)
}
/// Get the cursor position based on the current platform.
pub fn get_cursor_position(screen: &Context) -> (u16, u16)
pub fn get_cursor_position(screen: Rc<Context>) -> (u16, u16)
{
#[cfg(unix)]
return pos(&screen);
@ -31,7 +32,7 @@ pub fn get_cursor_position(screen: &Context) -> (u16, u16)
#[cfg(windows)]
/// Get an module specific implementation based on the current platform.
pub fn get_module<T>(winapi_impl: T, unix_impl: T, context: &Context) -> Option<T>
pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T>
{
let mut term: Option<T> = None;
let mut does_support = true;
@ -41,7 +42,7 @@ pub fn get_module<T>(winapi_impl: T, unix_impl: T, context: &Context) -> Option<
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(&context);
does_support = try_enable_ansi_support();
if !does_support
{

View File

@ -31,56 +31,56 @@ use std::io::{ self, Write};
use std::rc::Rc;
/// A wrapper for the raw terminal state. Which can be used to write to.
pub struct RawTerminal<'a>
pub struct RawTerminal
{
terminal : &'a Context,
context : Rc<Context>,
command_id: u16,
}
/// Trait withs contains a method for switching into raw mode.
pub trait IntoRawMode<'a>: Write + Sized
pub trait IntoRawMode: Write + Sized
{
fn into_raw_mode(&self, terminal: &'a Context) -> io::Result<RawTerminal<'a>>;
fn into_raw_mode(&self, context:Rc<Context>) -> io::Result<RawTerminal>;
}
impl <'a, W: Write> IntoRawMode<'a> for W
impl <W: Write> IntoRawMode for W
{
/// Raw mode means that input (stdin) won't be printed it will instead have to be written manually by
/// the program. The input isn't canonicalised or line buffered (that is, you can
/// read from input(stdin) one byte of a time).
fn into_raw_mode(&self, terminal: &'a Context) -> io::Result<RawTerminal<'a>> {
let command_id = EnableRawModeCommand::new(&terminal.state_manager);
fn into_raw_mode(&self, context: Rc<Context>) -> io::Result<RawTerminal> {
let command_id = EnableRawModeCommand::new(&context.state_manager);
let success = CommandManager::execute(terminal, command_id);
let success = CommandManager::execute(context.clone(), command_id);
if success
{
Ok(RawTerminal { terminal: &terminal, command_id: command_id})
Ok(RawTerminal { context: context.clone(), command_id: command_id})
}else { panic!("cannot move into raw mode") }
}
}
impl<'a> Write for RawTerminal<'a> {
impl Write for RawTerminal {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut screen = self.terminal.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.stdout().write(buf)
screen.write(buf)
}
}
fn flush(&mut self) -> io::Result<()> {
let mut screen = self.terminal.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.stdout().flush()
screen.flush()
}
}
}
/// If an instance of `RawTerminal` will be dropped all terminal changes that are made will be undone.
impl <'a> Drop for RawTerminal<'a>
impl Drop for RawTerminal
{
fn drop(&mut self)
{
let success = CommandManager::undo(&self.terminal, self.command_id);
let success = CommandManager::undo(self.context.clone(), self.command_id);
}
}

View File

@ -2,67 +2,97 @@
use Context;
use state::commands::*;
use super::functions;
use std::io::{self, Write};
pub struct AlternateScreen<'context> {
context: &'context Context
use std::io::{self, Write};
use std::rc::Rc;
pub struct AlternateScreen {
context: Rc<Context>,
command_id: u16
}
impl<'context> AlternateScreen<'context> {
impl AlternateScreen {
/// Get the alternate screen from the context.
/// By calling this method the current screen will be changed to the alternate screen.
/// And you get back an handle for that screen.
pub fn from(context: &'context Context) -> Self {
get_to_alternate_screen_command(context).execute(&context);
AlternateScreen { context: context }
pub fn from(context: Rc<Context>) -> Self {
let command_id = get_to_alternate_screen_command(context.clone());
let screen = AlternateScreen { context: context, command_id: command_id };
screen.to_alternate();
return screen;
}
/// Change the current screen to the mainscreen.
pub fn to_main(&self)
{
get_to_alternate_screen_command(&self.context).undo(&self.context);
let mut mutex = &self.context.state_manager;
{
let mut state_manager = mutex.lock().unwrap();
let mut mx = &state_manager.get(self.command_id);
{
let mut command = mx.lock().unwrap();
command.undo();
}
}
}
/// Change the current screen to alternate screen.
pub fn to_alternate(&self)
{
get_to_alternate_screen_command(&self.context).execute(&self.context);
let mut mutex = &self.context.state_manager;
{
let mut state_manager = mutex.lock().unwrap();
let mut mx = &state_manager.get(self.command_id);
{
let mut command = mx.lock().unwrap();
command.execute();
}
}
}
}
impl<'context> Write for AlternateScreen<'context> {
impl Write for AlternateScreen {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.stdout().write(buf)
screen.write(buf)
}
}
fn flush(&mut self) -> io::Result<()> {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.stdout().flush()
screen.flush()
}
}
}
impl<'context> Drop for AlternateScreen<'context>
impl Drop for AlternateScreen
{
fn drop(&mut self)
{
get_to_alternate_screen_command(&self.context).undo(&self.context);
let mut mutex = &self.context.state_manager;
{
let mut state_manager = mutex.lock().unwrap();
let mut mx = &state_manager.get(self.command_id);
{
let mut command = mx.lock().unwrap();
command.undo();
}
}
}
}
// Get the alternate screen command to enable and disable alternate screen based on the current platform
fn get_to_alternate_screen_command(context: &Context) -> Box<ICommand>
fn get_to_alternate_screen_command(context: Rc<Context>) -> u16
{
#[cfg(target_os = "windows")]
let command = functions::get_module::<Box<ICommand>>(win_commands::ToAlternateScreenBufferCommand::new(), shared_commands::ToAlternateScreenBufferCommand::new(), context).unwrap();
// #[cfg(target_os = "windows")]
// let command = functions::get_module::<Box<ICommand>>(win_commands::ToAlternateScreenBufferCommand::new(), shared_commands::ToAlternateScreenBufferCommand::new(), context).unwrap();
#[cfg(not(target_os = "windows"))]
let command = shared_commands::ToAlternateScreenBufferCommand::new();
command
// #[cfg(not(target_os = "windows"))]
win_commands::ToAlternateScreenBufferCommand::new(context)
}

View File

@ -9,32 +9,32 @@ pub struct CommandManager;
impl CommandManager
{
/// execute an certain command by id.
pub fn execute(terminal: &Context, command_id: u16) -> bool
pub fn execute(context: Rc<Context>, command_id: u16) -> bool
{
let mut mutex: Rc<Mutex<Box<IStateCommand>>>;
let mut state = terminal.state_manager.lock().unwrap();
let mut state = context.state_manager.lock().unwrap();
{
mutex = state.get(command_id);
}
let mut command = mutex.lock().unwrap();
let has_succeeded = command.execute(&terminal);
let has_succeeded = command.execute();
return has_succeeded;
}
/// undo an certain command by id.
pub fn undo(terminal: &Context, command_id: u16) -> bool
pub fn undo(context: Rc<Context>, command_id: u16) -> bool
{
let mut mutex: Rc<Mutex<Box<IStateCommand>>>;
let mut state = terminal.state_manager.lock().unwrap();
let mut state = context.state_manager.lock().unwrap();
{
mutex = state.get(command_id);
}
let mut command = mutex.lock().unwrap();
let has_succeeded = command.undo(&terminal);
let has_succeeded = command.undo();
return has_succeeded;
}
}

View File

@ -25,21 +25,11 @@ pub use self::unix_command::*;
#[cfg(windows)]
pub use self::win_commands::*;
use {StateManager, Context};
/// This command can be used for simple commands witch just have an `undo()` and an `execute()`
pub trait ICommand
{
fn new() -> Box<Self> where Self: Sized;
fn execute(&mut self, terminal: &Context) -> bool;
fn undo(&mut self, terminal: &Context) -> bool;
}
/// This command is used for complex commands whits change the terminal state.
/// By passing an `Context` instance this command will register it self to notify the terminal state change.
pub trait IStateCommand
{
fn execute(&mut self, terminal: &Context) -> bool;
fn undo(&mut self, terminal: &Context) -> bool;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}

View File

@ -1,35 +1,40 @@
//! This module contains the commands that can be used for both unix and windows systems. Or else said terminals that support ansi codes.
use Context;
use super::{ICommand, IStateCommand};
use super::{IStateCommand};
pub struct EmptyCommand;
impl IStateCommand for EmptyCommand
{
fn execute(&mut self, terminal: &Context) -> bool
fn execute(&mut self) -> bool
{
return false
}
fn undo(&mut self, terminal: &Context) -> bool
fn undo(&mut self) -> bool
{
return false;
}
}
/// This command is used for switching to alternate screen and back to main screen.
#[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand;
impl ICommand for ToAlternateScreenBufferCommand
pub struct ToAlternateScreenBufferCommand<'a>
{
fn new() -> Box<ToAlternateScreenBufferCommand> {
Box::from(ToAlternateScreenBufferCommand {})
}
context: &'a Context
}
fn execute(&mut self, terminal: &Context) -> bool
impl<'a> ToAlternateScreenBufferCommand<'a>
{
pub fn new(context: & 'a Context) -> Box < ToAlternateScreenBufferCommand > {
Box::from(ToAlternateScreenBufferCommand {context: context})
}
}
impl<'context> IStateCommand for ToAlternateScreenBufferCommand<'context>
{
fn execute(&mut self) -> bool
{
let mut screen = terminal.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("?1049h"));
screen.toggle_is_alternate_screen(true);
@ -37,9 +42,9 @@ impl ICommand for ToAlternateScreenBufferCommand
}
}
fn undo(&mut self, terminal: &Context) -> bool
fn undo(&mut self) -> bool
{
let mut screen = terminal.screen_manager.lock().unwrap();
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi_str(csi!("?1049l"));
screen.toggle_is_alternate_screen(false);

View File

@ -34,7 +34,7 @@ impl NoncanonicalModeCommand
impl IStateCommand for NoncanonicalModeCommand
{
fn execute(&mut self, terminal: &Context) -> bool
fn execute(&mut self) -> bool
{
// Set noncanonical mode
if let Ok(orig) = Termios::from_fd(FD_STDIN)
@ -53,7 +53,7 @@ impl IStateCommand for NoncanonicalModeCommand
}
}
fn undo(&mut self, terminal: &Context) -> bool
fn undo(&mut self) -> bool
{
// Disable noncanonical mode
if let Ok(orig) = Termios::from_fd(FD_STDIN)
@ -97,7 +97,7 @@ impl EnableRawModeCommand
impl IStateCommand for EnableRawModeCommand
{
fn execute(&mut self, terminal: &Context) -> bool
fn execute(&mut self) -> bool
{
let original_mode = terminal::get_terminal_mode();
@ -114,7 +114,7 @@ impl IStateCommand for EnableRawModeCommand
}
}
fn undo(&mut self, terminal: &Context) -> bool
fn undo(&mut self) -> bool
{
if let Some(ref original_mode) = self.original_mode
{

View File

@ -1,6 +1,6 @@
//! This module contains the commands that can be used for windows systems.
use super::{ICommand, IStateCommand};
use super::IStateCommand;
use { StateManager, Context };
use kernel::windows_kernel::{kernel, ansi_support};
@ -20,14 +20,18 @@ pub struct EnableAnsiCommand
mask: DWORD,
}
impl ICommand for EnableAnsiCommand
impl EnableAnsiCommand
{
fn new() -> Box<EnableAnsiCommand> {
pub fn new() -> Box<EnableAnsiCommand> {
let command = EnableAnsiCommand { mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING };
Box::from(command)
}
}
fn execute(&mut self, context: &Context) -> bool
impl IStateCommand for EnableAnsiCommand
{
fn execute(&mut self,) -> bool
{
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded.
if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled()
@ -39,14 +43,12 @@ impl ICommand for EnableAnsiCommand
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
panic!("Cannot get console mode");
return false;
}
dw_mode |= self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode)
{
panic!("Cannot get console mode");
return false;
}
@ -54,7 +56,7 @@ impl ICommand for EnableAnsiCommand
}
}
fn undo(&mut self, context: &Context) -> bool
fn undo(&mut self) -> bool
{
if ansi_support::ansi_enabled()
{
@ -90,8 +92,7 @@ pub struct EnableRawModeCommand
impl EnableRawModeCommand
{
pub fn new(state_manager: &Mutex<StateManager>) -> u16 {
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_PROCESSED_OUTPUT, ENABLE_WRAP_AT_EOL_OUTPUT, ENABLE_ECHO_INPUT};
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_ECHO_INPUT};
let mut state = state_manager.lock().unwrap();
{
@ -105,10 +106,8 @@ impl EnableRawModeCommand
impl IStateCommand for EnableRawModeCommand
{
fn execute(&mut self, context: &Context) -> bool
fn execute(&mut self) -> bool
{
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_ECHO_INPUT};
let input_handle = kernel::get_input_handle();
let mut dw_mode: DWORD = 0;
@ -127,7 +126,7 @@ impl IStateCommand for EnableRawModeCommand
true
}
fn undo(&mut self, context: &Context) -> bool
fn undo(&mut self) -> bool
{
let output_handle = kernel::get_output_handle();
@ -150,18 +149,39 @@ impl IStateCommand for EnableRawModeCommand
/// This command is used for switching to alternate screen and back to main screen.
/// check https://docs.microsoft.com/en-us/windows/console/reading-and-writing-blocks-of-characters-and-attributes for more info
#[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand;
impl ICommand for ToAlternateScreenBufferCommand
pub struct ToAlternateScreenBufferCommand
{
fn new() -> Box<ToAlternateScreenBufferCommand>
{
Box::from(ToAlternateScreenBufferCommand {})
}
context: Rc<Context>
}
fn execute(&mut self, context: &Context) -> bool
impl ToAlternateScreenBufferCommand
{
pub fn new(context: Rc<Context>) -> u16
{
let key = 2;
let mut state = context.state_manager.lock().unwrap();
{
let command = ToAlternateScreenBufferCommand {context: context.clone()};
state.register_change(Box::from(command), key);
key
}
}
}
impl IStateCommand for ToAlternateScreenBufferCommand
{
fn execute(&mut self) -> bool
{
use super::super::super::kernel::windows_kernel::ansi_support;
use super::super::super::manager::WinApiScreenManager;
let does_support = ansi_support::try_enable_ansi_support();
let mut screen_manager = self.context.screen_manager.lock().unwrap();
screen_manager.toggle_is_alternate_screen(true);
let mut chi_buffer: [CHAR_INFO;160] = unsafe {mem::zeroed() };
let handle = kernel::get_output_handle();
@ -172,6 +192,13 @@ impl ICommand for ToAlternateScreenBufferCommand
// Make the new screen buffer the active screen buffer.
kernel::set_active_screen_buffer(new_handle);
let b: &mut WinApiScreenManager = match screen_manager.as_any().downcast_mut::<WinApiScreenManager>() {
Some(b) => { b },
None => panic!("&a isn't a B!")
};
b.set_alternate_handle(new_handle);
// Set the source rectangle.
let mut srct_read_rect = SMALL_RECT
{
@ -214,10 +241,16 @@ impl ICommand for ToAlternateScreenBufferCommand
true
}
fn undo(&mut self, context: &Context) -> bool
fn undo(&mut self) -> bool
{
let handle = kernel::get_output_handle();
kernel::set_active_screen_buffer(handle);
{
let mut screen_manager = self.context.screen_manager.lock().unwrap();
screen_manager.toggle_is_alternate_screen(false);
}
true
}
}

View File

@ -1,13 +1,9 @@
//! This module contains the code for the context of the terminal.
use { StateManager, ScreenManager };
use std::sync::Mutex;
use std::rc::Rc;
use super::super::style;
use { StateManager, ScreenManager };
use std::fmt;
/// This type contains the context of the current terminal. The context surrounds the changed states of the terminal and can be used for managing the output of the terminal.
pub struct Context
{
@ -39,46 +35,12 @@ impl Context
/// let color = terminal::color(&context);
///
/// ```
pub fn new() -> Context
pub fn new() -> Rc<Context>
{
Context {
Rc::new(Context {
screen_manager: Rc::new(Mutex::new(ScreenManager::new())),
state_manager: Mutex::new(StateManager::new())
}
}
/// Wraps an displayable object so it can be formatted with colors and attributes.
///
/// Check `/examples/color` in the libary for more spesific examples.
///
/// #Example
///
/// ```rust
/// extern crate crossterm;
///
/// use self::crossterm::style::{paint,Color};
///
/// fn main()
/// {
/// // Create an styledobject object from the text 'Unstyled font'
/// // Currently it has the default foregroundcolor and backgroundcolor.
/// println!("{}",paint("Unstyled font"));
///
/// // Create an displayable object from the text 'Colored font',
/// // Paint this with the `Red` foreground color and `Blue` backgroundcolor.
/// // Print the result.
/// let styledobject = paint("Colored font").with(Color::Red).on(Color::Blue);
/// println!("{}", styledobject);
///
/// // Or all in one line
/// println!("{}", paint("Colored font").with(Color::Red).on(Color::Blue));
/// }
/// ```
pub fn paint<D>(&self, val: D) -> style::StyledObject<D>
where
D: fmt::Display,
{
style::ObjectStyle::new().apply_to(val, &self)
})
}
}
@ -87,6 +49,6 @@ impl Drop for Context
fn drop(&mut self)
{
let mut changes = self.state_manager.lock().unwrap();
changes.restore_changes(&self);
changes.restore_changes();
}
}

View File

@ -1,11 +1,9 @@
//! This module is used for registering, storing an restoring the terminal state changes.
use Context;
use super::commands::IStateCommand;
use super::commands::shared_commands::EmptyCommand;
use std::rc::Rc;
use std::sync::Mutex;
use std::collections::HashMap;
/// Struct that stores the changed states of the terminal.
@ -25,12 +23,12 @@ impl StateManager
}
/// Restore all changes that are made to the terminal.
pub fn restore_changes(&mut self, context: &Context)
pub fn restore_changes(&mut self)
{
for (id, item) in self.changed_states.iter_mut()
{
let mut item = item.lock().unwrap();
item.undo(&context);
item.undo();
}
}

View File

@ -17,9 +17,9 @@ pub struct TerminalColor {
impl TerminalColor {
/// Create new instance whereon color related actions can be performed.
pub fn new(context: &Context ) -> TerminalColor {
pub fn new(context: Rc<Context>) -> TerminalColor {
#[cfg(target_os = "windows")]
let color = functions::get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new(), context);
let color = functions::get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new());
#[cfg(not(target_os = "windows"))]
let color = Some(AnsiColor::new() as Box<ITerminalColor>);
@ -127,6 +127,6 @@ impl TerminalColor {
///
/// Check `/examples/version/color` in the libary for more specific examples.
///
pub fn color(context: &Context) -> Box<TerminalColor> {
pub fn color(context: Rc<Context>) -> Box<TerminalColor> {
Box::from(TerminalColor::new(context))
}

View File

@ -1,9 +1,9 @@
//! This module contains the `object style` that can be applied to an `styled object`.
use std::fmt::Display;
use Context;
use style::{Color, StyledObject};
use { ScreenManager, Context };
use std::sync::Mutex;
use std::fmt::Display;
use std::rc::Rc;
#[cfg(unix)]
@ -32,7 +32,7 @@ impl Default for ObjectStyle {
impl ObjectStyle {
/// Apply an `StyledObject` to the passed displayable object.
pub fn apply_to<'a, D>(&self, val: D, context: &'a Context) -> StyledObject<'a, D>
pub fn apply_to<D>(&self, val: D, context: Rc<Context>) -> StyledObject<D>
where
D: Display,
{

View File

@ -1,10 +1,10 @@
//! This module contains the logic to style an object that contains some state witch can be styled.
use Context;
use std::fmt;
use std::io::Write;
use std::sync::Mutex;
use std::rc::Rc;
use { ScreenManager, Context};
#[cfg(unix)]
use super::super::Attribute;
@ -12,13 +12,13 @@ use super::super::Attribute;
use style::{Color, ObjectStyle};
/// Struct that contains both the style and the content wits can be styled.
pub struct StyledObject<'a, D> {
pub struct StyledObject<D> {
pub object_style: ObjectStyle,
pub content: D,
pub context: &'a Context
pub context: Rc<Context>
}
impl<'a, D> StyledObject<'a,D> {
impl<D> StyledObject<D> {
/// Set the foreground of the styled object to the passed `Color`
///
/// #Example
@ -39,7 +39,7 @@ impl<'a, D> StyledObject<'a,D> {
/// println!("{}", paint("I am colored green").with(Color::Green));
///
/// ```
pub fn with(mut self, foreground_color: Color) -> StyledObject<'a,D> {
pub fn with(mut self, foreground_color: Color) -> StyledObject<D> {
self.object_style = self.object_style.fg(foreground_color);
self
}
@ -64,7 +64,7 @@ impl<'a, D> StyledObject<'a,D> {
/// println!("{}", paint("I am colored green").on(Color::Green))
///
/// ```
pub fn on(mut self, background_color: Color) -> StyledObject<'a,D> {
pub fn on(mut self, background_color: Color) -> StyledObject<D> {
self.object_style = self.object_style.bg(background_color);
self
}
@ -82,7 +82,7 @@ impl<'a, D> StyledObject<'a,D> {
///
/// ```
#[cfg(unix)]
pub fn attr(mut self, attr: Attribute) -> StyledObject<'a,D>
pub fn attr(mut self, attr: Attribute) -> StyledObject<D>
{
&self.object_style.add_attr(attr);
self
@ -113,10 +113,10 @@ impl<'a, D> StyledObject<'a,D> {
macro_rules! impl_fmt
{
($name:ident) => {
impl<'a, D: fmt::$name> fmt::$name for StyledObject<'a, D> {
impl<D: fmt::$name> fmt::$name for StyledObject<D> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
{
let mut colored_terminal = super::super::color(&self.context);
let mut colored_terminal = super::super::color(self.context.clone());
let mut reset = true;
if let Some(bg) = self.object_style.bg_color
@ -132,7 +132,11 @@ macro_rules! impl_fmt
#[cfg(unix)]
for attr in self.object_style.attrs.iter() {
write!(f, csi!("{}m"), *attr as i16);
let mutex = self.context.screen_manager;
{
let mut screen = mutex.lock().unwrap();
screen.write_ansi_str(format!(csi!("{}m"), *attr as i16)).expect("Flush failed");
}
reset = true;
}
@ -141,7 +145,8 @@ macro_rules! impl_fmt
let mutex = &self.context.screen_manager;
{
let mut screen = mutex.lock().unwrap();
screen.stdout().flush().expect("Flush stdout failed");
screen.flush().expect("Flush failed");
}
if reset

View File

@ -3,66 +3,65 @@
use Context;
use shared::functions;
use super::{ClearType, ITerminal};
use std::io::Write;
use super::{ClearType, ITerminal, Rc};
/// This struct is an ansi implementation for terminal related actions.
pub struct AnsiTerminal;
pub struct AnsiTerminal
{
context: Rc<Context>
}
impl AnsiTerminal {
pub fn new() -> Box<AnsiTerminal> {
Box::from(AnsiTerminal {})
pub fn new(context: Rc<Context>) -> Box<AnsiTerminal> {
Box::from(AnsiTerminal {context: context})
}
}
impl ITerminal for AnsiTerminal {
fn clear(&self, clear_type: ClearType, context: &Context) {
let mut screen_manager = context.screen_manager.lock().unwrap();
fn clear(&self, clear_type: ClearType)
{
let mut screen_manager = self.context.screen_manager.lock().unwrap();
{
let stdout = screen_manager.stdout();
match clear_type {
ClearType::All => {
write!(stdout, csi!("2J"));
screen_manager.write_ansi_str(csi!("2J"));
},
ClearType::FromCursorDown => {
write!(stdout, csi!("J"));
screen_manager.write_ansi_str(csi!("J"));
},
ClearType::FromCursorUp => {
write!(stdout, csi!("1J"));
screen_manager.write_ansi_str(csi!("1J"));
},
ClearType::CurrentLine => {
write!(stdout, csi!("2K"));
screen_manager.write_ansi_str(csi!("2K"));
},
ClearType::UntilNewLine => {
write!(stdout, csi!("K"));
screen_manager.write_ansi_str(csi!("K"));
},
};
}
}
fn terminal_size(&self, context: &Context) -> (u16, u16) {
fn terminal_size(&self) -> (u16, u16) {
functions::get_terminal_size()
}
fn scroll_up(&self, count: i16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn scroll_up(&self, count: i16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}S"), count));
}
}
fn scroll_down(&self, count: i16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
fn scroll_down(&self, count: i16) {
let mut screen = self.context.screen_manager.lock().unwrap();
{
screen.write_ansi(format!(csi!("{}T"), count));
}
}
fn set_size(&self, width: i16, height: i16, context: &Context) {
let mut screen = context.screen_manager.lock().unwrap();
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));
}

View File

@ -15,6 +15,7 @@ mod ansi_terminal;
#[cfg(target_os = "windows")]
use self::winapi_terminal::WinApiTerminal;
use self::ansi_terminal::AnsiTerminal;
use std::rc::Rc;
use Context;
pub use self::terminal::{ terminal};
@ -38,13 +39,13 @@ pub enum ClearType {
///! so that cursor related actions can be preformed on both unix and windows systems.
pub trait ITerminal {
/// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType, context: &Context);
fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y)
fn terminal_size(&self, context: &Context) -> (u16, u16);
fn terminal_size(&self) -> (u16, u16);
/// Scroll `n` lines up in the current terminal.
fn scroll_up(&self, count: i16, context: &Context);
fn scroll_up(&self, count: i16);
/// Scroll `n` lines down in the current terminal.
fn scroll_down(&self, count: i16, context: &Context);
fn scroll_down(&self, count: i16);
/// Resize terminal to the given width and height.
fn set_size(&self,width: i16, height: i16, context: &Context);
fn set_size(&self,width: i16, height: i16);
}

View File

@ -3,25 +3,26 @@
use super::*;
use Context;
use std::fmt;
use super::super::style;
use super::super::shared::functions;
use std::fmt;
use std::rc::Rc;
/// Struct that stores an specific platform implementation for terminal related actions.
pub struct Terminal<'context> {
pub struct Terminal {
terminal: Option<Box<ITerminal>>,
context: &'context Context
context: Rc<Context>
}
impl<'context> Terminal<'context> {
impl Terminal {
/// Create new terminal instance whereon terminal related actions can be performed.
pub fn new(context: &'context Context) -> Terminal<'context> {
pub fn new(context: Rc<Context>) -> Terminal {
#[cfg(target_os = "windows")]
let terminal = functions::get_module::<Box<ITerminal>>(WinApiTerminal::new(), AnsiTerminal::new(), context);
let terminal = functions::get_module::<Box<ITerminal>>(WinApiTerminal::new(context.clone()), AnsiTerminal::new(context.clone()));
#[cfg(not(target_os = "windows"))]
let terminal = Some(AnsiTerminal::new() as Box<ITerminal>);
let terminal = Some(AnsiTerminal::new(context.clone()) as Box<ITerminal>);
Terminal { terminal, context: context }
@ -54,7 +55,7 @@ impl<'context> Terminal<'context> {
/// ```
pub fn clear(&mut self, clear_type: ClearType) {
if let Some(ref terminal) = self.terminal {
terminal.clear(clear_type, &self.context);
terminal.clear(clear_type);
}
}
@ -77,7 +78,7 @@ impl<'context> Terminal<'context> {
/// ```
pub fn terminal_size(&mut self) -> (u16, u16) {
if let Some(ref terminal) = self.terminal {
return terminal.terminal_size(&self.context)
return terminal.terminal_size()
}
(0,0)
}
@ -101,7 +102,7 @@ impl<'context> Terminal<'context> {
/// ```
pub fn scroll_up(&mut self, count: i16) {
if let Some(ref terminal) = self.terminal {
terminal.scroll_up(count,&self.context);
terminal.scroll_up(count);
}
}
@ -124,7 +125,7 @@ impl<'context> Terminal<'context> {
/// ```
pub fn scroll_down(&mut self, count: i16) {
if let Some(ref terminal) = self.terminal {
terminal.scroll_down(count, &self.context);
terminal.scroll_down(count);
}
}
@ -148,7 +149,7 @@ impl<'context> Terminal<'context> {
pub fn set_size(&mut self, width: i16, height: i16)
{
if let Some (ref terminal) = self.terminal {
terminal.set_size(width,height,&self.context);
terminal.set_size(width,height);
}
}
@ -183,7 +184,7 @@ impl<'context> Terminal<'context> {
where
D: fmt::Display,
{
style::ObjectStyle::new().apply_to(val, &self.context)
style::ObjectStyle::new().apply_to(val, self.context.clone())
}
}
@ -208,6 +209,6 @@ impl<'context> Terminal<'context> {
///
/// ```
///
pub fn terminal<'context>(context: &'context Context) -> Box<Terminal<'context>> {
Box::from(Terminal::new(&context))
pub fn terminal(context: Rc<Context>) -> Box<Terminal> {
Box::from(Terminal::new(context.clone()))
}

View File

@ -3,44 +3,47 @@
use Context;
use cursor::cursor;
use super::{ClearType, ITerminal};
use super::{ClearType, ITerminal, Rc};
use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,};
use kernel::windows_kernel::{kernel, terminal};
/// This struct is an windows implementation for terminal related actions.
pub struct WinApiTerminal;
pub struct WinApiTerminal
{
context: Rc<Context>
}
impl WinApiTerminal {
pub fn new() -> Box<WinApiTerminal> {
Box::from(WinApiTerminal {})
pub fn new(context: Rc<Context>) -> Box<WinApiTerminal> {
Box::from(WinApiTerminal { context })
}
}
impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType, context: &Context) {
fn clear(&self, clear_type: ClearType) {
let csbi = kernel::get_console_screen_buffer_info();
let pos = cursor(&context).pos();
let pos = cursor(self.context.clone()).pos();
match clear_type
{
ClearType::All => clear_entire_screen(csbi, &context),
ClearType::FromCursorDown => clear_after_cursor(pos,csbi,&context),
ClearType::FromCursorUp => clear_before_cursor(pos, csbi, &context),
ClearType::CurrentLine => clear_current_line(pos, csbi, &context),
ClearType::UntilNewLine => clear_until_line(pos, csbi,&context),
ClearType::All => clear_entire_screen(csbi, self.context.clone()),
ClearType::FromCursorDown => clear_after_cursor(pos,csbi, self.context.clone()),
ClearType::FromCursorUp => clear_before_cursor(pos, csbi, self.context.clone()),
ClearType::CurrentLine => clear_current_line(pos, csbi, self.context.clone()),
ClearType::UntilNewLine => clear_until_line(pos, csbi,self.context.clone()),
};
}
fn terminal_size(&self, context: &Context) -> (u16, u16) {
fn terminal_size(&self) -> (u16, u16) {
terminal::terminal_size()
}
fn scroll_up(&self, count: i16, context: &Context) {
fn scroll_up(&self, count: i16) {
// yet to be inplemented
}
fn scroll_down(&self, count: i16, context: &Context) {
fn scroll_down(&self, count: i16) {
let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window;
@ -60,7 +63,7 @@ impl ITerminal for WinApiTerminal {
}
/// Set the current terminal size
fn set_size(&self, width: i16, height: i16, context: &Context) {
fn set_size(&self, width: i16, height: i16) {
if width <= 0
{
panic!("Cannot set the terminal width lower than 1");
@ -136,7 +139,7 @@ impl ITerminal for WinApiTerminal {
}
}
pub fn clear_after_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context) {
pub fn clear_after_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: Rc<Context>) {
let (mut x,mut y) = pos;
// if cursor position is at the outer right position
@ -154,7 +157,7 @@ pub fn clear_after_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, cont
clear(start_location,cells_to_write);
}
pub fn clear_before_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context) {
pub fn clear_before_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: Rc<Context>) {
let (xpos,ypos) = pos;
// one cell after cursor position
@ -170,7 +173,7 @@ pub fn clear_before_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, con
clear(start_location, cells_to_write);
}
pub fn clear_entire_screen(csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context) {
pub fn clear_entire_screen(csbi: CONSOLE_SCREEN_BUFFER_INFO, context: Rc<Context>) {
// position x at start
let x = 0;
// position y at start
@ -185,10 +188,10 @@ pub fn clear_entire_screen(csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context)
clear( start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor(&context).goto(0, 0);
cursor(context.clone()).goto(0, 0);
}
pub fn clear_current_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context)
pub fn clear_current_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: Rc<Context>)
{
// position x at start
let x = 0;
@ -204,10 +207,10 @@ pub fn clear_current_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, cont
clear(start_location, cells_to_write);
// put the cursor back at 1 cell on current row
cursor(&context).goto(0, y);
cursor(context.clone()).goto(0, y);
}
pub fn clear_until_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: &Context)
pub fn clear_until_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, context: Rc<Context>)
{
let (x,y) = pos;
@ -219,7 +222,7 @@ pub fn clear_until_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO, contex
clear(start_location, cells_to_write);
// put the cursor back at original cursor position
cursor(&context).goto(x,y);
cursor(context.clone()).goto(x,y);
}
fn clear(