This commit is contained in:
T 2018-03-03 15:40:51 +01:00
parent 215d0cfa83
commit 524955f8c0
42 changed files with 2022 additions and 1002 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

File diff suppressed because it is too large Load Diff

View File

@ -10,14 +10,21 @@ keywords = ["console", "color", "cursor", "terminal", "cli"]
exclude = ["target", "Cargo.lock"] exclude = ["target", "Cargo.lock"]
[dependencies] [dependencies]
rand = "0.4.2"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] } winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "0.2" libc = "0.2"
termios = "0.3.0"
[[bin]] [[bin]]
name = "example_bin" name = "a"
path = "./examples/bin.rs" path = "examples//bin.rs"

View File

@ -23,9 +23,35 @@ use self::crossterm::crossterm_terminal;
pub mod color; pub mod color;
pub mod cursor; pub mod cursor;
pub mod terminal; pub mod terminal;
use std::io::{Error, ErrorKind, Write};
use std::io;
use std::{time, thread};
use self::crossterm_terminal::screen::AlternateScreen;
use crossterm::crossterm_terminal::IntoRawMode;
use crossterm::Context;
fn main() { fn main() {
color::paint_background(); let mut context = Context::new();
color::paint_foreground(); //
color::paint_foreground_and_background(); let mut screen = io::stdout().into_raw_mode(&mut context).unwrap();
{
// let mut screen = io::stdout();
crossterm_cursor::cursor().goto(10, 10);
let mut curs = crossterm::crossterm_cursor::cursor();
curs.move_up(1);
// print!("1");
write!(screen, "{}", "1");
curs.move_right(1);
// print!("2");
write!(screen, "{}", "2");
curs.move_down(1);
// print!("3");
write!(screen, "{}", "3");
curs.move_left(1);
// write!()!("4");
write!(screen, "{}", "4");
}
} }

6
src/Terminal.rs Normal file
View File

@ -0,0 +1,6 @@
pub struct Crossterm;
impl Crossterm
{
fn
}

View File

