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;
fn main() {
terminal::clear_all_lines();
cursor::print();
color::paint_background();
color::paint_foreground();
color::paint_foreground_and_background();
}

View File

@ -80,7 +80,7 @@ pub fn clear_until_new_line()
print_test_data();
// 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.
terminal.clear(ClearType::UntilNewLine);
@ -92,7 +92,7 @@ pub fn print_terminal_size()
// Get terminal
let mut terminal = terminal();
// Get terminal size
let terminal_size = terminal.terminal_size().unwrap();
let terminal_size = terminal.terminal_size();
// Print results
print!("X: {}, y: {}", terminal_size.0, terminal_size.1);
}

View File

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

View File

@ -1,14 +1,10 @@
mod base_cursor;
mod cursor;
#[cfg(unix)]
mod ansi_cursor;
#[cfg(windows)]
mod winapi_cursor;
#[cfg(unix)]
use self::ansi_cursor::AnsiCursor;
#[cfg(windows)]
use self::winapi_cursor::WinApiCursor;
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.
#[derive(Debug)]
pub struct ANSIColor;
pub struct AnsiColor;
impl Construct for ANSIColor {
fn new() -> Box<ANSIColor> {
Box::from(ANSIColor {})
impl Construct for AnsiColor {
fn new() -> Box<AnsiColor> {
Box::from(AnsiColor {})
}
}
impl ITerminalColor for ANSIColor {
impl ITerminalColor for AnsiColor {
fn set_fg(&self, fg_color: Color) {
let mut some_writer = io::stdout();
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 {
Color::Black => "5;0",
@ -65,7 +65,9 @@ impl ITerminalColor for ANSIColor {
Color::DarkCyan => "5;6",
Color::Grey => "5;15",
Color::White => "5;7",
#[cfg(unix)]
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() }
};

View File

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

View File

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

View File

@ -3,21 +3,20 @@ use std::io::Write;
use Construct;
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.
pub struct UnixTerminal;
pub struct AnsiTerminal ;
impl Construct for UnixTerminal {
fn new() -> Box<UnixTerminal> {
Box::from(UnixTerminal {})
impl Construct for AnsiTerminal {
fn new() -> Box<AnsiTerminal> {
Box::from(AnsiTerminal {})
}
}
impl ITerminal for UnixTerminal {
impl ITerminal for AnsiTerminal {
fn clear(&self, clear_type: ClearType) {
let mut some_writer = io::stdout();
match clear_type {
ClearType::All => {
write!(&mut some_writer, csi!("2J"));
@ -37,8 +36,8 @@ impl ITerminal for UnixTerminal {
};
}
fn terminal_size(&self) -> Option<(u16, u16)> {
terminal_size()
fn terminal_size(&self) -> (u16, u16) {
resize_terminal()
}
fn scroll_up(&self, count: i16) {

View File

@ -11,7 +11,7 @@ pub trait ITerminal {
/// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType);
/// 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.
fn scroll_up(&self, count: i16);
/// Scroll `n` lines down in the current terminal.

View File

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

View File

@ -1,12 +1,11 @@
//! 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.
use Construct;
use super::base_terminal::{ClearType, ITerminal};
#[cfg(unix)]
use super::UnixTerminal;
#[cfg(windows)]
use super::AnsiTerminal;
use super::WinApiTerminal;
/// Struct that stores an specific platform implementation for terminal related actions.
@ -17,13 +16,25 @@ pub struct Terminal {
impl Terminal {
/// Create new terminal instance whereon terminal related actions can be performed.
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
{
term = Some(WinApiTerminal::new());
}
}
if does_support
{
#[cfg(unix)]
Some(UnixTerminal::new());
#[cfg(windows)]
Some(WinApiTerminal::new())
};
println!("This console does support ansi");
term = Some(AnsiTerminal::new());
}
Terminal { terminal: term }
}
@ -72,13 +83,11 @@ impl Terminal {
/// 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 {
let a = terminal.terminal_size();
a
} else {
None
return terminal.terminal_size()
}
(0,0)
}
/// Scroll `n` lines up in the current terminal.

View File

@ -14,6 +14,7 @@ impl Construct for WinApiTerminal {
impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType) {
println! ("Windows!!!");
match clear_type
{
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()
}

View File

@ -16,7 +16,7 @@ pub struct UnixSize {
}
/// 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
let us = UnixSize {
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.
Some((us.cols -1, us.rows -1))
} 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)
{
kernel::set_console_cursor_position(x, y );
}
/// Reset to saved cursor position

View File

@ -1,17 +1,19 @@
use winapi::shared::minwindef::DWORD;
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::processenv::{GetStdHandle};
use winapi::um::consoleapi::{SetConsoleMode};
use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode};
use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
FillConsoleOutputCharacterA, FillConsoleOutputAttribute,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD
FillConsoleOutputCharacterA, FillConsoleOutputAttribute, ENABLE_VIRTUAL_TERMINAL_PROCESSING,ENABLE_VIRTUAL_TERMINAL_INPUT,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, DISABLE_NEWLINE_AUTO_RETURN
};
use super::{Empty};
static mut CONSOLE_OUTPUT_HANDLE: Option<HANDLE> = None;
static mut CONSOLE_INPUT_HANDLE: Option<HANDLE> = None;
/// Get the std_output_handle of the console
pub fn get_output_handle() -> HANDLE {
@ -20,18 +22,43 @@ pub fn get_output_handle() -> HANDLE {
handle
} else {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !is_valid_handle(&handle)
{
panic!("Cannot get output handle")
}
CONSOLE_OUTPUT_HANDLE = Some(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.
pub fn is_valid_handle(handle: &HANDLE) -> bool {
if *handle == INVALID_HANDLE_VALUE {
true
} else {
false
} else {
true
}
}
@ -50,6 +77,26 @@ pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
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
{
let output_handle = get_output_handle();
@ -64,12 +111,19 @@ pub fn get_original_console_color() -> 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 {
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
{
if value == 0{
false
return false;
}
else{
true
return true;
}
}

View File

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

View File

@ -2,13 +2,13 @@ use super::{cursor, kernel};
use winapi::um::wincon::{SMALL_RECT, COORD};
/// 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();
Some((
(
(csbi.srWindow.Right - csbi.srWindow.Left) as u16,
(csbi.srWindow.Bottom - csbi.srWindow.Top) as u16,
))
)
}
/// 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]
pub mod macros;
pub mod traits;
pub mod functions;