diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index f693957..2fec012 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -2,27 +2,47 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
+
-
+
+
+
-
-
+
+
+
+
+
+
+
+
@@ -33,64 +53,21 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -99,50 +76,84 @@
-
-
-
-
-
-
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
+
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
@@ -158,42 +169,43 @@
- set
- fsr_window
- resizeBuffer
- set_console_text_attribute
- set(
- kern
- pos
- csbi
- kernel
- success
- count
- xpos
- handle::
- wincon
- CONSOLE_SCREEN_BUFFER_INFO
- safe_position
- init
- cursor::get()
- get()
- get
- output_handle
- UnixTerminal
- size
- DISABLE_NE
- dwInMode
- FillConsoleOutputCharacter
- println
- terminal
- term
- AnsiColor
+ INVALID_HANDLE_VALUE
+ PCHARINFO
+ NULL
+ ToMainscre
+ main_screen_handle
+ colored_terminal
+ clear_entire
+ mainscreen_handle
+ outout
+ handle
+ <'a>
+ unw
+ does
+ mut context
+ clear_en
+ clear
+ singleton
+ IState
+ Context
+ self.changed_states
+ context
+ undo
+ ICommand
+ empty
+ execute
+ register
+ ScreenContext
+ tomain
+ State
+ write
crossterm_cursor
D:\Windows\GIT\crossterm
+ D:\Windows\GIT\crossterm\src
@@ -207,45 +219,57 @@
@@ -256,10 +280,26 @@
DEFINITION_ORDER
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Android
+
+
+
+
@@ -276,6 +316,8 @@
+
+
@@ -317,6 +359,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -343,6 +398,13 @@
+
+
+
+
+
+
+
@@ -354,7 +416,7 @@
-
+
@@ -373,18 +435,23 @@
-
-
-
+
-
+
+
+
+
+
+
+
+
@@ -513,12 +580,45 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -526,32 +626,61 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -564,99 +693,80 @@
-
+
-
-
+
+
-
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -667,286 +777,285 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
-
+
-
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
-
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Cargo.toml b/Cargo.toml
index daac209..7396eaf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,14 +10,21 @@ keywords = ["console", "color", "cursor", "terminal", "cli"]
exclude = ["target", "Cargo.lock"]
[dependencies]
+rand = "0.4.2"
+
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi"] }
+
[target.'cfg(unix)'.dependencies]
libc = "0.2"
+termios = "0.3.0"
+
+
[[bin]]
-name = "example_bin"
-path = "./examples/bin.rs"
+name = "a"
+path = "examples//bin.rs"
+
diff --git a/examples/bin.rs b/examples/bin.rs
index 65ea4f0..eeebe6b 100644
--- a/examples/bin.rs
+++ b/examples/bin.rs
@@ -23,9 +23,35 @@ use self::crossterm::crossterm_terminal;
pub mod color;
pub mod cursor;
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() {
- color::paint_background();
- color::paint_foreground();
- color::paint_foreground_and_background();
-}
+ let mut context = Context::new();
+//
+ 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");
+ }
+}
\ No newline at end of file
diff --git a/src/Terminal.rs b/src/Terminal.rs
new file mode 100644
index 0000000..56e14d0
--- /dev/null
+++ b/src/Terminal.rs
@@ -0,0 +1,6 @@
+pub struct Crossterm;
+
+impl Crossterm
+{
+ fn
+}
\ No newline at end of file
diff --git a/src/crossterm_cursor/ansi_cursor.rs b/src/crossterm_cursor/ansi_cursor.rs
index 2ce1205..18b5aa1 100644
--- a/src/crossterm_cursor/ansi_cursor.rs
+++ b/src/crossterm_cursor/ansi_cursor.rs
@@ -3,6 +3,7 @@ use std::io::Write;
use Construct;
use super::base_cursor::ITerminalCursor;
+use shared::functions;
/// This struct is an ansi implementation for cursor related actions.
pub struct AnsiCursor;
@@ -20,8 +21,8 @@ impl ITerminalCursor for AnsiCursor {
write!(&mut some_writer, csi!("{};{}H"), y + 1, x +1);
}
- fn pos(&self) -> (i16, i16) {
- (0, 0)
+ fn pos(&self) -> (u16, u16) {
+ functions::get_cursor_position()
}
fn move_up(&self, count: u16) {
diff --git a/src/crossterm_cursor/base_cursor.rs b/src/crossterm_cursor/base_cursor.rs
index 68c8a24..9bbddb3 100644
--- a/src/crossterm_cursor/base_cursor.rs
+++ b/src/crossterm_cursor/base_cursor.rs
@@ -11,7 +11,7 @@ pub trait ITerminalCursor {
/// Goto some location (x,y) in the terminal.
fn goto(&self, x: u16, y: u16);
/// 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
fn move_up(&self, count: u16);
/// Move the cursor `n` times to the right.
diff --git a/src/crossterm_cursor/cursor.rs b/src/crossterm_cursor/cursor.rs
index 73defdb..df1cb04 100644
--- a/src/crossterm_cursor/cursor.rs
+++ b/src/crossterm_cursor/cursor.rs
@@ -1,43 +1,33 @@
//! With this module you can perform actions that are cursor related.
//! Like changing and displaying the position of the cursor in terminal.
-
+//!
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::AnsiCursor;
use super::WinApiCursor;
/// Struct that stores an specific platform implementation for cursor related actions.
pub struct TerminalCursor {
terminal_cursor: Option>,
+ context: Context
}
-
-impl TerminalCursor {
+impl TerminalCursor
+ {
/// Create new cursor instance whereon cursor related actions can be performed.
pub fn new() -> TerminalCursor {
- let mut cursor: Option> = None;
+ let mut context = Context::new();
- let mut does_support = true;
- if cfg!(target_os = "windows") {
- #[cfg(windows)]
- use kernel::windows_kernel::kernel::try_enable_ansi_support;
+ #[cfg(target_os = "windows")]
+ let cursor = get_module::>(WinApiCursor::new(), AnsiCursor::new(), &mut context);
- does_support = try_enable_ansi_support();
- // this returns an bool if the current windows console supports ansi.
- if !does_support
- {
- cursor = Some(WinApiCursor::new());
- }
- }
+ #[cfg(not(target_os = "windows"))]
+ let cursor = Some(AnsiCursor::new());
- if does_support
- {
- cursor = Some(AnsiCursor::new());
- }
-
- TerminalCursor { terminal_cursor: cursor }
+ TerminalCursor { terminal_cursor: cursor, context: context }
}
/// Goto some position (x,y) in the terminal.
@@ -74,7 +64,7 @@ impl TerminalCursor {
/// 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 {
terminal_cursor.pos()
} else {
diff --git a/src/crossterm_cursor/winapi_cursor.rs b/src/crossterm_cursor/winapi_cursor.rs
index 80039b1..4cd77ea 100644
--- a/src/crossterm_cursor/winapi_cursor.rs
+++ b/src/crossterm_cursor/winapi_cursor.rs
@@ -1,6 +1,7 @@
use Construct;
-use kernel::windows_kernel::cursor;
use super::base_cursor::ITerminalCursor;
+use kernel::windows_kernel::{kernel, cursor};
+
/// This struct is an windows implementation for cursor related actions.
pub struct WinApiCursor;
@@ -12,38 +13,40 @@ impl Construct for WinApiCursor {
}
impl ITerminalCursor for WinApiCursor {
+
+ /// Set the current cursor position to X and Y
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()
}
fn move_up(&self, count: u16) {
- let (xpos,ypos) = cursor::pos();
- cursor::set(xpos, ypos - count as i16);
+ let (xpos,ypos) = self.pos();
+ self.goto(xpos, ypos - count);
}
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) {
- 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) {
- 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)
{
cursor::save_cursor_pos();
diff --git a/src/crossterm_state/commands/mod.rs b/src/crossterm_state/commands/mod.rs
new file mode 100644
index 0000000..00921f4
--- /dev/null
+++ b/src/crossterm_state/commands/mod.rs
@@ -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 where Self: Sized;
+ fn execute(&mut self) -> bool;
+ fn undo(&mut self) -> bool;
+}
+
+pub trait IContextCommand
+{
+ fn new(context: &mut Context) -> (Box, 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::(), rng.gen::())
+// }
+ rand::random::()
+}
\ No newline at end of file
diff --git a/src/crossterm_state/commands/shared_commands.rs b/src/crossterm_state/commands/shared_commands.rs
new file mode 100644
index 0000000..5b4f451
--- /dev/null
+++ b/src/crossterm_state/commands/shared_commands.rs
@@ -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 {
+// 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
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/crossterm_state/commands/unix_command.rs b/src/crossterm_state/commands/unix_command.rs
new file mode 100644
index 0000000..3d1cffb
--- /dev/null
+++ b/src/crossterm_state/commands/unix_command.rs
@@ -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) {
+// 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,
+ key: i16
+}
+
+impl IContextCommand for EnableRawModeCommand
+{
+ fn new(context: &Context) -> (Box, 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
+ }
+ }
+}
diff --git a/src/crossterm_state/commands/win_commands.rs b/src/crossterm_state/commands/win_commands.rs
new file mode 100644
index 0000000..7c62f28
--- /dev/null
+++ b/src/crossterm_state/commands/win_commands.rs
@@ -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, 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, 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
+ {
+ 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
+ }
+}
diff --git a/src/crossterm_state/context.rs b/src/crossterm_state/context.rs
new file mode 100644
index 0000000..222986b
--- /dev/null
+++ b/src/crossterm_state/context.rs
@@ -0,0 +1,72 @@
+use super::commands::{IContextCommand, ICommand};
+use std::ops::Drop;
+use std::collections::HashMap;
+
+pub struct Context
+{
+ changed_states: HashMap>,
+}
+
+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, 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()
+// }
+//}
+
+
+
diff --git a/src/crossterm_state/mod.rs b/src/crossterm_state/mod.rs
new file mode 100644
index 0000000..f883952
--- /dev/null
+++ b/src/crossterm_state/mod.rs
@@ -0,0 +1,4 @@
+mod context;
+pub mod commands;
+
+pub use self::context::{Context};
\ No newline at end of file
diff --git a/src/crossterm_style/color/ansi_color.rs b/src/crossterm_style/color/ansi_color.rs
index bda84a1..8efd0a4 100644
--- a/src/crossterm_style/color/ansi_color.rs
+++ b/src/crossterm_style/color/ansi_color.rs
@@ -46,7 +46,8 @@ impl ITerminalColor for AnsiColor {
ansi_value.push_str("48;")
},
}
-
+
+ #[cfg(unix)]
let rgb_val: String;
let color_val = match color {
diff --git a/src/crossterm_style/color/color.rs b/src/crossterm_style/color/color.rs
index ca455af..c1ce7be 100644
--- a/src/crossterm_style/color/color.rs
+++ b/src/crossterm_style/color/color.rs
@@ -1,12 +1,14 @@
//! With this module you can perform actions that are color related.
//! Like styling the font, foreground color and background color.
+use std::ops::Drop;
use std::fmt;
use std::io;
-use Construct;
+use {Construct, Context };
use crossterm_style::{ObjectStyle, StyledObject};
use super::base_color::ITerminalColor;
+use shared::functions::get_module;
use super::super::Color;
use super::AnsiColor;
@@ -15,35 +17,24 @@ use super::WinApiColor;
/// Struct that stores an specific platform implementation for color related actions.
pub struct TerminalColor {
terminal_color: Option>,
+ context: Context
}
impl TerminalColor {
/// Create new instance whereon color related actions can be performed.
pub fn new() -> TerminalColor {
- let mut color: Option> = None;
+ let mut context = Context::new();
- let mut does_support = true;
- if cfg!(target_os = "windows") {
- #[cfg(windows)]
- use kernel::windows_kernel::kernel::try_enable_ansi_support;
+ #[cfg(target_os = "windows")]
+ let color = get_module::>(WinApiColor::new(), AnsiColor::new(), &mut context);
- does_support = try_enable_ansi_support();
- // this returns an bool if the current windows console supports ansi.
- if !does_support
- {
- color = Some(WinApiColor::new());
- }
- }
+ #[cfg(not(target_os = "windows"))]
+ let color = Some(AnsiColor::new());
- if does_support
- {
- color = Some(AnsiColor::new());
- }
-
- TerminalColor { terminal_color: color }
+ TerminalColor { terminal_color: color, context: context}
}
- /// Set the forground color to the given color.
+ /// Set the foreground color to the given color.
///
/// #Example
///
diff --git a/src/crossterm_style/color/winapi_color.rs b/src/crossterm_style/color/winapi_color.rs
index 42b9717..3c2f399 100644
--- a/src/crossterm_style/color/winapi_color.rs
+++ b/src/crossterm_style/color/winapi_color.rs
@@ -1,7 +1,9 @@
use Construct;
use super::super::{ColorType, Color};
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.
#[derive(Debug)]
@@ -12,27 +14,120 @@ pub struct WinApiColor {
impl Construct for WinApiColor {
fn new() -> Box {
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 {
+
+ /// This will set the foreground color by the given winapi color.
fn set_fg(&self, fg_color: Color) {
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::().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) {
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::().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) {
- 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 {
- 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()
}
}
diff --git a/src/crossterm_style/styles/objectstyle.rs b/src/crossterm_style/styles/objectstyle.rs
index e43d82b..a0f14e1 100644
--- a/src/crossterm_style/styles/objectstyle.rs
+++ b/src/crossterm_style/styles/objectstyle.rs
@@ -1,6 +1,7 @@
use crossterm_style::{Color, StyledObject};
use std::fmt::Display;
+#[cfg(unix)]
use super::super::Attribute;
/// Struct that contains the style properties that can be applied to an displayable object.
diff --git a/src/crossterm_style/styles/styledobject.rs b/src/crossterm_style/styles/styledobject.rs
index 343863a..e685c19 100644
--- a/src/crossterm_style/styles/styledobject.rs
+++ b/src/crossterm_style/styles/styledobject.rs
@@ -2,6 +2,7 @@ use std;
use std::fmt;
use std::io::Write;
+#[cfg(unix)]
use super::super::Attribute;
use crossterm_style::{Color, ObjectStyle};
diff --git a/src/crossterm_terminal/ansi_terminal.rs b/src/crossterm_terminal/ansi_terminal.rs
index d704fe1..b734985 100644
--- a/src/crossterm_terminal/ansi_terminal.rs
+++ b/src/crossterm_terminal/ansi_terminal.rs
@@ -3,7 +3,8 @@ use std::io::Write;
use Construct;
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.
pub struct AnsiTerminal ;
@@ -37,7 +38,7 @@ impl ITerminal for AnsiTerminal {
}
fn terminal_size(&self) -> (u16, u16) {
- resize_terminal()
+ get_terminal_size()
}
fn scroll_up(&self, count: i16) {
diff --git a/src/crossterm_terminal/base_terminal.rs b/src/crossterm_terminal/base_terminal.rs
index ff06f69..f2af6f9 100644
--- a/src/crossterm_terminal/base_terminal.rs
+++ b/src/crossterm_terminal/base_terminal.rs
@@ -7,7 +7,7 @@ pub enum ClearType {
UntilNewLine,
}
-pub trait ITerminal {
+pub trait ITerminal{
/// Clear the current cursor by specifying the clear type
fn clear(&self, clear_type: ClearType);
/// Get the terminal size (x,y)
diff --git a/src/crossterm_terminal/mod.rs b/src/crossterm_terminal/mod.rs
index 3a3c0ca..ac942a8 100644
--- a/src/crossterm_terminal/mod.rs
+++ b/src/crossterm_terminal/mod.rs
@@ -1,6 +1,9 @@
+mod raw_terminal;
mod base_terminal;
mod terminal;
+pub mod screen;
+
mod ansi_terminal;
mod winapi_terminal;
@@ -8,4 +11,5 @@ use self::ansi_terminal::AnsiTerminal;
use self::winapi_terminal::WinApiTerminal;
pub use self::base_terminal::ClearType;
-pub use self::terminal::{ Terminal, terminal };
\ No newline at end of file
+pub use self::terminal::{ Terminal, terminal};
+pub use self::raw_terminal::{RawTerminal, IntoRawMode};
\ No newline at end of file
diff --git a/src/crossterm_terminal/raw_terminal.rs b/src/crossterm_terminal/raw_terminal.rs
new file mode 100644
index 0000000..0e077c2
--- /dev/null
+++ b/src/crossterm_terminal/raw_terminal.rs
@@ -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>;
+}
+
+impl IntoRawMode for W
+{
+ fn into_raw_mode<'a>(self, context: &'a mut Context) -> io::Result>
+ {
+ 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 {
+ self.output.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.output.flush()
+ }
+}
\ No newline at end of file
diff --git a/src/crossterm_terminal/screen/mod.rs b/src/crossterm_terminal/screen/mod.rs
new file mode 100644
index 0000000..ff45c57
--- /dev/null
+++ b/src/crossterm_terminal/screen/mod.rs
@@ -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 {
+ /// The output target.
+ output: W,
+}
+
+impl AlternateScreen< W> {
+ pub fn from(mut output: W) -> Self {
+ write!(output, "{}", ToAlternateScreen);
+ AlternateScreen { output: output }
+ }
+}
+
+impl ops::Deref for AlternateScreen {
+ type Target = W;
+
+ fn deref(&self) -> &W {
+ &self.output
+ }
+}
+
+impl ops::DerefMut for AlternateScreen {
+ fn deref_mut(&mut self) -> &mut W {
+ &mut self.output
+ }
+}
+
+impl Write for AlternateScreen {
+ fn write(&mut self, buf: &[u8]) -> io::Result {
+ self.output.write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.output.flush()
+ }
+}
+
+impl Drop for AlternateScreen
+{
+ fn drop(&mut self)
+ {
+ write!(self, "{}", ToMainScreen).expect("switch to main screen");
+ }
+}
+
+fn get_to_alternate_screen_command() -> Box
+{
+// let mut does_support = true;
+ let mut command: Option> = 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()
+}
\ No newline at end of file
diff --git a/src/crossterm_terminal/terminal.rs b/src/crossterm_terminal/terminal.rs
index 10e32b5..e3d4227 100644
--- a/src/crossterm_terminal/terminal.rs
+++ b/src/crossterm_terminal/terminal.rs
@@ -1,9 +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 std::ops::Drop;
-use Construct;
+use {Construct, Context};
use super::base_terminal::{ClearType, ITerminal};
+use shared::functions::get_module;
use super::AnsiTerminal;
use super::WinApiTerminal;
@@ -11,32 +13,21 @@ use super::WinApiTerminal;
/// Struct that stores an specific platform implementation for terminal related actions.
pub struct Terminal {
terminal: Option>,
+ context: Context
}
-impl Terminal {
+impl Terminal {
/// Create new terminal instance whereon terminal related actions can be performed.
pub fn new() -> Terminal {
- let mut term: Option> = None;
- let mut does_support = true;
- if cfg!(target_os = "windows") {
- use kernel::windows_kernel::kernel::try_enable_ansi_support;
+ let mut context = Context::new();
+ #[cfg(target_os = "windows")]
+ let terminal = get_module::>(WinApiTerminal::new(), AnsiTerminal::new(), &mut context);
- does_support = try_enable_ansi_support();
- // this returns an bool if the current windows console supports ansi.
- if !does_support
- {
- term = Some(WinApiTerminal::new());
- }
- }
+ #[cfg(not(target_os = "windows"))]
+ let terminal = Some(AnsiTerminal::new());
- if does_support
- {
- println!("This console does support ansi");
- term = Some(AnsiTerminal::new());
- }
-
- Terminal { terminal: term }
+ Terminal { terminal: terminal, context: context }
}
/// Clear the current cursor by specifying the clear type
@@ -173,6 +164,7 @@ impl Terminal {
///
/// ```
///
-pub fn terminal() -> Box {
+pub fn terminal() -> Box
+{
Box::from(Terminal::new())
}
diff --git a/src/crossterm_terminal/winapi_terminal.rs b/src/crossterm_terminal/winapi_terminal.rs
index 34eca28..3f8420e 100644
--- a/src/crossterm_terminal/winapi_terminal.rs
+++ b/src/crossterm_terminal/winapi_terminal.rs
@@ -1,7 +1,8 @@
-use Construct;
+use {Construct};
+use crossterm_cursor::cursor;
use super::base_terminal::{ClearType, ITerminal};
-
-use kernel::windows_kernel::terminal;
+use winapi::um::wincon::{SMALL_RECT, COORD, CONSOLE_SCREEN_BUFFER_INFO,};
+use kernel::windows_kernel::{kernel, terminal};
/// This struct is an windows implementation for terminal related actions.
pub struct WinApiTerminal;
@@ -13,29 +14,233 @@ impl Construct for WinApiTerminal {
}
impl ITerminal for WinApiTerminal {
+ /// Clear the screen to the given cleartype.
fn clear(&self, clear_type: ClearType) {
- println! ("Windows!!!");
+ let csbi = kernel::get_console_screen_buffer_info();
+ let pos = cursor().pos();
+
match clear_type
{
- ClearType::All => terminal::clear_entire_screen(),
- ClearType::FromCursorDown => terminal::clear_after_cursor(),
- ClearType::FromCursorUp => terminal::clear_before_cursor(),
- ClearType::CurrentLine => terminal::clear_current_line(),
- ClearType::UntilNewLine => terminal::clear_until_line(),
+ ClearType::All => clear_entire_screen(csbi),
+ ClearType::FromCursorDown => clear_after_cursor(pos,csbi),
+ ClearType::FromCursorUp => clear_before_cursor(pos, csbi),
+ ClearType::CurrentLine => clear_current_line(pos, csbi),
+ ClearType::UntilNewLine => clear_until_line(pos, csbi),
};
}
+ /// Get the terminal size
fn terminal_size(&self) -> (u16, u16) {
- terminal::terminal_size()
+ terminal::terminal_size()
}
+ /// Scroll up n` rows
fn scroll_up(&self, count: i16) {
// yet to be inplemented
}
+ /// Scroll down `n` rows
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");
+ }
+}
\ No newline at end of file
diff --git a/src/kernel/linux_kernel/terminal.rs b/src/kernel/linux_kernel/terminal.rs
deleted file mode 100644
index 14e9a56..0000000
--- a/src/kernel/linux_kernel/terminal.rs
+++ /dev/null
@@ -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)
- }
-}
\ No newline at end of file
diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs
index b90161f..84589de 100644
--- a/src/kernel/mod.rs
+++ b/src/kernel/mod.rs
@@ -1,4 +1,4 @@
#[cfg(unix)]
-pub mod linux_kernel;
+pub mod unix_kernel;
#[cfg(windows)]
pub mod windows_kernel;
\ No newline at end of file
diff --git a/src/kernel/linux_kernel/mod.rs b/src/kernel/unix_kernel/mod.rs
similarity index 94%
rename from src/kernel/linux_kernel/mod.rs
rename to src/kernel/unix_kernel/mod.rs
index a566381..79479c3 100644
--- a/src/kernel/linux_kernel/mod.rs
+++ b/src/kernel/unix_kernel/mod.rs
@@ -1 +1,2 @@
pub mod terminal;
+
diff --git a/src/kernel/unix_kernel/terminal.rs b/src/kernel/unix_kernel/terminal.rs
new file mode 100644
index 0000000..6c7824b
--- /dev/null
+++ b/src/kernel/unix_kernel/terminal.rs
@@ -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
+{
+ 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)
+ }
+}
\ No newline at end of file
diff --git a/src/kernel/windows_kernel/ansi_support.rs b/src/kernel/windows_kernel/ansi_support.rs
new file mode 100644
index 0000000..409f895
--- /dev/null
+++ b/src/kernel/windows_kernel/ansi_support.rs
@@ -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 = None;
+static mut DOES_WINDOWS_SUPPORT_ANSI: Option = 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;
+ }
+}
\ No newline at end of file
diff --git a/src/kernel/windows_kernel/color.rs b/src/kernel/windows_kernel/color.rs
deleted file mode 100644
index 018b7ad..0000000
--- a/src/kernel/windows_kernel/color.rs
+++ /dev/null
@@ -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
-}
diff --git a/src/kernel/windows_kernel/cursor.rs b/src/kernel/windows_kernel/cursor.rs
index 0587f59..0820e7c 100644
--- a/src/kernel/windows_kernel/cursor.rs
+++ b/src/kernel/windows_kernel/cursor.rs
@@ -1,36 +1,29 @@
use super::kernel;
+use crossterm_cursor::cursor;
/// This stores the cursor pos, at program level. So it can be recalled later.
-static mut SAVED_CURSOR_POS:(i16,i16) = (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 );
-
-}
+static mut SAVED_CURSOR_POS:(u16,u16) = (0,0);
/// Reset to saved cursor position
pub fn reset_to_saved_position()
{
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.
pub fn save_cursor_pos()
{
- let position = pos();
+ let position = cursor().pos();
unsafe {
SAVED_CURSOR_POS = (position.0, position.1);
}
}
-/// Get current cursor position (X,Y)
-pub fn pos() -> (i16,i16)
+pub fn pos() -> (u16,u16)
{
let csbi = kernel::get_console_screen_buffer_info();
- ( csbi.dwCursorPosition.X , csbi.dwCursorPosition.Y )
-}
\ No newline at end of file
+ ( csbi.dwCursorPosition.X as u16, csbi.dwCursorPosition.Y as u16)
+}
diff --git a/src/kernel/windows_kernel/kernel.rs b/src/kernel/windows_kernel/kernel.rs
index 4c1c649..5bd9c56 100644
--- a/src/kernel/windows_kernel/kernel.rs
+++ b/src/kernel/windows_kernel/kernel.rs
@@ -1,13 +1,15 @@
-use winapi::shared::minwindef::DWORD;
use winapi::um::winnt::HANDLE;
use winapi::um::winbase::{STD_OUTPUT_HANDLE, STD_INPUT_HANDLE };
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::{GetStdHandle};
-use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode};
-use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize,
+use winapi::um::consoleapi::{SetConsoleMode,GetConsoleMode, };
+
+use winapi::um::wincon;
+use winapi::shared::minwindef::{TRUE};
+use winapi::um::wincon::{ SetConsoleWindowInfo, SetConsoleCursorPosition, SetConsoleTextAttribute, SetConsoleScreenBufferSize, CreateConsoleScreenBuffer,SetConsoleActiveScreenBuffer,
GetLargestConsoleWindowSize, GetConsoleScreenBufferInfo,
- FillConsoleOutputCharacterA, FillConsoleOutputAttribute, ENABLE_VIRTUAL_TERMINAL_PROCESSING,ENABLE_VIRTUAL_TERMINAL_INPUT,
- CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, DISABLE_NEWLINE_AUTO_RETURN
+ FillConsoleOutputCharacterA, FillConsoleOutputAttribute,
+ CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT, COORD, CHAR_INFO, PSMALL_RECT
};
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 {
let output_handle = get_output_handle();
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
@@ -77,26 +78,6 @@ 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();
@@ -220,6 +201,90 @@ pub fn fill_console_output_attribute(cells_written: &mut u32, start_location: CO
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::() 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
fn is_true(value: i32) -> bool
{
diff --git a/src/kernel/windows_kernel/mod.rs b/src/kernel/windows_kernel/mod.rs
index a5fe20e..a3ae2a3 100644
--- a/src/kernel/windows_kernel/mod.rs
+++ b/src/kernel/windows_kernel/mod.rs
@@ -1,7 +1,38 @@
-pub mod cursor;
-pub mod color;
-pub mod kernel;
-pub mod terminal;
-mod winapi_extentions;
+extern crate winapi;
+use winapi::um::wincon::{COORD, CONSOLE_SCREEN_BUFFER_INFO, SMALL_RECT};
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(),
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/kernel/windows_kernel/terminal.rs b/src/kernel/windows_kernel/terminal.rs
index f8a36b4..2771f44 100644
--- a/src/kernel/windows_kernel/terminal.rs
+++ b/src/kernel/windows_kernel/terminal.rs
@@ -1,221 +1,8 @@
-use super::{cursor, kernel};
-use winapi::um::wincon::{SMALL_RECT, COORD};
-
-/// Get the terminal size (y,x)
+/// Get the terminal size
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.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");
- }
-}
+}
\ No newline at end of file
diff --git a/src/kernel/windows_kernel/winapi_extentions.rs b/src/kernel/windows_kernel/winapi_extentions.rs
deleted file mode 100644
index 9cf1000..0000000
--- a/src/kernel/windows_kernel/winapi_extentions.rs
+++ /dev/null
@@ -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(),
- }
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 13f3459..538c952 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,14 +1,20 @@
#[macro_use]
mod shared;
mod kernel;
+mod crossterm_state;
pub mod crossterm_cursor;
pub mod crossterm_style;
pub mod crossterm_terminal;
use shared::traits::{Construct};
+pub use crossterm_state::{ Context};
#[cfg(windows)]
extern crate winapi;
#[cfg(unix)]
-extern crate libc;
\ No newline at end of file
+extern crate libc;
+#[cfg(unix)]
+extern crate termios;
+
+extern crate rand;
diff --git a/src/shared/functions.rs b/src/shared/functions.rs
index 8da81f7..cff5c25 100644
--- a/src/shared/functions.rs
+++ b/src/shared/functions.rs
@@ -1,9 +1,51 @@
-#[cfg(unix)]
-use kernel::linux_kernel::terminal::terminal_size;
+//! Some actions need to preformed platform independently.
+//!
+use Context;
+use shared::traits::Construct;
#[cfg(windows)]
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()
+}
+
+pub fn get_cursor_position() -> (u16,u16)
+{
+ pos()
+}
+
+/// Get the module specific implementation based on the current platform
+pub fn get_module(winapi_impl: T, unix_impl: T, context: &mut Context) -> Option
+{
+ let mut term: Option = 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
}
\ No newline at end of file
diff --git a/src/shared/mod.rs b/src/shared/mod.rs
index a507168..e9ee529 100644
--- a/src/shared/mod.rs
+++ b/src/shared/mod.rs
@@ -1,4 +1,4 @@
#[macro_use]
pub mod macros;
pub mod traits;
-pub mod functions;
+pub mod functions;
\ No newline at end of file
diff --git a/src/shared/traits.rs b/src/shared/traits.rs
index edaeef6..44437b0 100644
--- a/src/shared/traits.rs
+++ b/src/shared/traits.rs
@@ -10,3 +10,4 @@ pub trait Construct {
pub trait Empty {
fn empty() -> Self;
}
+