@ -3,6 +3,7 @@ use std::io::Write;
use Construct; use Construct;
use super::base_cursor::ITerminalCursor; use super::base_cursor::ITerminalCursor;
use shared::functions;
/// This struct is an ansi implementation for cursor related actions. /// This struct is an ansi implementation for cursor related actions.
pub struct AnsiCursor; pub struct AnsiCursor;
@ -20,8 +21,8 @@ impl ITerminalCursor for AnsiCursor {
write!(&mut some_writer, csi!("{};{}H"), y + 1, x +1); write!(&mut some_writer, csi!("{};{}H"), y + 1, x +1);
} }
fn pos(&self) -> (i16, i16) { fn pos(&self) -> (u16, u16) {
(0, 0) functions::get_cursor_position()
} }
fn move_up(&self, count: u16) { fn move_up(&self, count: u16) {

View File

@ -11,7 +11,7 @@ pub trait ITerminalCursor {
/// Goto some location (x,y) in the terminal. /// Goto some location (x,y) in the terminal.
fn goto(&self, x: u16, y: u16); fn goto(&self, x: u16, y: u16);
/// Get the location (x,y) of the current curor in the terminal /// Get the location (x,y) of the current curor in the terminal
fn pos(&self) -> (i16, i16); fn pos(&self) -> (u16, u16);
/// Move cursor n times up /// Move cursor n times up
fn move_up(&self, count: u16); fn move_up(&self, count: u16);
/// Move the cursor `n` times to the right. /// Move the cursor `n` times to the right.

View File

@ -1,43 +1,33 @@
//! With this module you can perform actions that are cursor related. //! With this module you can perform actions that are cursor related.
//! Like changing and displaying the position of the cursor in terminal. //! Like changing and displaying the position of the cursor in terminal.
//!
use std::fmt::Display; use std::fmt::Display;
use std::ops::Drop;
use Construct; use {Construct, Context};
use shared::functions::get_module;
use super::base_cursor::ITerminalCursor; use super::base_cursor::ITerminalCursor;
use super::AnsiCursor; use super::AnsiCursor;
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.
pub struct TerminalCursor { pub struct TerminalCursor {
terminal_cursor: Option<Box<ITerminalCursor>>, terminal_cursor: Option<Box<ITerminalCursor>>,
context: Context
} }
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 mut cursor: Option<Box<ITerminalCursor>> = None; let mut context = Context::new();
let mut does_support = true; #[cfg(target_os = "windows")]
if cfg!(target_os = "windows") { let cursor = get_module::<Box<ITerminalCursor>>(WinApiCursor::new(), AnsiCursor::new(), &mut context);
#[cfg(windows)]
use kernel::windows_kernel::kernel::try_enable_ansi_support;
does_support = try_enable_ansi_support(); #[cfg(not(target_os = "windows"))]
// this returns an bool if the current windows console supports ansi. let cursor = Some(AnsiCursor::new());
if !does_support
{
cursor = Some(WinApiCursor::new());
}
}
if does_support TerminalCursor { terminal_cursor: cursor, context: context }
{
cursor = Some(AnsiCursor::new());
}
TerminalCursor { terminal_cursor: cursor }
} }
/// Goto some position (x,y) in the terminal. /// Goto some position (x,y) in the terminal.
@ -74,7 +64,7 @@ impl TerminalCursor {
/// println!("{:?}", pos); /// println!("{:?}", pos);
/// ///
/// ``` /// ```
pub fn pos(&mut self) -> (i16, i16) { pub fn pos(&mut self) -> (u16, u16) {
if let Some(ref terminal_cursor) = self.terminal_cursor { if let Some(ref terminal_cursor) = self.terminal_cursor {
terminal_cursor.pos() terminal_cursor.pos()
} else { } else {

View File

@ -1,6 +1,7 @@
use Construct; use Construct;
use kernel::windows_kernel::cursor;
use super::base_cursor::ITerminalCursor; use super::base_cursor::ITerminalCursor;
use kernel::windows_kernel::{kernel, cursor};
/// This struct is an windows implementation for cursor related actions. /// This struct is an windows implementation for cursor related actions.
pub struct WinApiCursor; pub struct WinApiCursor;
@ -12,38 +13,40 @@ impl Construct for WinApiCursor {
} }
impl ITerminalCursor for WinApiCursor { impl ITerminalCursor for WinApiCursor {
/// Set the current cursor position to X and Y
fn goto(&self, x: u16, y: u16) { fn goto(&self, x: u16, y: u16) {
cursor::set(x as i16, y as i16); kernel::set_console_cursor_position(x as i16, y as i16);
} }
fn pos(&self) -> (i16, i16) { /// Get current cursor position (X,Y)
fn pos(&self) -> (u16, u16) {
cursor::pos() cursor::pos()
} }
fn move_up(&self, count: u16) { fn move_up(&self, count: u16) {
let (xpos,ypos) = cursor::pos(); let (xpos,ypos) = self.pos();
cursor::set(xpos, ypos - count as i16); self.goto(xpos, ypos - count);
} }
fn move_right(&self, count: u16) { fn move_right(&self, count: u16) {
let (xpos,ypos) = cursor::pos(); let (xpos,ypos) = self.pos();
cursor::set(xpos + count as i16, ypos); self.goto(xpos + count, ypos);
} }
fn move_down(&self, count: u16) { fn move_down(&self, count: u16) {
let (xpos,ypos) = cursor::pos(); let (xpos,ypos) = self.pos();
cursor::set(xpos, ypos + count as i16); self.goto(xpos, ypos + count);
} }
fn move_left(&self, count: u16) { fn move_left(&self, count: u16) {
let (xpos,ypos) = cursor::pos(); let (xpos,ypos) = self.pos();
cursor::set(xpos - count as i16, ypos); self.goto(xpos - count, ypos);
} }
fn save_position(&mut self) fn save_position(&mut self)
{ {
cursor::save_cursor_pos(); cursor::save_cursor_pos();

View File

@ -0,0 +1,45 @@
use rand;
#[cfg(unix)]
pub mod unix_command;
#[cfg(windows)]
pub mod win_commands;
pub mod shared_commands;
#[cfg(unix)]
pub use self::unix_command::*;
#[cfg(windows)]
pub use self::win_commands::*;
use super::Context;
pub enum CommandType
{
Unix,
Windows,
}
pub trait ICommand
{
fn new() -> Box<Self> where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
pub trait IContextCommand
{
fn new(context: &mut Context) -> (Box<Self>, i16) where Self: Sized;
fn execute(&mut self) -> bool;
fn undo(&mut self) -> bool;
}
fn generate_key() -> i16 {
// let mut rng = rand::thread_rng();
// if rng.gen() { // random bool
// println!("i32: {}, u32: {}", rng.gen::<i32>(), rng.gen::<u32>())
// }
rand::random::<i16>()
}

View File

@ -0,0 +1,36 @@
use super::ICommand;
use std::io;
use std::io::Write;
#[derive(Clone, Copy)]
pub struct ToAlternateScreenBufferCommand;
impl ICommand for ToAlternateScreenBufferCommand
{
fn new() -> Box<ToAlternateScreenBufferCommand> {
// println!("create new unix alternate screen");
Box::from(ToAlternateScreenBufferCommand { })
}
fn execute(&mut self) -> bool
{
// println!("execute alternate screen");
let mut some_writer = io::stdout();
match write!(some_writer, csi!("?1049h"))
{
Ok(_) => true,
Err(_) => false
}
}
fn undo(&mut self) -> bool
{
// println!("undo alternate screen");
let mut some_writer = io::stdout();
match write!(some_writer, csi!("?1049l"))
{
Ok(_) => true,
Err(_) => false
}
}
}

View File

@ -0,0 +1,95 @@
use crossterm_state::{Context};
use super::IContextCommand;
use kernel::unix_kernel::terminal::Termios;
use kernel::unix_kernel::terminal;
#[derive(Clone, Copy)]
pub struct NoncanonicaModeCommand
{
key: i16
}
impl IContextCommand for NoncanonicalModeCommand
{
fn new(context: &mut Context) -> (Box<NoncanonicalModeCommand>) {
// println!("new new NoncanonicalModeCommand unix");
let key = super::generate_key();
let command = NoncanonicaModeCommand{ key: key };
context.register_change(command,key);
(Box::from(NoncanonicalModeCommand {}), key)
}
fn execute(&mut self) -> bool
{
// println!("execute NoncanonicalModeCommand uxix");
// Set noncanonical mode
let orig = Termios::from_fd(FD_STDIN)?;
let mut noncan = orig.clone();
noncan.c_lflag &= !ICANON;
noncan.c_lflag &= !ECHO;
noncan.c_lflag &= !CREAD;
match tcsetattr(FD_STDIN, TCSAFLUSH, &noncan)
{
Ok(_) => return true,
Err(_) => return false,
};
}
fn undo(&mut self) -> bool
{
// println!("undo NoncanonicalModeCommand unix");
// Disable noncanonical mode
let orig = Termios::from_fd(FD_STDIN)?;
let mut noncan = orig.clone();
noncan.c_lflag &= ICANON;
noncan.c_lflag &= ECHO;
noncan.c_lflag &= CREAD;
match tcsetattr(FD_STDIN, TCSAFLUSH, &noncan)
{
Ok(_) => return true,
Err(_) => return false,
};
}
}
#[derive(Clone, Copy)]
pub struct EnableRawModeCommand
{
original_mode: Option<Termios>,
key: i16
}
impl IContextCommand for EnableRawModeCommand
{
fn new(context: &Context) -> (Box<EnableRawModeCommand>, i16) {
// println!("new EnableRawModeCommand unix");
let key = super::generate_key();
let command = EnableRawModeCommand { original_mode: None, key: key };
context.state.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
// println!("execute EnableRawModeCommand unix");
self.original_mode = terminal::get_terminal_mode()?;
let mut new_mode = self.original_mode.unwrap();
new_mode.make_raw();
terminal::set_terminal_mode(new_mode);
true
}
fn undo(&mut self) -> bool
{
// println!("undo EnableRawModeCommand unix");
let result = terminal::set_terminal_mode(self.original_mode).unwrap();
match result
{
Ok(_) => true,
Err(_) => false
}
}
}

View File

@ -0,0 +1,220 @@
use super::{ICommand, IContextCommand};
use super::super::Context;
use kernel::windows_kernel::{kernel, ansi_support};
use winapi::um::winnt::{HANDLE};
use winapi::shared::minwindef::DWORD;
use winapi::um::wincon;
use winapi::um::wincon::{ENABLE_VIRTUAL_TERMINAL_PROCESSING ,SMALL_RECT, COORD, CHAR_INFO};
use std::mem;
use std::fmt::Write;
/// check https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences for more info.
#[derive(Clone, Copy)]
pub struct EnableAnsiCommand
{
mask: DWORD,
key: i16
}
impl IContextCommand for EnableAnsiCommand
{
fn new(context: &mut Context) -> (Box<EnableAnsiCommand>, i16) {
// println!("new EnableRawModeCommand winapi");
let key = super::generate_key();
let command = EnableAnsiCommand { mask: ENABLE_VIRTUAL_TERMINAL_PROCESSING, key: key };
context.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
// println!("exucute EnableAnsiCommand winapi");
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded.
if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled()
{
return ansi_support::windows_supportable();
} else {
let output_handle = kernel::get_output_handle();
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;
}
return true;
}
}
fn undo(&mut self) -> bool
{
// println!("undo EnableAnsiCommand winapi");
if ansi_support::ansi_enabled()
{
let output_handle = kernel::get_output_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
return false;
}
dw_mode &= !self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode)
{
return false;
}
ansi_support::set_ansi_enabled(false);
}
return true;
}
}
#[derive(Clone, Copy)]
pub struct EnableRawModeCommand
{
mask: DWORD,
key: i16
}
impl IContextCommand for EnableRawModeCommand
{
fn new(context: &mut Context) -> (Box<EnableRawModeCommand>, i16) {
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_PROCESSED_OUTPUT, ENABLE_WRAP_AT_EOL_OUTPUT, ENABLE_ECHO_INPUT};
// println!("new EnableRawModeCommand winapi");
let key = super::generate_key();
let command = EnableRawModeCommand { mask: ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT, key: key };
context.register_change(Box::from(command), key);
(Box::from(command),key)
}
fn execute(&mut self) -> bool
{
use self::wincon::{ENABLE_LINE_INPUT,ENABLE_PROCESSED_INPUT, ENABLE_ECHO_INPUT};
// println!("execute EnableRawModeCommand winapi");
let input_handle = kernel::get_input_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&input_handle, &mut dw_mode)
{
return false;
}
let new_mode = dw_mode & !self.mask;
if !kernel::set_console_mode(&input_handle, new_mode)
{
return false;
}
true
}
fn undo(&mut self) -> bool
{
// println!("undo EnableRawModeCommand winapi");
let output_handle = kernel::get_output_handle();
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode)
{
return false;
}
let new_mode = dw_mode | self.mask;
if !kernel::set_console_mode(&output_handle, new_mode)
{
return false;
}
true
}
}
/// 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
{
fn new() -> Box<ToAlternateScreenBufferCommand>
{
Box::from(ToAlternateScreenBufferCommand {})
}
fn execute(&mut self) -> bool
{
// println!("executte ToAlternateScreenBufferCommand winapi");
let mut chi_buffer: [CHAR_INFO;160] = unsafe {mem::zeroed() };
let handle = kernel::get_output_handle();
// create a new screen buffer to copy to.
let new_handle = kernel::create_console_screen_buffer();
// Make the new screen buffer the active screen buffer.
kernel::set_active_screen_buffer(new_handle);
// Set the source rectangle.
let mut srct_read_rect = SMALL_RECT
{
Top: 0,
Left: 0 ,
Bottom: 1,
Right: 79,
};
// The temporary buffer size is 2 rows x 80 columns.
let coord_buffer_size = COORD
{
X: 2,
Y: 80
};
// The top left destination cell of the temporary buffer is
// row 0, col 0.
let coord_buffer_coord = COORD
{
X: 0,
Y: 0,
};
// Copy the block from the screen buffer to the temp. buffer.
kernel::read_console_output(&handle, &mut chi_buffer, coord_buffer_size, coord_buffer_coord, &mut srct_read_rect);
// Set the destination rectangle.
let mut srct_write_rect = SMALL_RECT
{
Top: 10,
Left: 0,
Bottom: 11,
Right: 19,
};
// Copy from the temporary buffer to the new screen buffer.
kernel::write_console_output(&new_handle, &mut chi_buffer, coord_buffer_size, coord_buffer_coord, &mut srct_write_rect);
true
}
fn undo(&mut self) -> bool
{
// println!("undo ToAlternateScreenBufferCommand winapi");
let handle = kernel::get_output_handle();
kernel::set_active_screen_buffer(handle);
true
}
}

View File

@ -0,0 +1,72 @@
use super::commands::{IContextCommand, ICommand};
use std::ops::Drop;
use std::collections::HashMap;
pub struct Context
{
changed_states: HashMap<i16, Box<IContextCommand>>,
}
impl Context
{
pub fn new() -> Context
{
Context { changed_states: HashMap::new() }
}
/// restore to default terminal state
pub fn restore_changes(&mut self)
{
// println!("restore change");
for (x, state) in self.changed_states.iter_mut()
{
state.undo();
// println!("State is removed, total state");
}
}
/// Register new changed state
pub fn register_change(&mut self, change: Box<IContextCommand>, key: i16)
{
// println!("register change");
if !self.changed_states.contains_key(&key)
{
self.changed_states.insert(key, change);
// println!("State is registerd, total states: {}", self.changed_states.len());
}
}
/// Undo state
pub fn undo_state(&mut self, state_key: i16)
{
// println!("undo specific");
if self.changed_states.contains_key(&state_key)
{
self.changed_states.remove(&state_key);
}
}
}
//
//fn state_wrapper() -> Context {
// // Initialize it to a null value
// static mut SINGLETON: *const StateWrapper = 0 as *const StateWrapper;
// static ONCE: Once = ONCE_INIT;
//
// unsafe {
// ONCE.call_once(|| {
// // Make it
// let singleton = StateWrapper {
// state: Arc::new(Mutex::new(State::new())),
// };
//
// // Put it in the heap so it can outlive this call
// SINGLETON = mem::transmute(Box::new(singleton));
// });
//
// // Now we give out a copy of the data that is safe to use concurrently.
// (*SINGLETON).clone()
// }
//}

View File

@ -0,0 +1,4 @@
mod context;
pub mod commands;
pub use self::context::{Context};

View File

@ -47,6 +47,7 @@ impl ITerminalColor for AnsiColor {
}, },
} }
#[cfg(unix)]
let rgb_val: String; let rgb_val: String;
let color_val = match color { let color_val = match color {

View File

@ -1,12 +1,14 @@
//! With this module you can perform actions that are color related. //! With this module you can perform actions that are color related.
//! Like styling the font, foreground color and background color. //! Like styling the font, foreground color and background color.
use std::ops::Drop;
use std::fmt; use std::fmt;
use std::io; use std::io;
use Construct; use {Construct, Context };
use crossterm_style::{ObjectStyle, StyledObject}; use crossterm_style::{ObjectStyle, StyledObject};
use super::base_color::ITerminalColor; use super::base_color::ITerminalColor;
use shared::functions::get_module;
use super::super::Color; use super::super::Color;
use super::AnsiColor; use super::AnsiColor;
@ -15,35 +17,24 @@ 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.
pub struct TerminalColor { pub struct TerminalColor {
terminal_color: Option<Box<ITerminalColor>>, terminal_color: Option<Box<ITerminalColor>>,
context: Context
} }
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 mut color: Option<Box<ITerminalColor>> = None; let mut context = Context::new();
let mut does_support = true; #[cfg(target_os = "windows")]
if cfg!(target_os = "windows") { let color = get_module::<Box<ITerminalColor>>(WinApiColor::new(), AnsiColor::new(), &mut context);
#[cfg(windows)]
use kernel::windows_kernel::kernel::try_enable_ansi_support;
does_support = try_enable_ansi_support(); #[cfg(not(target_os = "windows"))]
// this returns an bool if the current windows console supports ansi. let color = Some(AnsiColor::new());
if !does_support
{ TerminalColor { terminal_color: color, context: context}
color = Some(WinApiColor::new());
}
} }
if does_support /// Set the foreground color to the given color.
{
color = Some(AnsiColor::new());
}
TerminalColor { terminal_color: color }
}
/// Set the forground color to the given color.
/// ///
/// #Example /// #Example
/// ///

View File

@ -1,7 +1,9 @@
use Construct; use Construct;
use super::super::{ColorType, Color}; use super::super::{ColorType, Color};
use super::base_color::ITerminalColor; use super::base_color::ITerminalColor;
use kernel::windows_kernel;
use kernel::windows_kernel::kernel;
use winapi::um::wincon;
/// This struct is an windows implementation for color related actions. /// This struct is an windows implementation for color related actions.
#[derive(Debug)] #[derive(Debug)]
@ -12,27 +14,120 @@ pub struct WinApiColor {
impl Construct for WinApiColor { impl Construct for WinApiColor {
fn new() -> Box<WinApiColor> { fn new() -> Box<WinApiColor> {
Box::from(WinApiColor { Box::from(WinApiColor {
original_console_color: windows_kernel::kernel::get_original_console_color(), original_console_color: kernel::get_original_console_color(),
}) })
} }
} }
impl ITerminalColor for WinApiColor { impl ITerminalColor for WinApiColor {
/// This will set the foreground color by the given winapi color.
fn set_fg(&self, fg_color: Color) { fn set_fg(&self, fg_color: Color) {
let color_value = &self.color_value(fg_color, ColorType::Foreground); let color_value = &self.color_value(fg_color, ColorType::Foreground);
windows_kernel::color::set_fg_color(color_value.parse().unwrap());
let csbi = kernel::get_console_screen_buffer_info();
// Notice that the color values are stored in wAttribute.
// So we need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16;
let attrs = csbi.wAttributes;
let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | bg_color;
// background intensity is a seperate value in attrs,
// wee need to check if this was applied to the current bg color.
if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 {
color = color | wincon::BACKGROUND_INTENSITY as u16;
} }
kernel::set_console_text_attribute(color);
}
/// This will set the background color by the given winapi color value.
fn set_bg(&self, bg_color: Color) { fn set_bg(&self, bg_color: Color) {
let color_value = &self.color_value(bg_color, ColorType::Background); let color_value = &self.color_value(bg_color, ColorType::Background);
windows_kernel::color::set_bg_color(color_value.parse().unwrap());
let csbi = kernel::get_console_screen_buffer_info();
// Notice that the color values are stored in wAttribute.
// So wee need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16;
let attrs = csbi.wAttributes;
let fg_color = attrs & 0x0007;
color = fg_color | color_value.parse::<u16>().unwrap();
// Foreground intensity is a separate value in attrs,
// So we need to check if this was applied to the current fg color.
if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 {
color = color | wincon::FOREGROUND_INTENSITY as u16;
} }
kernel::set_console_text_attribute(color);
}
/// This will reset the colors to the given winapi color value.
fn reset(&self) { fn reset(&self) {
windows_kernel::color::reset(self.original_console_color); kernel::set_console_text_attribute(self.original_console_color);
} }
/// This will get the winapi color value from the Color and ColorType struct
fn color_value(&self, color: Color, color_type: ColorType) -> String { fn color_value(&self, color: Color, color_type: ColorType) -> String {
windows_kernel::color::winapi_color_val(color, color_type).to_string()
use crossterm_style::{Color, ColorType};
let winapi_color: u16;
let fg_green = wincon::FOREGROUND_GREEN;
let fg_red = wincon::FOREGROUND_RED;
let fg_blue = wincon::FOREGROUND_BLUE;
let fg_intensity = wincon::FOREGROUND_INTENSITY;
let bg_green = wincon::BACKGROUND_GREEN;
let bg_red = wincon::BACKGROUND_RED;
let bg_blue = wincon::BACKGROUND_BLUE;
let bg_intensity = wincon::BACKGROUND_INTENSITY;
match color_type {
ColorType::Foreground => {
winapi_color = match color {
Color::Black => 0,
Color::Red => fg_intensity | fg_red,
Color::DarkRed => fg_red,
Color::Green => fg_intensity | fg_green,
Color::DarkGreen => fg_green,
Color::Yellow => fg_intensity | fg_green | fg_red,
Color::DarkYellow => fg_green | fg_red,
Color::Blue => fg_intensity | fg_blue,
Color::DarkBlue => fg_blue,
Color::Magenta => fg_intensity | fg_red | fg_blue,
Color::DarkMagenta => fg_red | fg_blue,
Color::Cyan => fg_intensity | fg_green | fg_blue,
Color::DarkCyan => fg_green | fg_blue,
Color::Grey => fg_intensity,
Color::White => fg_intensity | fg_red | fg_green | fg_blue,
};
}
ColorType::Background => {
winapi_color = match color {
Color::Black => 0,
Color::Red => bg_intensity | bg_red,
Color::DarkRed => bg_red,
Color::Green => bg_intensity | bg_green,
Color::DarkGreen => bg_green,
Color::Yellow => bg_intensity | bg_green | bg_red,
Color::DarkYellow => bg_green | bg_red,
Color::Blue => bg_intensity | bg_blue,
Color::DarkBlue => bg_blue,
Color::Magenta => bg_intensity | bg_red | bg_blue,
Color::DarkMagenta => bg_red | bg_blue,
Color::Cyan => bg_intensity | bg_green | bg_blue,
Color::DarkCyan => bg_green | bg_blue,
Color::Grey => bg_intensity,
Color::White => bg_intensity | bg_red | bg_green | bg_blue,
};
}
};
winapi_color.to_string()
} }
} }

View File

@ -1,6 +1,7 @@
use crossterm_style::{Color, StyledObject}; use crossterm_style::{Color, StyledObject};
use std::fmt::Display; use std::fmt::Display;
#[cfg(unix)]
use super::super::Attribute; use super::super::Attribute;
/// Struct that contains the style properties that can be applied to an displayable object. /// Struct that contains the style properties that can be applied to an displayable object.

View File

@ -2,6 +2,7 @@ use std;
use std::fmt; use std::fmt;
use std::io::Write; use std::io::Write;
#[cfg(unix)]
use super::super::Attribute; use super::super::Attribute;
use crossterm_style::{Color, ObjectStyle}; use crossterm_style::{Color, ObjectStyle};

View File

@ -3,7 +3,8 @@ use std::io::Write;
use Construct; use Construct;
use super::base_terminal::{ClearType, ITerminal}; use super::base_terminal::{ClearType, ITerminal};
use shared::functions::resize_terminal;
use shared::functions::get_terminal_size;
/// This struct is an ansi implementation for terminal related actions. /// This struct is an ansi implementation for terminal related actions.
pub struct AnsiTerminal ; pub struct AnsiTerminal ;
@ -37,7 +38,7 @@ impl ITerminal for AnsiTerminal {
} }
fn terminal_size(&self) -> (u16, u16) { fn terminal_size(&self) -> (u16, u16) {
resize_terminal() get_terminal_size()
} }
fn scroll_up(&self, count: i16) { fn scroll_up(&self, count: i16) {

View File

@ -1,6 +1,9 @@
mod raw_terminal;
mod base_terminal; mod base_terminal;
mod terminal; mod terminal;
pub mod screen;
mod ansi_terminal; mod ansi_terminal;
mod winapi_terminal; mod winapi_terminal;
@ -9,3 +12,4 @@ use self::winapi_terminal::WinApiTerminal;
pub use self::base_terminal::ClearType; pub use self::base_terminal::ClearType;
pub use self::terminal::{ Terminal, terminal}; pub use self::terminal::{ Terminal, terminal};
pub use self::raw_terminal::{RawTerminal, IntoRawMode};

View File

@ -0,0 +1,55 @@
#[cfg(not(windows))]
use crossterm_state::commands::unix_command::EnableRawModeCommand;
#[cfg(windows)]
use crossterm_state::commands::win_commands::EnableRawModeCommand;
use crossterm_state::commands::IContextCommand;
use shared::traits::Construct;
use crossterm_state::{ Context };
use crossterm_state::commands::ICommand;
use std::io::{self, Write};
pub struct RawTerminal<'a, W: Write>
{
output: W,
key: i16,
context: &'a mut Context
}
impl<'a, W: Write> Drop for RawTerminal<'a,W> {
fn drop(&mut self) {
self.context.undo_state(self.key);
}
}
pub trait IntoRawMode: Write + Sized
{
fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result<RawTerminal<Self>>;
}
impl<W: Write> IntoRawMode for W
{
fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result<RawTerminal<Self>>
{
let (mut command, key) = EnableRawModeCommand::new(context);
let success = command.execute();
if success
{
Ok(RawTerminal {output: self, key: key, context: context})
}else { panic!("cannot move into raw mode") }
}
}
impl<'a, W: Write> Write for RawTerminal<'a, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
}

View File

@ -0,0 +1,118 @@
use std::io::{self, Write};
use std::ops;
use std::any::Any;
use crossterm_state::commands::{shared_commands,win_commands,ICommand, CommandType};
use crossterm_state::commands::IContextCommand;
use shared::traits::Construct;
use std::fmt;
/// let context = ScreenContext::new();
/// ToMainScreen {}.execute(&mut context);
/// ToAlternateScreen {}.execute(context);
///
///
/// ToMainScreen {}.execute(&mut context);
///
/// context.to_main();
/// let alternate_screen = context.to_alternate(stdout());
///
/// let alternate = AlternateScreen::from(stdout, context);
/// ToMainScreen [} .execute(ScreenContext::new()))
/// ToAlternateScreen {}. execute(ScreenContext::new());
pub struct ToMainScreen;
impl fmt::Display for ToMainScreen
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
get_to_alternate_screen_command().undo();
Ok(())
}
}
/// Switch to the alternate screen buffer of the terminal.
pub struct ToAlternateScreen;
impl fmt::Display for ToAlternateScreen
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
get_to_alternate_screen_command().execute();
Ok(())
}
}
pub struct AlternateScreen<W: Write> {
/// The output target.
output: W,
}
impl<W: Write> AlternateScreen< W> {
pub fn from(mut output: W) -> Self {
write!(output, "{}", ToAlternateScreen);
AlternateScreen { output: output }
}
}
impl<W: Write> ops::Deref for AlternateScreen<W> {
type Target = W;
fn deref(&self) -> &W {
&self.output
}
}
impl<W: Write> ops::DerefMut for AlternateScreen<W> {
fn deref_mut(&mut self) -> &mut W {
&mut self.output
}
}
impl<W: Write> Write for AlternateScreen<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.output.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.output.flush()
}
}
impl<W: Write> Drop for AlternateScreen<W>
{
fn drop(&mut self)
{
write!(self, "{}", ToMainScreen).expect("switch to main screen");
}
}
fn get_to_alternate_screen_command() -> Box<ICommand>
{
// let mut does_support = true;
let mut command: Option<Box<ICommand>> = None;
//
// let succeeded = false;
//
// if cfg!(target_os = "windows")
// {
// #[cfg(windows)]
// use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
//
// // Try to enable ansi on windows if not than use WINAPI.
// does_support = try_enable_ansi_support();
//
// println!("does support: {}", does_support);
// if !does_support
// {
command = Some(win_commands::ToAlternateScreenBufferCommand::new());
command.unwrap()
// }
// }
//
// if does_support
// {
// command = Some(shared_commands::ToAlternateScreenBufferCommand::new().0);
// }
//
// command.unwrap()
}

