}
This commit is contained in:
parent
215d0cfa83
commit
524955f8c0
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
1015
.idea/workspace.xml
1015
.idea/workspace.xml
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
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"
|
||||
|
||||
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
6
src/Terminal.rs
Normal file
6
src/Terminal.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub struct Crossterm;
|
||||
|
||||
impl Crossterm
|
||||
{
|
||||
fn
|
||||
}
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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<Box<ITerminalCursor>>,
|
||||
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<Box<ITerminalCursor>> = 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::<Box<ITerminalCursor>>(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 {
|
||||
|
@ -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();
|
||||
|
45
src/crossterm_state/commands/mod.rs
Normal file
45
src/crossterm_state/commands/mod.rs
Normal 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>()
|
||||
}
|
36
src/crossterm_state/commands/shared_commands.rs
Normal file
36
src/crossterm_state/commands/shared_commands.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
95
src/crossterm_state/commands/unix_command.rs
Normal file
95
src/crossterm_state/commands/unix_command.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
220
src/crossterm_state/commands/win_commands.rs
Normal file
220
src/crossterm_state/commands/win_commands.rs
Normal 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
|
||||
}
|
||||
}
|
72
src/crossterm_state/context.rs
Normal file
72
src/crossterm_state/context.rs
Normal 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()
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
4
src/crossterm_state/mod.rs
Normal file
4
src/crossterm_state/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod context;
|
||||
pub mod commands;
|
||||
|
||||
pub use self::context::{Context};
|
@ -46,7 +46,8 @@ impl ITerminalColor for AnsiColor {
|
||||
ansi_value.push_str("48;")
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
#[cfg(unix)]
|
||||
let rgb_val: String;
|
||||
|
||||
let color_val = match color {
|
||||
|
@ -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<Box<ITerminalColor>>,
|
||||
context: Context
|
||||
}
|
||||
|
||||
impl TerminalColor {
|
||||
/// Create new instance whereon color related actions can be performed.
|
||||
pub fn new() -> TerminalColor {
|
||||
let mut color: Option<Box<ITerminalColor>> = 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::<Box<ITerminalColor>>(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
|
||||
///
|
||||
|
@ -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<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 {
|
||||
|
||||
/// 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::<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) {
|
||||
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) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -2,6 +2,7 @@ use std;
|
||||
use std::fmt;
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(unix)]
|
||||
use super::super::Attribute;
|
||||
|
||||
use crossterm_style::{Color, ObjectStyle};
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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 };
|
||||
pub use self::terminal::{ Terminal, terminal};
|
||||
pub use self::raw_terminal::{RawTerminal, IntoRawMode};
|
55
src/crossterm_terminal/raw_terminal.rs
Normal file
55
src/crossterm_terminal/raw_terminal.rs
Normal 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()
|
||||
}
|
||||
}
|
118
src/crossterm_terminal/screen/mod.rs
Normal file
118
src/crossterm_terminal/screen/mod.rs
Normal 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()
|
||||
}
|
@ -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<Box<ITerminal>>,
|
||||
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<Box<ITerminal>> = 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::<Box<ITerminal>>(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<Terminal> {
|
||||
pub fn terminal() -> Box<Terminal>
|
||||
{
|
||||
Box::from(Terminal::new())
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#[cfg(unix)]
|
||||
pub mod linux_kernel;
|
||||
pub mod unix_kernel;
|
||||
#[cfg(windows)]
|
||||
pub mod windows_kernel;
|
@ -1 +1,2 @@
|
||||
pub mod terminal;
|
||||
|
115
src/kernel/unix_kernel/terminal.rs
Normal file
115
src/kernel/unix_kernel/terminal.rs
Normal 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)
|
||||
}
|
||||
}
|
69
src/kernel/windows_kernel/ansi_support.rs
Normal file
69
src/kernel/windows_kernel/ansi_support.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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 )
|
||||
}
|
||||
( csbi.dwCursorPosition.X as u16, csbi.dwCursorPosition.Y as u16)
|
||||
}
|
||||
|
@ -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::<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
|
||||
fn is_true(value: i32) -> bool
|
||||
{
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
extern crate libc;
|
||||
#[cfg(unix)]
|
||||
extern crate termios;
|
||||
|
||||
extern crate rand;
|
||||
|
@ -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<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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod traits;
|
||||
pub mod functions;
|
||||
pub mod functions;
|
@ -10,3 +10,4 @@ pub trait Construct {
|
||||
pub trait Empty {
|
||||
fn empty() -> Self;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user