Use default ANSI for windows, if current windows does not support ANSI switch back to WINAPI. Unix is not tested yet. Notice that currently the console will be set to another mode and that ther is no way back, when ansi is enabled. Storing the old state of the terminal and enable the client to switsh back to the old state will likely be inplemented in crossterm 0.3.0

This commit is contained in:
T 2018-02-03 17:17:28 +01:00
parent 4212e728d5
commit 215d0cfa83
21 changed files with 652 additions and 488 deletions

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ pub mod cursor;
pub mod terminal; pub mod terminal;
fn main() { fn main() {
terminal::clear_all_lines(); color::paint_background();
cursor::print(); color::paint_foreground();
color::paint_foreground_and_background();
} }

View File

@ -80,7 +80,7 @@ pub fn clear_until_new_line()
print_test_data(); print_test_data();
// Set terminal cursor position (see example for more info). // Set terminal cursor position (see example for more info).
crossterm_cursor::cursor().goto(4,7); crossterm_cursor::cursor().goto(4,20);
// Clear all the cells until next line. // Clear all the cells until next line.
terminal.clear(ClearType::UntilNewLine); terminal.clear(ClearType::UntilNewLine);
@ -92,7 +92,7 @@ pub fn print_terminal_size()
// Get terminal // Get terminal
let mut terminal = terminal(); let mut terminal = terminal();
// Get terminal size // Get terminal size
let terminal_size = terminal.terminal_size().unwrap(); let terminal_size = terminal.terminal_size();
// Print results // Print results
print!("X: {}, y: {}", terminal_size.0, terminal_size.1); print!("X: {}, y: {}", terminal_size.0, terminal_size.1);
} }

View File