View File

@ -1,9 +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 std::ops::Drop;
use Construct; use {Construct, Context};
use super::base_terminal::{ClearType, ITerminal}; use super::base_terminal::{ClearType, ITerminal};
use shared::functions::get_module;
use super::AnsiTerminal; use super::AnsiTerminal;
use super::WinApiTerminal; use super::WinApiTerminal;
@ -11,32 +13,21 @@ 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.
pub struct Terminal { pub struct Terminal {
terminal: Option<Box<ITerminal>>, terminal: Option<Box<ITerminal>>,
context: Context
} }
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 mut term: Option<Box<ITerminal>> = None;
let mut does_support = true; let mut context = Context::new();
if cfg!(target_os = "windows") { #[cfg(target_os = "windows")]
use kernel::windows_kernel::kernel::try_enable_ansi_support; let terminal = get_module::<Box<ITerminal>>(WinApiTerminal::new(), AnsiTerminal::new(), &mut context);
does_support = try_enable_ansi_support(); #[cfg(not(target_os = "windows"))]
// this returns an bool if the current windows console supports ansi. let terminal = Some(AnsiTerminal::new());
if !does_support
{
term = Some(WinApiTerminal::new());
}
}
if does_support Terminal { terminal: terminal, context: context }
{
println!("This console does support ansi");
term = Some(AnsiTerminal::new());
}
Terminal { terminal: term }
} }
/// Clear the current cursor by specifying the clear type /// Clear the current cursor by specifying the clear type
@ -173,6 +164,7 @@ impl Terminal {
/// ///
/// ``` /// ```
/// ///
pub fn terminal() -> Box<Terminal> { pub fn terminal() -> Box<Terminal>
{
Box::from(Terminal::new()) Box::from(Terminal::new())
} }

View File

@ -1,7 +1,8 @@
use Construct; use {Construct};
use crossterm_cursor::cursor;
use super::base_terminal::{ClearType, ITerminal}; use super::base_terminal::{ClearType, ITerminal};
use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,};
use kernel::windows_kernel::terminal; use kernel::windows_kernel::{kernel, terminal};
/// This struct is an windows implementation for terminal related actions. /// This struct is an windows implementation for terminal related actions.
pub struct WinApiTerminal; pub struct WinApiTerminal;
@ -13,29 +14,233 @@ impl Construct for WinApiTerminal {
} }
impl ITerminal for WinApiTerminal { impl ITerminal for WinApiTerminal {
/// Clear the screen to the given cleartype.
fn clear(&self, clear_type: ClearType) { fn clear(&self, clear_type: ClearType) {
println! ("Windows!!!"); let csbi = kernel::get_console_screen_buffer_info();
let pos = cursor().pos();
match clear_type match clear_type
{ {
ClearType::All => terminal::clear_entire_screen(), ClearType::All => clear_entire_screen(csbi),
ClearType::FromCursorDown => terminal::clear_after_cursor(), ClearType::FromCursorDown => clear_after_cursor(pos,csbi),
ClearType::FromCursorUp => terminal::clear_before_cursor(), ClearType::FromCursorUp => clear_before_cursor(pos, csbi),
ClearType::CurrentLine => terminal::clear_current_line(), ClearType::CurrentLine => clear_current_line(pos, csbi),
ClearType::UntilNewLine => terminal::clear_until_line(), ClearType::UntilNewLine => clear_until_line(pos, csbi),
}; };
} }
/// Get the terminal size
fn terminal_size(&self) -> (u16, u16) { fn terminal_size(&self) -> (u16, u16) {
terminal::terminal_size() terminal::terminal_size()
} }
/// Scroll up n` rows
fn scroll_up(&self, count: i16) { fn scroll_up(&self, count: i16) {
// yet to be inplemented // yet to be inplemented
} }
/// Scroll down `n` rows
fn scroll_down(&self, count: i16) { fn scroll_down(&self, count: i16) {
terminal::scroll_down(count as i16); let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window;
// Set srctWindow to the current window size and location.
srct_window = csbi.srWindow;
// Check whether the window is too close to the screen buffer top
if srct_window.Bottom < csbi.dwSize.Y - count {
srct_window.Top += count; // move top down
srct_window.Bottom += count; // move bottom down
let success = kernel::set_console_info(true, &mut srct_window);
if success {
panic!("Something went wrong when scrolling down");
}
}
} }
fn set_size(&self, width: i16, height: i16) { terminal::resize_terminal(width,height); } /// Set the current terminal size
fn set_size(&self, width: i16, height: i16) {
if width <= 0
{
panic!("Cannot set the terminal width lower than 1");
}
if height <= 0
{
panic!("Cannot set the terminal height lower then 1")
}
// Get the position of the current console window
let csbi = kernel::get_console_screen_buffer_info();
let mut success = false;
// If the buffer is smaller than this new window size, resize the
// buffer to be large enough. Include window position.
let mut resize_buffer = false;
let mut size = COORD { X: csbi.dwSize.X, Y: csbi.dwSize.Y };
if csbi.dwSize.X < csbi.srWindow.Left + width
{
if csbi.srWindow.Left >= i16::max_value() - width
{
panic!("Argument out of range when setting terminal width.");
}
size.X = csbi.srWindow.Left + width;
resize_buffer = true;
}
if csbi.dwSize.Y < csbi.srWindow.Top + height {
if csbi.srWindow.Top >= i16::max_value() - height
{
panic!("Argument out of range when setting terminal height");
}
size.Y = csbi.srWindow.Top + height;
resize_buffer = true;
}
if resize_buffer {
success = kernel::set_console_screen_buffer_size(size);
if !success
{
panic!("Something went wrong when setting screen buffer size.");
}
}
let mut fsr_window: SMALL_RECT = csbi.srWindow;
// Preserve the position, but change the size.
fsr_window.Bottom = fsr_window.Top + height;
fsr_window.Right = fsr_window.Left + width;
let success = kernel::set_console_info(true, &fsr_window);
if success {
// If we resized the buffer, un-resize it.
if resize_buffer {
kernel::set_console_screen_buffer_size(csbi.dwSize);
}
let bounds = kernel::get_largest_console_window_size();
if width > bounds.X
{
panic!("Argument width: {} out of range when setting terminal width.", width);
}
if height > bounds.Y
{
panic!("Argument height: {} out of range when setting terminal height", height);
}
}
}
}
pub fn clear_after_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO) {
let (mut x,mut y) = pos;
// if cursor position is at the outer right position
if x as i16 > csbi.dwSize.X
{
y += 1;
x = 0;
}
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear(start_location,cells_to_write);
}
pub fn clear_before_cursor(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO) {
let (xpos,ypos) = pos;
// one cell after cursor position
let x = 0;
// one at row of cursor position
let y = 0;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X as u32 * ypos as u32) + (xpos as u32 + 1);
clear(start_location, cells_to_write);
}
pub fn clear_entire_screen(csbi: CONSOLE_SCREEN_BUFFER_INFO) {
// position x at start
let x = 0;
// position y at start
let y = 0;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear( start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor().goto(0, 0);
}
pub fn clear_current_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO)
{
// position x at start
let x = 0;
// position y at start
let y = pos.1;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32;
clear(start_location, cells_to_write);
// put the cursor back at 1 cell on current row
cursor().goto(0, y);
}
pub fn clear_until_line(pos: (u16,u16), csbi: CONSOLE_SCREEN_BUFFER_INFO)
{
let (x,y) = pos;
// location where to start clearing
let start_location = COORD { X: x as i16, Y: y as i16};
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X - x as i16) as u32;
clear(start_location, cells_to_write);
// put the cursor back at original cursor position
cursor().goto(x,y);
}
fn clear(
start_loaction: COORD,
cells_to_write: u32
) {
let mut cells_written = 0;
let mut success = false;
success = kernel::fill_console_output_character(&mut cells_written, start_loaction, cells_to_write);
if !success
{
panic!("Could not clear screen after cursor");
}
cells_written = 0;
success = kernel::fill_console_output_attribute(&mut cells_written, start_loaction, cells_to_write);
if !success {
panic!("Couldnot reset attributes after cursor");
}
} }

View File

@ -1,34 +0,0 @@
use libc;
use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort};
use self::libc::ioctl;
/// A representation of the size of the current terminal
#[repr(C)]
#[derive(Debug)]
pub struct UnixSize {
/// number of rows
pub rows: c_ushort,
/// number of columns
pub cols: c_ushort,
x: c_ushort,
y: c_ushort,
}
/// Gets the current terminal size
pub fn terminal_size() -> (u16,u16) {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize {
rows: 0,
cols: 0,
x: 0,
y: 0,
};
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &us) };
if r == 0 {
// 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 {
(0,0)
}
}

View File

@ -1,4 +1,4 @@
#[cfg(unix)] #[cfg(unix)]
pub mod linux_kernel; pub mod unix_kernel;
#[cfg(windows)] #[cfg(windows)]
pub mod windows_kernel; pub mod windows_kernel;

View File

@ -1 +1,2 @@
pub mod terminal; pub mod terminal;

View File

@ -0,0 +1,115 @@
use libc;
use self::libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort, ioctl};
pub use self::libc::termios as Termios;
use std::io;
/// A representation of the size of the current terminal
#[repr(C)]
#[derive(Debug)]
pub struct UnixSize {
/// number of rows
pub rows: c_ushort,
/// number of columns
pub cols: c_ushort,
x: c_ushort,
y: c_ushort,
}
/// Gets the current terminal size
pub fn terminal_size() -> (u16,u16) {
// http://rosettacode.org/wiki/Terminal_control/Dimensions#Library:_BSD_libc
let us = UnixSize {
rows: 0,
cols: 0,
x: 0,
y: 0,
};
let r = unsafe { ioctl(STDOUT_FILENO, TIOCGWINSZ, &us) };
if r == 0 {
// 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 {
(0,0)
}
}
/// Get the current cursor position
pub fn pos() -> (u16,u16)
{
use std::io::{Write,Read};
use std::io::Error;
let command = NoncanonicalModeCommand::new();
command.execute();
// This code is original written by term_cursor credits to them.
let mut stdout = std::io::stdout();
// Write command
stdout.write(b"\x1B[6n")?;
stdout.flush()?;
// Read back result
let mut buf = [0u8; 2];
// Expect `ESC[`
std::io::stdin().read_exact(&mut buf)?;
if buf[0] != 0x1B || buf[1] as char != '[' {
return (0,0);
}
// Read rows and cols through a ad-hoc integer parsing function
let read_num = || -> Result<(i32, char), Error> {
let mut num = 0;
let mut c;
loop {
let mut buf = [0u8; 1];
std::io::stdin().read_exact(&mut buf)?;
c = buf[0] as char;
if let Some(d) = c.to_digit(10) {
num = if num == 0 { 0 } else { num * 10 };
num += d as i32;
} else {
break;
}
}
Ok((num, c))
};
// Read rows and expect `;`
let (rows, c) = read_num()?;
if c != ';' {
return (0,0);
}
// Read cols
let (cols, c) = read_num()?;
// Expect `R`
let res = if c == 'R' { Ok((cols, rows)) } else { return (0,0); };
command.undo();
res
}
pub fn set_terminal_mode(terminal: &Termios) -> io::Result<()>
{
extern "C" {
pub fn tcsetattr(fd: c_int, opt: c_int, termptr: *const Termios) -> c_int;
}
cvt(unsafe { tcsetattr(0, 0, termios) }).and(Ok(()))
}
pub fn get_terminal_mode() -> io::Result<Termios>
{
extern "C" {
pub fn tcgetattr(fd: c_int, termptr: *mut Termios) -> c_int;
}
unsafe {
let mut termios = mem::zeroed();
cvt(tcgetattr(0, &mut termios))?;
Ok(termios)
}
}