@ -6,9 +6,7 @@ use std::fmt::Display;
use Construct; use Construct;
use super::base_cursor::ITerminalCursor; use super::base_cursor::ITerminalCursor;
#[cfg(unix)]
use super::AnsiCursor; use super::AnsiCursor;
#[cfg(windows)]
use super::WinApiCursor; use super::WinApiCursor;
/// Struct that stores an specific platform implementation for cursor related actions. /// Struct that stores an specific platform implementation for cursor related actions.
@ -19,12 +17,25 @@ pub struct TerminalCursor {
impl TerminalCursor { impl TerminalCursor {
/// Create new cursor instance whereon cursor related actions can be performed. /// Create new cursor instance whereon cursor related actions can be performed.
pub fn new() -> TerminalCursor { pub fn new() -> TerminalCursor {
let cursor: Option<Box<ITerminalCursor>> = { let mut cursor: Option<Box<ITerminalCursor>> = None;
#[cfg(unix)]
Some(AnsiCursor::new()); let mut does_support = true;
if cfg!(target_os = "windows") {
#[cfg(windows)] #[cfg(windows)]
Some(WinApiCursor::new()) use kernel::windows_kernel::kernel::try_enable_ansi_support;
};
does_support = try_enable_ansi_support();
// this returns an bool if the current windows console supports ansi.
if !does_support
{
cursor = Some(WinApiCursor::new());
}
}
if does_support
{
cursor = Some(AnsiCursor::new());
}
TerminalCursor { terminal_cursor: cursor } TerminalCursor { terminal_cursor: cursor }
} }

View File

@ -1,14 +1,10 @@
mod base_cursor; mod base_cursor;
mod cursor; mod cursor;
#[cfg(unix)]
mod ansi_cursor; mod ansi_cursor;
#[cfg(windows)]
mod winapi_cursor; mod winapi_cursor;
#[cfg(unix)]
use self::ansi_cursor::AnsiCursor; use self::ansi_cursor::AnsiCursor;
#[cfg(windows)]
use self::winapi_cursor::WinApiCursor; use self::winapi_cursor::WinApiCursor;
pub use self::cursor::{ cursor, TerminalCursor }; pub use self::cursor::{ cursor, TerminalCursor };

View File

@ -9,15 +9,15 @@ use super::base_color::ITerminalColor;
/// This struct is an ansi implementation for color related actions. /// This struct is an ansi implementation for color related actions.
#[derive(Debug)] #[derive(Debug)]
pub struct ANSIColor; pub struct AnsiColor;
impl Construct for ANSIColor { impl Construct for AnsiColor {
fn new() -> Box<ANSIColor> { fn new() -> Box<AnsiColor> {
Box::from(ANSIColor {}) Box::from(AnsiColor {})
} }
} }
impl ITerminalColor for ANSIColor { impl ITerminalColor for AnsiColor {
fn set_fg(&self, fg_color: Color) { fn set_fg(&self, fg_color: Color) {
let mut some_writer = io::stdout(); let mut some_writer = io::stdout();
write!(&mut some_writer, csi!("{}m"), self.color_value(fg_color, ColorType::Foreground)); write!(&mut some_writer, csi!("{}m"), self.color_value(fg_color, ColorType::Foreground));
@ -47,7 +47,7 @@ impl ITerminalColor for ANSIColor {
}, },
} }
let rgb_val; let rgb_val: String;
let color_val = match color { let color_val = match color {
Color::Black => "5;0", Color::Black => "5;0",
@ -65,7 +65,9 @@ impl ITerminalColor for ANSIColor {
Color::DarkCyan => "5;6", Color::DarkCyan => "5;6",
Color::Grey => "5;15", Color::Grey => "5;15",
Color::White => "5;7", Color::White => "5;7",
#[cfg(unix)]
Color::Rgb{r,g,b} => { rgb_val = format!("2;{};{};{}", r,g,b); rgb_val.as_str()}, Color::Rgb{r,g,b} => { rgb_val = format!("2;{};{};{}", r,g,b); rgb_val.as_str()},
#[cfg(unix)]
Color::AnsiValue(val) => { rgb_val = format!("5;{}",val); rgb_val.as_str() } Color::AnsiValue(val) => { rgb_val = format!("5;{}",val); rgb_val.as_str() }
}; };

View File

@ -9,9 +9,7 @@ use crossterm_style::{ObjectStyle, StyledObject};
use super::base_color::ITerminalColor; use super::base_color::ITerminalColor;
use super::super::Color; use super::super::Color;
#[cfg(unix)] use super::AnsiColor;
use super::ANSIColor;
#[cfg(windows)]
use super::WinApiColor; use super::WinApiColor;
/// Struct that stores an specific platform implementation for color related actions. /// Struct that stores an specific platform implementation for color related actions.
@ -22,12 +20,25 @@ pub struct TerminalColor {
impl TerminalColor { impl TerminalColor {
/// Create new instance whereon color related actions can be performed. /// Create new instance whereon color related actions can be performed.
pub fn new() -> TerminalColor { pub fn new() -> TerminalColor {
let color: Option<Box<ITerminalColor>> = { let mut color: Option<Box<ITerminalColor>> = None;
#[cfg(unix)]
Some(ANSIColor::new()); let mut does_support = true;
if cfg!(target_os = "windows") {
#[cfg(windows)] #[cfg(windows)]
Some(WinApiColor::new()) use kernel::windows_kernel::kernel::try_enable_ansi_support;
};
does_support = try_enable_ansi_support();
// this returns an bool if the current windows console supports ansi.
if !does_support
{
color = Some(WinApiColor::new());
}
}
if does_support
{
color = Some(AnsiColor::new());
}
TerminalColor { terminal_color: color } TerminalColor { terminal_color: color }
} }

View File

@ -1,12 +1,8 @@
pub mod base_color; pub mod base_color;
pub mod color; pub mod color;
#[cfg(unix)]
mod ansi_color; mod ansi_color;
#[cfg(windows)]
mod winapi_color; mod winapi_color;
#[cfg(unix)] use self::ansi_color::AnsiColor;
use self::ansi_color::ANSIColor;
#[cfg(windows)]
use self::winapi_color::WinApiColor; use self::winapi_color::WinApiColor;

View File

@ -3,21 +3,20 @@ use std::io::Write;
use Construct; use Construct;
use super::base_terminal::{ClearType, ITerminal}; use super::base_terminal::{ClearType, ITerminal};
use kernel::linux_kernel::terminal::*; use shared::functions::resize_terminal;
/// This struct is an ansi implementation for terminal related actions. /// This struct is an ansi implementation for terminal related actions.
pub struct UnixTerminal; pub struct AnsiTerminal ;
impl Construct for UnixTerminal { impl Construct for AnsiTerminal {
fn new() -> Box<UnixTerminal> { fn new() -> Box<AnsiTerminal> {
Box::from(UnixTerminal {}) Box::from(AnsiTerminal {})
} }
} }
impl ITerminal for UnixTerminal { impl ITerminal for AnsiTerminal {
fn clear(&self, clear_type: ClearType) { fn clear(&self, clear_type: ClearType) {
let mut some_writer = io::stdout(); let mut some_writer = io::stdout();
match clear_type { match clear_type {
ClearType::All => { ClearType::All => {
write!(&mut some_writer, csi!("2J")); write!(&mut some_writer, csi!("2J"));
@ -37,8 +36,8 @@ impl ITerminal for UnixTerminal {
}; };
} }
fn terminal_size(&self) -> Option<(u16, u16)> { fn terminal_size(&self) -> (u16, u16) {
terminal_size() resize_terminal()
} }
fn scroll_up(&self, count: i16) { fn scroll_up(&self, count: i16) {

View File

@ -11,7 +11,7 @@ pub trait ITerminal {
/// Clear the current cursor by specifying the clear type /// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType); fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y) /// Get the terminal size (x,y)
fn terminal_size(&self) -> Option<(u16, u16)>; fn terminal_size(&self) -> (u16, u16);
/// Scroll `n` lines up in the current terminal. /// Scroll `n` lines up in the current terminal.
fn scroll_up(&self, count: i16); fn scroll_up(&self, count: i16);
/// Scroll `n` lines down in the current terminal. /// Scroll `n` lines down in the current terminal.

View File

@ -1,14 +1,10 @@
mod base_terminal; mod base_terminal;
mod terminal; mod terminal;
#[cfg(unix)]
mod ansi_terminal; mod ansi_terminal;
#[cfg(windows)]
mod winapi_terminal; mod winapi_terminal;
#[cfg(unix)] use self::ansi_terminal::AnsiTerminal;
use self::ansi_terminal::UnixTerminal;
#[cfg(windows)]
use self::winapi_terminal::WinApiTerminal; use self::winapi_terminal::WinApiTerminal;
pub use self::base_terminal::ClearType; pub use self::base_terminal::ClearType;

View File

@ -1,12 +1,11 @@
//! With this module you can perform actions that are terminal related. //! With this module you can perform actions that are terminal related.
//! Like clearing and scrolling in the terminal or getting the size of the terminal. //! Like clearing and scrolling in the terminal or getting the size of the terminal.
use Construct; use Construct;
use super::base_terminal::{ClearType, ITerminal}; use super::base_terminal::{ClearType, ITerminal};
#[cfg(unix)] use super::AnsiTerminal;
use super::UnixTerminal;
#[cfg(windows)]
use super::WinApiTerminal; use super::WinApiTerminal;
/// Struct that stores an specific platform implementation for terminal related actions. /// Struct that stores an specific platform implementation for terminal related actions.
@ -17,13 +16,25 @@ pub struct Terminal {
impl Terminal { impl Terminal {
/// Create new terminal instance whereon terminal related actions can be performed. /// Create new terminal instance whereon terminal related actions can be performed.
pub fn new() -> Terminal { pub fn new() -> Terminal {
let term: Option<Box<ITerminal>> = let mut term: Option<Box<ITerminal>> = None;
let mut does_support = true;
if cfg!(target_os = "windows") {
use kernel::windows_kernel::kernel::try_enable_ansi_support;
does_support = try_enable_ansi_support();
// this returns an bool if the current windows console supports ansi.
if !does_support
{ {
#[cfg(unix)] term = Some(WinApiTerminal::new());
Some(UnixTerminal::new()); }
#[cfg(windows)] }
Some(WinApiTerminal::new())
}; if does_support
{
println!("This console does support ansi");
term = Some(AnsiTerminal::new());
}
Terminal { terminal: term } Terminal { terminal: term }
} }
@ -72,13 +83,11 @@ impl Terminal {
/// println!("{:?}", size); /// println!("{:?}", size);
/// ///
/// ``` /// ```
pub fn terminal_size(&mut self) -> Option<(u16, u16)> { pub fn terminal_size(&mut self) -> (u16, u16) {
if let Some(ref terminal) = self.terminal { if let Some(ref terminal) = self.terminal {
let a = terminal.terminal_size(); return terminal.terminal_size()
a
} else {
None
} }
(0,0)
} }
/// Scroll `n` lines up in the current terminal. /// Scroll `n` lines up in the current terminal.

View File

@ -14,6 +14,7 @@ impl Construct for WinApiTerminal {
impl ITerminal for WinApiTerminal { impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType) { fn clear(&self, clear_type: ClearType) {
println! ("Windows!!!");
match clear_type match clear_type
{ {
ClearType::All => terminal::clear_entire_screen(), ClearType::All => terminal::clear_entire_screen(),
@ -24,7 +25,7 @@ impl ITerminal for WinApiTerminal {
}; };
} }
fn terminal_size(&self) -> Option<(u16, u16)> { fn terminal_size(&self) -> (u16, u16) {
terminal::terminal_size() terminal::terminal_size()
} }

View File

@ -16,7 +16,7 @@ pub struct UnixSize {
} }
/// Gets the current terminal size /// Gets the current terminal size
pub fn terminal_size() -> Option<(u16,u16)> { pub fn terminal_size() -> (u16,u16) {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc // http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize { let us = UnixSize {
rows: 0, rows: 0,
@ -29,6 +29,6 @@ pub fn terminal_size() -> Option<(u16,u16)> {
// because crossterm works starts counting at 0 and unix terminal starts at cell 1 you have subtract one to get 0-based results. // because crossterm works starts counting at 0 and unix terminal starts at cell 1 you have subtract one to get 0-based results.
Some((us.cols -1, us.rows -1)) Some((us.cols -1, us.rows -1))
} else { } else {
None (0,0)
} }
} }

View File

@ -1,9 +0,0 @@
///! Notice that this feature is not used. But will be implemented later.
use super::kernel;
/// Enables ansi for windows terminals.
pub fn enable_ansi_support() {
let enable_ansi_code: u32 = 7;
kernel::set_console_mode(enable_ansi_code);
}

View File

@ -7,6 +7,7 @@ static mut SAVED_CURSOR_POS:(i16,i16) = (0,0);
pub fn set(x: i16, y: i16) pub fn set(x: i16, y: i16)
{ {
kernel::set_console_cursor_position(x, y ); kernel::set_console_cursor_position(x, y );
} }
/// Reset to saved cursor position /// Reset to saved cursor position

View File

@ -1,17 +1,19 @@
use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::HANDLE; use winapi::um::winnt::HANDLE;
use winapi::um::winbase::STD_OUTPUT_HANDLE; use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE };
use winapi::um::handleapi::INVALID_HANDLE_VALUE; use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::{GetStdHandle}; use winapi::um::processenv::{GetStdHandle};
use winapi::um::consoleapi::{SetConsoleMode}; use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode};
use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo, GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
FillConsoleOutputCharacterA, FillConsoleOutputAttribute, FillConsoleOutputCharacterA, FillConsoleOutputAttribute, ENABLE_VIRTUAL_TERMINAL_PROCESSING,ENABLE_VIRTUAL_TERMINAL_INPUT,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, DISABLE_NEWLINE_AUTO_RETURN
}; };
use super::{Empty}; use super::{Empty};
static mut CONSOLE_OUTPUT_HANDLE: Option<HANDLE> = None; static mut CONSOLE_OUTPUT_HANDLE: Option<HANDLE> = None;
static mut CONSOLE_INPUT_HANDLE: Option<HANDLE> = None;
/// Get the std_output_handle of the console /// Get the std_output_handle of the console
pub fn get_output_handle() -> HANDLE { pub fn get_output_handle() -> HANDLE {
@ -20,18 +22,43 @@ pub fn get_output_handle() -> HANDLE {
handle handle
} else { } else {
let handle = GetStdHandle(STD_OUTPUT_HANDLE); let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !is_valid_handle(&handle)
{
panic!("Cannot get output handle")
}
CONSOLE_OUTPUT_HANDLE = Some(handle); CONSOLE_OUTPUT_HANDLE = Some(handle);
handle handle
} }
} }
} }
/// Get the std_input_handle of the console
pub fn get_input_handle() -> HANDLE {
unsafe {
if let Some(handle) = CONSOLE_INPUT_HANDLE {
handle
} else {
let handle = GetStdHandle(STD_INPUT_HANDLE);
if !is_valid_handle(&handle)
{
panic!("Cannot get input handle")
}
CONSOLE_INPUT_HANDLE = Some(handle);
handle
}
}
}
/// Checks if the console handle is an invalid handle value. /// Checks if the console handle is an invalid handle value.
pub fn is_valid_handle(handle: &HANDLE) -> bool { pub fn is_valid_handle(handle: &HANDLE) -> bool {
if *handle == INVALID_HANDLE_VALUE { if *handle == INVALID_HANDLE_VALUE {
true
} else {
false false
} else {
true
} }
} }
@ -50,6 +77,26 @@ pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
csbi csbi
} }
/// Enables ansi for windows terminals.
pub fn try_enable_ansi_support() -> bool {
let output_handle = get_output_handle();
let mut dw_mode: DWORD = 0;
if !get_console_mode(&output_handle, &mut dw_mode)
{
return false;
}
dw_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if !set_console_mode(&output_handle, dw_mode)
{
return false;
}
return true;
}
pub fn get_largest_console_window_size() -> COORD pub fn get_largest_console_window_size() -> COORD
{ {
let output_handle = get_output_handle(); let output_handle = get_output_handle();
@ -64,12 +111,19 @@ pub fn get_original_console_color() -> u16 {
console_buffer_info.wAttributes as u16 console_buffer_info.wAttributes as u16
} }
pub fn set_console_mode(console_mode: u32) pub fn set_console_mode(handle: &HANDLE, console_mode: u32) -> bool
{ {
let output_handle = get_output_handle();
unsafe { unsafe {
SetConsoleMode(output_handle, console_mode); let success = SetConsoleMode(*handle, console_mode);
return is_true(success);
}
}
pub fn get_console_mode(handle: &HANDLE, current_mode: &mut u32) -> bool
{
unsafe {
let success = GetConsoleMode(*handle, &mut *current_mode);
return is_true(success);
} }
} }
@ -170,9 +224,9 @@ pub fn fill_console_output_attribute(cells_written: &mut u32, start_location: CO
fn is_true(value: i32) -> bool fn is_true(value: i32) -> bool
{ {
if value == 0{ if value == 0{
false return false;
} }
else{ else{
true return true;
} }
} }

View File

@ -1,6 +1,5 @@
pub mod cursor; pub mod cursor;
pub mod color; pub mod color;
pub mod ansi_support;
pub mod kernel; pub mod kernel;
pub mod terminal; pub mod terminal;
mod winapi_extentions; mod winapi_extentions;

View File

@ -2,13 +2,13 @@ use super::{cursor, kernel};
use winapi::um::wincon::{SMALL_RECT, COORD}; use winapi::um::wincon::{SMALL_RECT, COORD};
/// Get the terminal size (y,x) /// Get the terminal size (y,x)
pub fn terminal_size() -> Option<(u16, u16)> { pub fn terminal_size() -> (u16, u16) {
let csbi = kernel::get_console_screen_buffer_info(); let csbi = kernel::get_console_screen_buffer_info();
Some(( (
(csbi.srWindow.Right - csbi.srWindow.Left) as u16, (csbi.srWindow.Right - csbi.srWindow.Left) as u16,
(csbi.srWindow.Bottom - csbi.srWindow.Top) as u16, (csbi.srWindow.Bottom - csbi.srWindow.Top) as u16,
)) )
} }
/// Scroll down `n` rows /// Scroll down `n` rows

9
src/shared/functions.rs Normal file
View File

@ -0,0 +1,9 @@
#[cfg(unix)]
use kernel::linux_kernel::terminal::terminal_size;
#[cfg(windows)]
use kernel::windows_kernel::terminal::terminal_size;
pub fn resize_terminal() -> (u16,u16)
{
terminal_size()
}

View File

@ -1,3 +1,4 @@
#[macro_use] #[macro_use]
pub mod macros; pub mod macros;
pub mod traits; pub mod traits;
pub mod functions;