View File

@ -0,0 +1,69 @@
use crossterm_state::Context;
use crossterm_state::commands::IContextCommand;
use shared::traits::Construct;
static mut IS_ANSI_ON_WINDOWS_ENABLED: Option<bool> = None;
static mut DOES_WINDOWS_SUPPORT_ANSI: Option<bool> = None;
static mut HAS_BEEN_TRYED_TO_ENABLE: bool = false;
/// Try enable ANSI escape codes and return the result.
pub fn try_enable_ansi_support(context: &mut Context) -> bool
{
use crossterm_state::commands::win_commands::EnableAnsiCommand;
let (mut command, key) = EnableAnsiCommand::new(context);
let success = command.execute();
set_is_windows_ansi_supportable(success);
set_ansi_enabled(success);
has_been_tried_to_enable(true);
success
}
/// Get whether ansi has been enabled.
pub fn ansi_enabled() -> bool
{
unsafe { IS_ANSI_ON_WINDOWS_ENABLED.unwrap_or_else(| | false) }
}
/// Get whether windows supports ansi
pub fn windows_supportable() -> bool
{
unsafe { DOES_WINDOWS_SUPPORT_ANSI.unwrap_or_else(| | false)}
}
/// Get whether ansi has been tried to enable before.
pub fn has_been_tried_to_enable_ansi() -> bool
{
unsafe
{
return HAS_BEEN_TRYED_TO_ENABLE;
}
}
/// Set the is ansi escape property enabled or disabled. So whe can determine if the ansi escape codes are enabled.
pub fn set_ansi_enabled(is_enabled :bool)
{
unsafe
{
IS_ANSI_ON_WINDOWS_ENABLED = Some(is_enabled);
}
}
/// Set the is_windows_ansi_supportable property. So whe can determine whether windows supports ansi.
fn set_is_windows_ansi_supportable(is_enabled :bool)
{
unsafe
{
DOES_WINDOWS_SUPPORT_ANSI = Some(is_enabled);
}
}
/// Set the has_been_tried_to_enable property. So we can determine whether ansi has been tried to enable before.
fn has_been_tried_to_enable(has_been_tried: bool)
{
unsafe
{
HAS_BEEN_TRYED_TO_ENABLE = has_been_tried;
}
}

View File

@ -1,108 +0,0 @@
use winapi::um::wincon;
use super::{kernel};
use crossterm_style as style;
/// This will set the foreground color by the given winapi color.
pub fn set_fg_color(fg_color: u16) {
let csbi = kernel::get_console_screen_buffer_info();
// Notice that the color values are stored in wAttribute.
// So wee need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16;
let attrs = csbi.wAttributes;
let bg_color = attrs & 0x0070;
color = fg_color | bg_color;
// background intensity is a seperate value in attrs,
// wee need to check if this was applied to the current bg color.
if (attrs & wincon::BACKGROUND_INTENSITY as u16) != 0 {
color = color | wincon::BACKGROUND_INTENSITY as u16;
}
kernel::set_console_text_attribute(color);
}
/// This will set the background color by the given winapi color value.
pub fn set_bg_color(bg_color: u16) {
let csbi = kernel::get_console_screen_buffer_info();
// Notice that the color values are stored in wAttribute.
// So wee need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16;
let attrs = csbi.wAttributes;
let fg_color = attrs & 0x0007;
color = fg_color | bg_color;
// foreground intensity is a separate value in attrs,
// wee need to check if this was applied to the current fg color.
if (attrs & wincon::FOREGROUND_INTENSITY as u16) != 0 {
color = color | wincon::FOREGROUND_INTENSITY as u16;
}
kernel::set_console_text_attribute(color);
}
/// This will reset the colors to the given winapi color value.
pub fn reset(original_color: u16) {
kernel::set_console_text_attribute(original_color);
}
/// This will get the winapi color value from the Color and ColorType struct
pub fn winapi_color_val(color: style::Color, color_type: style::ColorType) -> u16 {
use crossterm_style::{Color, ColorType};
let winapi_color: u16;
let fg_green = wincon::FOREGROUND_GREEN;
let fg_red = wincon::FOREGROUND_RED;
let fg_blue = wincon::FOREGROUND_BLUE;
let fg_intensity = wincon::FOREGROUND_INTENSITY;
let bg_green = wincon::BACKGROUND_GREEN;
let bg_red = wincon::BACKGROUND_RED;
let bg_blue = wincon::BACKGROUND_BLUE;
let bg_intensity = wincon::BACKGROUND_INTENSITY;
match color_type {
ColorType::Foreground => {
winapi_color = match color {
Color::Black => 0,
Color::Red => fg_intensity | fg_red,
Color::DarkRed => fg_red,
Color::Green => fg_intensity | fg_green,
Color::DarkGreen => fg_green,
Color::Yellow => fg_intensity | fg_green | fg_red,
Color::DarkYellow => fg_green | fg_red,
Color::Blue => fg_intensity | fg_blue,
Color::DarkBlue => fg_blue,
Color::Magenta => fg_intensity | fg_red | fg_blue,
Color::DarkMagenta => fg_red | fg_blue,
Color::Cyan => fg_intensity | fg_green | fg_blue,
Color::DarkCyan => fg_green | fg_blue,
Color::Grey => fg_intensity,
Color::White => fg_intensity | fg_red | fg_green | fg_blue,
};
}
ColorType::Background => {
winapi_color = match color {
Color::Black => 0,
Color::Red => bg_intensity | bg_red,
Color::DarkRed => bg_red,
Color::Green => bg_intensity | bg_green,
Color::DarkGreen => bg_green,
Color::Yellow => bg_intensity | bg_green | bg_red,
Color::DarkYellow => bg_green | bg_red,
Color::Blue => bg_intensity | bg_blue,
Color::DarkBlue => bg_blue,
Color::Magenta => bg_intensity | bg_red | bg_blue,
Color::DarkMagenta => bg_red | bg_blue,
Color::Cyan => bg_intensity | bg_green | bg_blue,
Color::DarkCyan => bg_green | bg_blue,
Color::Grey => bg_intensity,
Color::White => bg_intensity | bg_red | bg_green | bg_blue,
};
}
};
winapi_color as u16
}

View File

@ -1,36 +1,29 @@
use super::kernel; use super::kernel;
use crossterm_cursor::cursor;
/// This stores the cursor pos, at program level. So it can be recalled later. /// This stores the cursor pos, at program level. So it can be recalled later.
static mut SAVED_CURSOR_POS:(i16,i16) = (0,0); static mut SAVED_CURSOR_POS:(u16,u16) = (0,0);
/// Set the current cursor position to X and Y
pub fn set(x: i16, y: i16)
{
kernel::set_console_cursor_position(x, y );
}
/// Reset to saved cursor position /// Reset to saved cursor position
pub fn reset_to_saved_position() pub fn reset_to_saved_position()
{ {
unsafe { unsafe {
kernel::set_console_cursor_position(SAVED_CURSOR_POS.0, SAVED_CURSOR_POS.1); kernel::set_console_cursor_position(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16);
} }
} }
/// Save current cursor position to recall later. /// Save current cursor position to recall later.
pub fn save_cursor_pos() pub fn save_cursor_pos()
{ {
let position = pos(); let position = cursor().pos();
unsafe { unsafe {
SAVED_CURSOR_POS = (position.0, position.1); SAVED_CURSOR_POS = (position.0, position.1);
} }
} }
/// Get current cursor position (X,Y) pub fn pos() -> (u16,u16)
pub fn pos() -> (i16,i16)
{ {
let csbi = kernel::get_console_screen_buffer_info(); let csbi = kernel::get_console_screen_buffer_info();
( csbi.dwCursorPosition.X , csbi.dwCursorPosition.Y ) ( csbi.dwCursorPosition.X as u16, csbi.dwCursorPosition.Y as u16)
} }

View File

@ -1,13 +1,15 @@
use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::HANDLE; use winapi::um::winnt::HANDLE;
use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_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,GetConsoleMode}; use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode, };
use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize,
use winapi::um::wincon;
use winapi::shared::minwindef::{TRUE};
use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, CreateConsoleScreenBuffer,SetConsoleActiveScreenBuffer,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo, GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
FillConsoleOutputCharacterA, FillConsoleOutputAttribute, ENABLE_VIRTUAL_TERMINAL_PROCESSING,ENABLE_VIRTUAL_TERMINAL_INPUT, FillConsoleOutputCharacterA, FillConsoleOutputAttribute,
CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, DISABLE_NEWLINE_AUTO_RETURN CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, CHAR_INFO, PSMALL_RECT
}; };
use super::{Empty}; use super::{Empty};
@ -62,7 +64,6 @@ pub fn is_valid_handle(handle: &HANDLE) -> bool {
} }
} }
/// Get console screen buffer info.
pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO { pub fn get_console_screen_buffer_info() -> CONSOLE_SCREEN_BUFFER_INFO {
let output_handle = get_output_handle(); let output_handle = get_output_handle();
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty(); let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
@ -77,26 +78,6 @@ 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();
@ -220,6 +201,90 @@ pub fn fill_console_output_attribute(cells_written: &mut u32, start_location: CO
is_true(success) is_true(success)
} }
pub fn create_console_screen_buffer() -> HANDLE
{
use winapi::shared::ntdef::NULL;
use winapi::um::wincon::CONSOLE_TEXTMODE_BUFFER;
use winapi::um::winnt::{GENERIC_READ, GENERIC_WRITE, FILE_SHARE_READ, FILE_SHARE_WRITE};
use winapi::um::minwinbase::SECURITY_ATTRIBUTES;
use std::mem::size_of;
unsafe
{
let mut security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES
{
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: NULL,
bInheritHandle: TRUE
};
let new_screen_buffer = CreateConsoleScreenBuffer(
GENERIC_READ | // read/write access
GENERIC_WRITE,
FILE_SHARE_READ |
FILE_SHARE_WRITE, // shared
&mut security_attr, // default security attributes
CONSOLE_TEXTMODE_BUFFER, // must be TEXTMODE
NULL
);
new_screen_buffer
}
}
pub fn set_active_screen_buffer(new_buffer: HANDLE)
{
unsafe
{
if !is_true(SetConsoleActiveScreenBuffer(new_buffer))
{
panic!("Cannot set active screen buffer");
}
}
}
pub fn read_console_output(read_buffer: &HANDLE, copy_buffer: &mut [CHAR_INFO;160], buffer_size: COORD, buffer_coord: COORD, source_buffer: PSMALL_RECT)
{
use self::wincon::ReadConsoleOutputA;
unsafe
{
if !is_true(ReadConsoleOutputA(
*read_buffer, // screen buffer to read from
copy_buffer.as_mut_ptr(), // buffer to copy into
buffer_size, // col-row size of chiBuffer
buffer_coord, // top left dest. cell in chiBuffer
source_buffer) // screen buffer source rectangle
){
panic!("Cannot read console output");
}
}
}
pub fn write_console()
{
}
pub fn write_console_output(write_buffer: &HANDLE, copy_buffer: &mut [CHAR_INFO;160], buffer_size: COORD, buffer_coord: COORD, source_buffer: PSMALL_RECT)
{
use self::wincon::WriteConsoleOutputA;
unsafe
{
if !is_true(WriteConsoleOutputA(
*write_buffer, // screen buffer to write to
copy_buffer.as_mut_ptr(), // buffer to copy into
buffer_size, // col-row size of chiBuffer
buffer_coord, // top left dest. cell in chiBuffer
source_buffer)// screen buffer source rectangle
){
panic!("Cannot write to console output");
}
}
}
/// Parse integer to an bool /// Parse integer to an bool
fn is_true(value: i32) -> bool fn is_true(value: i32) -> bool
{ {

View File

@ -1,7 +1,38 @@
pub mod cursor; extern crate winapi;
pub mod color;
pub mod kernel;
pub mod terminal;
mod winapi_extentions;
use winapi::um::wincon::{COORD, CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT};
use shared::traits::Empty; use shared::traits::Empty;
pub mod kernel;
pub mod cursor;
pub mod terminal;
pub mod ansi_support;
impl Empty for COORD {
fn empty() -> COORD {
COORD { X: 0, Y: 0 }
}
}
impl Empty for SMALL_RECT {
fn empty() -> SMALL_RECT {
SMALL_RECT {
Top: 0,
Right: 0,
Bottom: 0,
Left: 0,
}
}
}
impl Empty for CONSOLE_SCREEN_BUFFER_INFO {
fn empty() -> CONSOLE_SCREEN_BUFFER_INFO {
CONSOLE_SCREEN_BUFFER_INFO {
dwSize: COORD::empty(),
dwCursorPosition: COORD::empty(),
wAttributes: 0,
srWindow: SMALL_RECT::empty(),
dwMaximumWindowSize: COORD::empty(),
}
}
}

View File

@ -1,221 +1,8 @@
use super::{cursor, kernel}; /// Get the terminal size
use winapi::um::wincon::{SMALL_RECT, COORD};
/// Get the terminal size (y,x)
pub fn terminal_size() -> (u16, u16) { pub fn terminal_size() -> (u16, u16) {
let csbi = kernel::get_console_screen_buffer_info(); let csbi = super::kernel::get_console_screen_buffer_info();
( (
(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
pub fn scroll_down(rows: i16) {
let csbi = kernel::get_console_screen_buffer_info();
let mut srct_window;
// Set srctWindow to the current window size and location.
srct_window = csbi.srWindow;
// Check whether the window is too close to the screen buffer top
if srct_window.Bottom < csbi.dwSize.Y - rows {
srct_window.Top += rows; // move top down
srct_window.Bottom += rows; // move bottom down
let success = kernel::set_console_info(true, &mut srct_window);
if success {
panic!("Something went wrong when scrolling down");
}
}
}
pub fn clear_after_cursor() {
let csbi = kernel::get_console_screen_buffer_info();
let (mut x,mut y) = cursor::pos();
// if cursor position is at the outer right position
if x > csbi.dwSize.X
{
y += 1;
x = 0;
}
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear(start_location,cells_to_write);
}
pub fn clear_before_cursor() {
let csbi = kernel::get_console_screen_buffer_info();
let (xpos,ypos) = cursor::pos();
// one cell after cursor position
let x = 0;
// one at row of cursor position
let y = 0;
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X as u32 * ypos as u32) + (xpos as u32 + 1);
clear(start_location, cells_to_write);
}
pub fn clear_entire_screen() {
let csbi = kernel::get_console_screen_buffer_info();
// position x at start
let x = 0;
// position y at start
let y = 0;
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
clear( start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(0, 0);
}
pub fn clear_current_line()
{
let csbi = kernel::get_console_screen_buffer_info();
// position x at start
let x = 0;
// position y at start
let y = cursor::pos().1;
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = csbi.dwSize.X as u32;
clear(start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(x, y);
}
pub fn clear_until_line()
{
let csbi = kernel::get_console_screen_buffer_info();
let (x,y) = cursor::pos();
// location where to start clearing
let start_location = COORD { X: x, Y: y };
// get sum cells before cursor
let cells_to_write = (csbi.dwSize.X - x) as u32;
clear(start_location, cells_to_write);
// put the cursor back at (0, 0)
cursor::set(x, y);
}
pub fn resize_terminal(width: i16, height: i16)
{
if width <= 0
{
panic!("Cannot set the terminal width lower than 1");
}
if height <= 0
{
panic!("Cannot set the terminal height lower then 1")
}
// Get the position of the current console window
let csbi = kernel::get_console_screen_buffer_info();
let mut success = false;
// If the buffer is smaller than this new window size, resize the
// buffer to be large enough. Include window position.
let mut resize_buffer = false;
let mut size = COORD { X: csbi.dwSize.X, Y: csbi.dwSize.Y };
if csbi.dwSize.X < csbi.srWindow.Left + width
{
if csbi.srWindow.Left >= i16::max_value() - width
{
panic!("Argument out of range when setting terminal width.");
}
size.X = csbi.srWindow.Left + width;
resize_buffer = true;
}
if csbi.dwSize.Y < csbi.srWindow.Top + height {
if csbi.srWindow.Top >= i16::max_value() - height
{
panic!("Argument out of range when setting terminal height");
}
size.Y = csbi.srWindow.Top + height;
resize_buffer = true;
}
if resize_buffer {
success = kernel::set_console_screen_buffer_size(size);
if !success
{
panic!("Something went wrong when setting screen buffer size.");
}
}
let mut fsr_window: SMALL_RECT = csbi.srWindow;
// Preserve the position, but change the size.
fsr_window.Bottom = fsr_window.Top + height;
fsr_window.Right = fsr_window.Left + width;
let success = kernel::set_console_info(true, &fsr_window);
if success {
// If we resized the buffer, un-resize it.
if resize_buffer {
kernel::set_console_screen_buffer_size(csbi.dwSize);
}
let bounds = kernel::get_largest_console_window_size();
if width > bounds.X
{
panic!("Argument width: {} out of range when setting terminal width.", width);
}
if height > bounds.Y
{
panic!("Argument height: {} out of range when setting terminal height", height);
}
}
}
fn clear(
start_loaction: COORD,
cells_to_write: u32
) {
let mut cells_written = 0;
let mut success = false;
success = kernel::fill_console_output_character(&mut cells_written,start_loaction,cells_to_write);
if !success {
panic!("Could not clear screen after cursor");
}
cells_written = 0;
success = kernel::fill_console_output_attribute(&mut cells_written,start_loaction, cells_to_write);
if !success {
panic!("Couldnot reset attributes after cursor");
}
}

View File

@ -1,31 +0,0 @@
use winapi::um::wincon::{COORD, CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT};
use super::Empty;
impl Empty for COORD {
fn empty() -> COORD {
COORD { X: 0, Y: 0 }
}
}
impl Empty for SMALL_RECT {
fn empty() -> SMALL_RECT {
SMALL_RECT {
Top: 0,
Right: 0,
Bottom: 0,
Left: 0,
}
}
}
impl Empty for CONSOLE_SCREEN_BUFFER_INFO {
fn empty() -> CONSOLE_SCREEN_BUFFER_INFO {
CONSOLE_SCREEN_BUFFER_INFO {
dwSize: COORD::empty(),
dwCursorPosition: COORD::empty(),
wAttributes: 0,
srWindow: SMALL_RECT::empty(),
dwMaximumWindowSize: COORD::empty(),
}
}
}

View File

@ -1,14 +1,20 @@
#[macro_use] #[macro_use]
mod shared; mod shared;
mod kernel; mod kernel;
mod crossterm_state;
pub mod crossterm_cursor; pub mod crossterm_cursor;
pub mod crossterm_style; pub mod crossterm_style;
pub mod crossterm_terminal; pub mod crossterm_terminal;
use shared::traits::{Construct}; use shared::traits::{Construct};
pub use crossterm_state::{ Context};
#[cfg(windows)] #[cfg(windows)]
extern crate winapi; extern crate winapi;
#[cfg(unix)] #[cfg(unix)]
extern crate libc; extern crate libc;
#[cfg(unix)]
extern crate termios;
extern crate rand;

View File

@ -1,9 +1,51 @@
#[cfg(unix)] //! Some actions need to preformed platform independently.
use kernel::linux_kernel::terminal::terminal_size; //!
use Context;
use shared::traits::Construct;
#[cfg(windows)] #[cfg(windows)]
use kernel::windows_kernel::terminal::terminal_size; use kernel::windows_kernel::terminal::terminal_size;
#[cfg(unix)]
use kernel::unix_kernel::terminal::terminal_size;
pub fn resize_terminal() -> (u16,u16) #[cfg(windows)]
use kernel::windows_kernel::cursor::pos;
#[cfg(unix)]
use kernel::unix_kernel::terminal::pos;
pub fn get_terminal_size() -> (u16, u16)
{ {
terminal_size() terminal_size()
} }
pub fn get_cursor_position() -> (u16,u16)
{
pos()
}
/// Get the module specific implementation based on the current platform
pub fn get_module<T>(winapi_impl: T, unix_impl: T, context: &mut Context) -> Option<T>
{
let mut term: Option<T> = None;
let mut does_support = true;
if cfg!(target_os = "windows") {
#[cfg(windows)]
use kernel::windows_kernel::ansi_support::try_enable_ansi_support;
// Try to enable ansi on windows if not than use WINAPI.
does_support = try_enable_ansi_support(context);
if !does_support
{
term = Some(winapi_impl);
}
}
if does_support
{
term = Some(unix_impl);
}
term
}

View File

@ -10,3 +10,4 @@ pub trait Construct {
pub trait Empty { pub trait Empty {
fn empty() -> Self; fn empty() -> Self;
} }