Refactored winapi and moved code to (crossterm_winapi) (#67)

* Refactored winapi and moved some code to its own crate (crossterm_winapi).
This commit is contained in:
Timon 2018-12-31 10:55:48 -08:00 committed by GitHub
parent 14bd60af78
commit ff9b5d9a39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 470 additions and 865 deletions

View File

@ -10,9 +10,9 @@ keywords = ["console", "color", "cursor", "input", "terminal"]
exclude = ["target", "Cargo.lock"] exclude = ["target", "Cargo.lock"]
readme = "README.md" readme = "README.md"
[dependencies]
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.5", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi","errhandlingapi"] } winapi = { version = "0.3.5", features = ["winbase","winuser","consoleapi","processenv","wincon", "handleapi","errhandlingapi"] }
crossterm_winapi = { version = "0.0.0", git="https://github.com/TimonPost/crossterm_winapi" }
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
libc = "0.2.43" libc = "0.2.43"

View File

@ -0,0 +1,2 @@
Power shell does not interpreter 'DarkYellow' and is instead using gray Winapi.
Arc and manjaro konsole are not seeming to resize the terminal instead they are resizeing the buffer.

View File

@ -10,7 +10,7 @@ use self::crossterm::{terminal, Screen};
pub fn paint_foreground() { pub fn paint_foreground() {
// Create a styled object. // Create a styled object.
// Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum. // Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum.
let mut styledobject = style("Red foreground").with(Color::Red); let styledobject = style("Red foreground").with(Color::Red);
// Print the object to the given screen and. // Print the object to the given screen and.
println!("Colored text: {}", styledobject); println!("Colored text: {}", styledobject);
@ -26,7 +26,7 @@ pub fn paint_foreground() {
pub fn paint_background() { pub fn paint_background() {
// Create a styled object. // Create a styled object.
// Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum. // Call the method `with()` on the object given by `style()` and pass in any Color from the Color enum.
let mut styledobject = style("Red foreground").on(Color::Red); let styledobject = style("Red foreground").on(Color::Red);
// Print the object to the given screen and. // Print the object to the given screen and.
println!("Colored text: {}", styledobject); println!("Colored text: {}", styledobject);
@ -109,7 +109,10 @@ pub fn print_all_foreground_colors() {
); );
#[cfg(unix)] #[cfg(unix)]
println!("{}", style("RGB color (10,10,10) ").with(Color::AnsiValue(50))); println!(
"{}",
style("RGB color (10,10,10) ").with(Color::AnsiValue(50))
);
} }
/// Print all available foreground colors | demonstration. /// Print all available foreground colors | demonstration.

View File

@ -9,7 +9,7 @@ use self::crossterm::Screen;
/// Set the cursor to position X: 10, Y: 5 in the terminal. /// Set the cursor to position X: 10, Y: 5 in the terminal.
pub fn goto() { pub fn goto() {
// Get the cursor // Get the cursor
let mut cursor = cursor(); let cursor = cursor();
// Set the cursor to position X: 10, Y: 5 in the terminal // Set the cursor to position X: 10, Y: 5 in the terminal
cursor.goto(10, 5); cursor.goto(10, 5);
} }
@ -17,9 +17,11 @@ pub fn goto() {
/// get the cursor position /// get the cursor position
pub fn pos() { pub fn pos() {
// Get the cursor // Get the cursor
let mut cursor = cursor(); let cursor = cursor();
// get the cursor position. // get the cursor position.
let (x, y) = cursor.pos(); let (x, y) = cursor.pos();
println!("{} {}", x, y);
} }
/// Move the cursor 3 up | demonstration. /// Move the cursor 3 up | demonstration.
@ -81,7 +83,7 @@ pub fn move_left() {
/// Save and reset cursor position | demonstration.. /// Save and reset cursor position | demonstration..
pub fn safe_and_reset_position() { pub fn safe_and_reset_position() {
let mut cursor = cursor(); let cursor = cursor();
// Goto X: 5 Y: 5 // Goto X: 5 Y: 5
cursor.goto(5, 5); cursor.goto(5, 5);
@ -101,19 +103,19 @@ pub fn safe_and_reset_position() {
/// Hide cursor display | demonstration. /// Hide cursor display | demonstration.
pub fn hide_cursor() { pub fn hide_cursor() {
let mut cursor = cursor(); let cursor = cursor();
cursor.hide(); cursor.hide();
} }
/// Show cursor display | demonstration. /// Show cursor display | demonstration.
pub fn show_cursor() { pub fn show_cursor() {
let mut cursor = cursor(); let cursor = cursor();
cursor.show(); cursor.show();
} }
/// Show cursor display, only works on certain terminals.| demonstration /// Show cursor display, only works on certain terminals.| demonstration
pub fn blink_cursor() { pub fn blink_cursor() {
let mut cursor = cursor(); let cursor = cursor();
cursor.blink(false); cursor.blink(false);
cursor.blink(false); cursor.blink(false);
} }

View File

@ -8,10 +8,21 @@
extern crate crossterm; extern crate crossterm;
// modules that could be test // modules that could be test
//mod color; mod color;
//mod cursor; mod cursor;
//mod input; mod input;
//mod some_types; //mod some_types;
//mod terminal; mod terminal;
fn main() { } fn main() {
use input::keyboard::input;
// color::print_all_foreground_colors();
// color::print_all_background_colors();
use terminal::alternate_screen;
// color::print_all_background_colors();
// color::print_all_foreground_colors();
alternate_screen::print_wait_screen_on_alternate_window();
}

View File

@ -16,11 +16,11 @@ pub fn read_async_until() {
// init some modules we use for this demo // init some modules we use for this demo
let input = crossterm.input(); let input = crossterm.input();
let terminal = crossterm.terminal(); let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor(); let cursor = crossterm.cursor();
let mut stdin = input.read_until_async(b'\r').bytes(); let mut stdin = input.read_until_async(b'\r').bytes();
for i in 0..100 { for _i in 0..100 {
terminal.clear(ClearType::All); terminal.clear(ClearType::All);
cursor.goto(1, 1); cursor.goto(1, 1);
let a = stdin.next(); let a = stdin.next();
@ -68,7 +68,7 @@ pub fn read_async_demo() {
// init some modules we use for this demo // init some modules we use for this demo
let input = crossterm.input(); let input = crossterm.input();
let terminal = crossterm.terminal(); let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor(); let cursor = crossterm.cursor();
// this will setup the async reading. // this will setup the async reading.
let mut stdin = input.read_async().bytes(); let mut stdin = input.read_async().bytes();

View File

@ -1,6 +1,6 @@
extern crate crossterm; extern crate crossterm;
use self::crossterm::input::{input, TerminalInput, KeyEvent}; use self::crossterm::input::{input, KeyEvent, TerminalInput};
use self::crossterm::Screen; use self::crossterm::Screen;
pub fn read_char() { pub fn read_char() {

View File

@ -131,7 +131,7 @@ fn game_over_screen()
terminal.clear(ClearType::All); terminal.clear(ClearType::All);
println!("{}",messages::END_MESSAGE.join("\n\r")); println!("{}", crossterm.style(format!("{}",messages::END_MESSAGE.join("\n\r"))).with(Color::Red));
// cursor.goto() // cursor.goto()
cursor.show(); cursor.show();
} }

View File

@ -14,7 +14,7 @@ fn print_test_data() {
/// Clear all lines in terminal | demonstration /// Clear all lines in terminal | demonstration
pub fn clear_all_lines() { pub fn clear_all_lines() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -24,7 +24,7 @@ pub fn clear_all_lines() {
/// Clear all lines from cursor position X:4, Y:4 down | demonstration /// Clear all lines from cursor position X:4, Y:4 down | demonstration
pub fn clear_from_cursor_down() { pub fn clear_from_cursor_down() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -37,7 +37,7 @@ pub fn clear_from_cursor_down() {
/// Clear all lines from cursor position X:4, Y:4 up | demonstration /// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_from_cursor_up() { pub fn clear_from_cursor_up() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -50,12 +50,12 @@ pub fn clear_from_cursor_up() {
/// Clear all lines from cursor position X:4, Y:4 up | demonstration /// Clear all lines from cursor position X:4, Y:4 up | demonstration
pub fn clear_current_line() { pub fn clear_current_line() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
// Set terminal cursor position (see example for more info). // Set terminal cursor position (see example for more info).
cursor().goto(4, 4); cursor().goto(4, 3);
// Clear current line cells. // Clear current line cells.
terminal.clear(ClearType::CurrentLine); terminal.clear(ClearType::CurrentLine);
@ -63,7 +63,7 @@ pub fn clear_current_line() {
/// Clear all lines from cursor position X:4, Y:7 up | demonstration /// Clear all lines from cursor position X:4, Y:7 up | demonstration
pub fn clear_until_new_line() { pub fn clear_until_new_line() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -76,7 +76,7 @@ pub fn clear_until_new_line() {
/// Print the the current terminal size | demonstration. /// Print the the current terminal size | demonstration.
pub fn print_terminal_size() { pub fn print_terminal_size() {
let mut terminal = terminal(); let terminal = terminal();
// Get terminal size // Get terminal size
let (width, height) = terminal.terminal_size(); let (width, height) = terminal.terminal_size();
@ -87,14 +87,14 @@ pub fn print_terminal_size() {
/// Set the terminal size to width 10, height: 10 | demonstration. /// Set the terminal size to width 10, height: 10 | demonstration.
pub fn set_terminal_size() { pub fn set_terminal_size() {
let mut terminal = terminal(); let terminal = terminal();
terminal.set_size(10, 10); terminal.set_size(10, 10);
} }
/// Scroll down 10 lines | demonstration. /// Scroll down 10 lines | demonstration.
pub fn scroll_down() { pub fn scroll_down() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -104,7 +104,7 @@ pub fn scroll_down() {
/// Scroll down 10 lines | demonstration. /// Scroll down 10 lines | demonstration.
pub fn scroll_up() { pub fn scroll_up() {
let mut terminal = terminal(); let terminal = terminal();
print_test_data(); print_test_data();
@ -114,7 +114,7 @@ pub fn scroll_up() {
/// Resize the terminal to X: 10, Y: 10 | demonstration. /// Resize the terminal to X: 10, Y: 10 | demonstration.
pub fn resize_terminal() { pub fn resize_terminal() {
let mut terminal = terminal(); let terminal = terminal();
// Get terminal size // Get terminal size
terminal.set_size(10, 10); terminal.set_size(10, 10);
@ -122,6 +122,6 @@ pub fn resize_terminal() {
/// exit the current proccess. /// exit the current proccess.
pub fn exit() { pub fn exit() {
let mut terminal = terminal(); let terminal = terminal();
terminal.exit(); terminal.exit();
} }

View File

@ -18,8 +18,8 @@ pub trait IStateCommand {
} }
pub trait IEnableAnsiCommand { pub trait IEnableAnsiCommand {
fn enable(&self) -> bool; fn enable(&self) -> io::Result<bool>;
fn disable(&self) -> bool; fn disable(&self) -> io::Result<()>;
} }
// This trait provides an interface for switching to alternate screen and back. // This trait provides an interface for switching to alternate screen and back.

View File

@ -2,12 +2,12 @@
use super::{IAlternateScreenCommand, IEnableAnsiCommand, TerminalOutput}; use super::{IAlternateScreenCommand, IEnableAnsiCommand, TerminalOutput};
use kernel::windows_kernel::{ansi_support, csbi, handle, kernel}; use kernel::windows_kernel::{ansi_support, ConsoleMode, Handle, ScreenBuffer};
use winapi::shared::minwindef::DWORD; use winapi::shared::minwindef::DWORD;
use winapi::um::wincon; use winapi::um::wincon;
use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING; use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_PROCESSING;
use std::io::{Error, ErrorKind, Result}; use std::io::Result;
/// This command is used for enabling and disabling ANSI code support for windows systems, /// This command is used for enabling and disabling ANSI code support for windows systems,
/// For more info check: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences. /// For more info check: https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.
@ -26,43 +26,35 @@ impl EnableAnsiCommand {
} }
impl IEnableAnsiCommand for EnableAnsiCommand { impl IEnableAnsiCommand for EnableAnsiCommand {
fn enable(&self) -> bool { fn enable(&self) -> Result<bool> {
// we need to check whether we tried to enable ansi before. If we have we can just return if that had succeeded. // 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() { if ansi_support::has_been_tried_to_enable_ansi() && ansi_support::ansi_enabled() {
return ansi_support::windows_supportable(); return Ok(ansi_support::windows_supportable());
} else { } else {
let output_handle = handle::get_output_handle().unwrap(); let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0; let mut dw_mode = console_mode.mode()?;
if !kernel::get_console_mode(&output_handle, &mut dw_mode) {
return false;
}
dw_mode |= self.mask; dw_mode |= self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode) {
return false; console_mode.set_mode(dw_mode)?;
} Ok(true)
return true;
} }
} }
fn disable(&self) -> bool { fn disable(&self) -> Result<()> {
if ansi_support::ansi_enabled() { if ansi_support::ansi_enabled() {
let output_handle = handle::get_output_handle().unwrap(); let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0; let mut dw_mode = console_mode.mode()?;
if !kernel::get_console_mode(&output_handle, &mut dw_mode) {
return false;
}
dw_mode &= !self.mask; dw_mode &= !self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode) {
return false; console_mode.set_mode(dw_mode)?;
}
ansi_support::set_ansi_enabled(false); ansi_support::set_ansi_enabled(false);
} }
return true; Ok(())
} }
} }
@ -84,48 +76,26 @@ impl RawModeCommand {
impl RawModeCommand { impl RawModeCommand {
/// Enables raw mode. /// Enables raw mode.
pub fn enable(&mut self) -> Result<()> { pub fn enable(&mut self) -> Result<()> {
let mut dw_mode: DWORD = 0; let console_mode = ConsoleMode::new()?;
let stdout = handle::get_output_handle().unwrap();
if !kernel::get_console_mode(&stdout, &mut dw_mode) { let mut dw_mode = console_mode.mode()?;
return Err(Error::new(
ErrorKind::Other,
"Could not get console mode when enabling raw mode",
));
}
let new_mode = dw_mode & !self.mask; let new_mode = dw_mode & !self.mask;
if !kernel::set_console_mode(&stdout, new_mode) { console_mode.set_mode(new_mode)?;
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when enabling raw mode",
));
}
Ok(()) Ok(())
} }
/// Disables raw mode. /// Disables raw mode.
pub fn disable(&self) -> Result<()> { pub fn disable(&self) -> Result<()> {
let stdout = handle::get_output_handle().unwrap(); let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0; let mut dw_mode = console_mode.mode()?;
if !kernel::get_console_mode(&stdout, &mut dw_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not get console mode when disabling raw mode",
));
}
let new_mode = dw_mode | self.mask; let new_mode = dw_mode | self.mask;
if !kernel::set_console_mode(&stdout, new_mode) { console_mode.set_mode(new_mode)?;
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when disabling raw mode",
));
}
return Ok(()); return Ok(());
} }
@ -143,21 +113,14 @@ impl ToAlternateScreenCommand {
impl IAlternateScreenCommand for ToAlternateScreenCommand { impl IAlternateScreenCommand for ToAlternateScreenCommand {
fn enable(&self, _stdout: &mut TerminalOutput) -> Result<()> { fn enable(&self, _stdout: &mut TerminalOutput) -> Result<()> {
let _handle = handle::get_output_handle()?; let alternate_screen = ScreenBuffer::create();
alternate_screen.show();
// create a new screen buffer to copy to.
let new_handle = csbi::create_console_screen_buffer();
// Make the new screen buffer the active screen buffer.
csbi::set_active_screen_buffer(new_handle)?;
Ok(()) Ok(())
} }
fn disable(&self, _stdout: &TerminalOutput) -> Result<()> { fn disable(&self, _stdout: &TerminalOutput) -> Result<()> {
let handle = handle::get_output_handle()?; let screen_buffer = ScreenBuffer::from(Handle::output_handle()?);
csbi::set_active_screen_buffer(handle); screen_buffer.show()?;
Ok(()) Ok(())
} }
} }

View File

@ -8,25 +8,49 @@ use std::sync::Arc;
use kernel::windows_kernel::ansi_support::{try_enable_ansi_support, windows_supportable}; use kernel::windows_kernel::ansi_support::{try_enable_ansi_support, windows_supportable};
#[cfg(windows)] #[cfg(windows)]
use kernel::windows_kernel::terminal::{exit, terminal_size}; use kernel::windows_kernel::exit;
#[cfg(windows)]
use kernel::windows_kernel::ScreenBuffer;
#[cfg(windows)] #[cfg(windows)]
use kernel::windows_kernel::cursor::pos; use kernel::windows_kernel::Cursor;
#[cfg(unix)] #[cfg(unix)]
use kernel::unix_kernel::terminal::{exit, pos, terminal_size}; use kernel::unix_kernel::terminal::{exit, pos, terminal_size};
/// Get the terminal size based on the current platform. /// Get the terminal size based on the current platform.
#[cfg(unix)]
pub fn get_terminal_size() -> (u16, u16) { pub fn get_terminal_size() -> (u16, u16) {
terminal_size() terminal_size()
} }
/// Get the cursor position based on the current platform.
pub fn get_cursor_position() -> (u16, u16) {
#[cfg(unix)]
return pos().expect("Valide position");
#[cfg(windows)] #[cfg(windows)]
return pos(); pub fn get_terminal_size() -> (u16, u16) {
if let Ok(buffer) = ScreenBuffer::current() {
let size = buffer.info().unwrap().terminal_size();
(size.width as u16, size.height as u16)
} else {
(0, 0)
}
}
/// Get the cursor position based on the current platform.
#[cfg(unix)]
pub fn get_cursor_position() -> (u16, u16) {
if let Ok(pos) = pos() {
pos
} else {
(0, 0)
}
}
#[cfg(windows)]
pub fn get_cursor_position() -> (u16, u16) {
if let Ok(cursor) = Cursor::new() {
cursor.position().unwrap().into()
} else {
(0, 0)
}
} }
/// exit the current terminal. /// exit the current terminal.
@ -40,18 +64,18 @@ pub fn exit_terminal() {
/// If the current platform is unix it will return the ansi implementation. /// If the current platform is unix it will return the ansi implementation.
pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T> { pub fn get_module<T>(winapi_impl: T, unix_impl: T) -> Option<T> {
let mut term: Option<T> = None; let mut term: Option<T> = None;
let mut does_support = true; let does_support = false;
if !windows_supportable() { // if !windows_supportable() {
// Try to enable ansi on windows if not than use WINAPI. // Try to enable ansi on windows if not than use WINAPI.
does_support = try_enable_ansi_support(); // does_support = try_enable_ansi_support();
// uncomment this line when you want to use the winapi implementation. // uncomment this line when you want to use the winapi implementation.
// does_support = false; // does_support = false;
if !does_support { if !does_support {
term = Some(winapi_impl); term = Some(winapi_impl);
} }
} // }
if does_support { if does_support {
term = Some(unix_impl); term = Some(unix_impl);

View File

@ -13,7 +13,7 @@ use common::commands::IEnableAnsiCommand;
pub fn try_enable_ansi_support() -> bool { pub fn try_enable_ansi_support() -> bool {
ENABLE_ANSI.call_once(|| { ENABLE_ANSI.call_once(|| {
let command = EnableAnsiCommand::new(); let command = EnableAnsiCommand::new();
let success = command.enable(); let success = command.enable().unwrap();
set_is_windows_ansi_supportable(success); set_is_windows_ansi_supportable(success);
set_ansi_enabled(success); set_ansi_enabled(success);

View File

@ -1,117 +0,0 @@
//! This contains the logic for working with the console buffer.
use winapi::shared::minwindef::TRUE;
use winapi::shared::ntdef::NULL;
use winapi::um::minwinbase::SECURITY_ATTRIBUTES;
use winapi::um::wincon::{
CreateConsoleScreenBuffer, GetConsoleScreenBufferInfo, SetConsoleActiveScreenBuffer,
SetConsoleScreenBufferSize, CONSOLE_SCREEN_BUFFER_INFO, CONSOLE_TEXTMODE_BUFFER, COORD,
};
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE};
use super::{handle, kernel, Empty, HANDLE};
use std::io::{self, Result};
use std::mem::size_of;
use std::sync::{Once, ONCE_INIT};
/// Create a new console screen buffer info struct.
pub fn get_csbi() -> Result<CONSOLE_SCREEN_BUFFER_INFO> {
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
let success;
unsafe { success = GetConsoleScreenBufferInfo(handle::get_current_out_handle()?, &mut csbi) }
if success == 0 {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get console screen buffer info",
));
}
Ok(csbi)
}
/// Get buffer info and handle of the current screen.
pub fn get_csbi_and_handle() -> Result<(CONSOLE_SCREEN_BUFFER_INFO, HANDLE)> {
let handle = handle::get_current_out_handle()?;
let csbi = get_csbi_by_handle(&handle)?;
return Ok((csbi, handle));
}
/// Create a new console screen buffer info struct.
pub fn get_csbi_by_handle(handle: &HANDLE) -> Result<CONSOLE_SCREEN_BUFFER_INFO> {
let mut csbi = CONSOLE_SCREEN_BUFFER_INFO::empty();
unsafe {
if !kernel::is_true(GetConsoleScreenBufferInfo(*handle, &mut csbi)) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get console screen buffer info",
));
}
}
Ok(csbi)
}
/// Set the console screen buffer size
pub fn set_console_screen_buffer_size(size: COORD) -> bool {
let handle = handle::get_current_out_handle().unwrap();
unsafe {
if !kernel::is_true(SetConsoleScreenBufferSize(handle, size)) {
return false;
} else {
return true;
}
}
}
/// Create new console screen buffer. This can be used for alternate screen.
pub fn create_console_screen_buffer() -> HANDLE {
let mut security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES {
nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: NULL,
bInheritHandle: TRUE,
};
unsafe {
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
}
}
/// Set the active screen buffer to the given handle. This can be used for alternate screen.
pub fn set_active_screen_buffer(new_buffer: HANDLE) -> Result<()> {
unsafe {
if !kernel::is_true(SetConsoleActiveScreenBuffer(new_buffer)) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not set the active screen buffer",
));
}
}
Ok(())
}
static GET_ORIGINAL_CONSOLE_COLOR: Once = ONCE_INIT;
static mut original_console_color: u16 = 0;
pub fn get_original_console_color() -> u16 {
GET_ORIGINAL_CONSOLE_COLOR.call_once(|| {
let handle = handle::get_output_handle().unwrap();
let csbi = get_csbi_by_handle(&handle).unwrap();
unsafe { original_console_color = csbi.wAttributes as u16 };
});
return unsafe { original_console_color };
}

View File

@ -1,99 +1,123 @@
//! This module handles some logic for cursor interaction in the windows console. //! This module handles some logic for cursor interaction in the windows console.
use winapi::shared::minwindef::{FALSE, TRUE}; use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer};
use winapi::um::wincon::{
SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD, use winapi::{
shared::minwindef::{FALSE, TRUE},
um::wincon::{SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD},
um::winnt::HANDLE,
}; };
use super::{csbi, handle, kernel}; use std::io::{self, Result};
use std::io;
/// This stores the cursor pos, at program level. So it can be recalled later. /// This stores the cursor pos, at program level. So it can be recalled later.
static mut SAVED_CURSOR_POS: (u16, u16) = (0, 0); static mut SAVED_CURSOR_POS: (u16, u16) = (0, 0);
/// Reset to saved cursor position pub struct Cursor {
pub fn reset_to_saved_position() { screen_buffer: ScreenBuffer,
unsafe {
set_console_cursor_position(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16);
}
} }
/// Save current cursor position to recall later. impl Cursor {
pub fn save_cursor_pos() { pub fn new() -> io::Result<Cursor> {
let position = pos(); Ok(Cursor {
screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?),
unsafe { })
SAVED_CURSOR_POS = (position.0, position.1);
}
} }
/// get the current cursor position. /// get the current cursor position.
pub fn pos() -> (u16, u16) { pub fn position(&self) -> Result<Coord> {
let handle = handle::get_current_out_handle().unwrap(); Ok(self.screen_buffer.info()?.cursor_pos())
if let Ok(csbi) = csbi::get_csbi_by_handle(&handle) {
(
csbi.dwCursorPosition.X as u16,
csbi.dwCursorPosition.Y as u16,
)
} else {
(0, 0)
}
} }
/// Set the cursor position to the given x and y. Note that this is 0 based. /// Set the cursor position to the given x and y. Note that this is 0 based.
pub fn set_console_cursor_position(x: i16, y: i16) { pub fn goto(&self, x: i16, y: i16) -> io::Result<()> {
if x < 0 || x >= <i16>::max_value() { if x < 0 || x >= <i16>::max_value() {
panic!( return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Argument Out of Range Exception when setting cursor position to X: {}", "Argument Out of Range Exception when setting cursor position to X: {}",
x x
); ),
));
} }
if y < 0 || y >= <i16>::max_value() { if y < 0 || y >= <i16>::max_value() {
panic!( return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Argument Out of Range Exception when setting cursor position to Y: {}", "Argument Out of Range Exception when setting cursor position to Y: {}",
y y
); ),
));
} }
let handle = handle::get_current_out_handle().unwrap();
let position = COORD { X: x, Y: y }; let position = COORD { X: x, Y: y };
unsafe { unsafe {
let success = SetConsoleCursorPosition(handle, position); if !is_true(SetConsoleCursorPosition(
**self.screen_buffer.get_handle(),
if success == 0 { position,
panic!("Argument out of range when trying to set cursor position."); )) {
return Err(io::Error::last_os_error());
} }
} }
Ok(())
} }
//pub fn set_relative_cursor_pos(x: i16, y: i16)
//{
// let (cur_x, cur_y) = pos()?;
// let Relative(x, y) = *self;
// let (x, y) = (x + cur_x, y + cur_y);
// platform::set_cursor_pos(x, y)?;
//}
/// change the cursor visibility. /// change the cursor visibility.
pub fn cursor_visibility(visable: bool) -> io::Result<()> { pub fn set_visibility(&self, visable: bool) -> io::Result<()> {
let handle = handle::get_current_out_handle().unwrap();
let cursor_info = CONSOLE_CURSOR_INFO { let cursor_info = CONSOLE_CURSOR_INFO {
dwSize: 100, dwSize: 100,
bVisible: if visable { TRUE } else { FALSE }, bVisible: if visable { TRUE } else { FALSE },
}; };
unsafe { unsafe {
if !kernel::is_true(SetConsoleCursorInfo(handle, &cursor_info)) { if !is_true(SetConsoleCursorInfo(
return Err(io::Error::new( **self.screen_buffer.get_handle(),
io::ErrorKind::Other, &cursor_info,
"Could not get console screen buffer info", )) {
)); return Err(io::Error::last_os_error());
} }
} }
Ok(()) Ok(())
} }
/// Reset to saved cursor position
pub fn reset_to_saved_position() -> io::Result<()> {
let cursor = Cursor::new()?;
unsafe {
cursor.goto(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16)?;
}
Ok(())
}
/// Save current cursor position to recall later.
pub fn save_cursor_pos() -> io::Result<()> {
let cursor = Cursor::new()?;
let position = cursor.position()?;
unsafe {
SAVED_CURSOR_POS = (position.x as u16, position.y as u16);
}
Ok(())
}
}
impl From<Handle> for Cursor {
fn from(handle: Handle) -> Self {
Cursor {
screen_buffer: ScreenBuffer::from(handle),
}
}
}
impl From<HANDLE> for Cursor {
fn from(handle: HANDLE) -> Self {
Cursor {
screen_buffer: ScreenBuffer::from(handle),
}
}
}

View File

@ -1,99 +0,0 @@
//! This module contains some logic for working with the console handle.
use super::*;
use winapi::um::fileapi::{CreateFileW, OPEN_EXISTING};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::{STD_INPUT_HANDLE, STD_OUTPUT_HANDLE};
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE};
use std::io::{self, Result};
use std::ptr::null_mut;
/// Get the handle of the active screen.
pub fn get_current_out_handle() -> Result<HANDLE> {
let utf16: Vec<u16> = "CONOUT$\0".encode_utf16().collect();
let utf16_ptr: *const u16 = utf16.as_ptr();
let handle = unsafe {
CreateFileW(
utf16_ptr,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
null_mut(),
OPEN_EXISTING,
0,
null_mut(),
)
};
if !is_valid_handle(&handle) {
return Err(io::Error::last_os_error());
}
Ok(handle)
}
/// Get the handle of the active screen.
pub fn get_current_in_handle() -> Result<HANDLE> {
let utf16: Vec<u16> = "CONIN$\0".encode_utf16().collect();
let utf16_ptr: *const u16 = utf16.as_ptr();
let handle = unsafe {
CreateFileW(
utf16_ptr,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
null_mut(),
OPEN_EXISTING,
0,
null_mut(),
)
};
if !is_valid_handle(&handle) {
return Err(io::Error::last_os_error());
}
Ok(handle)
}
/// Get the std_output_handle of the console
pub fn get_output_handle() -> Result<HANDLE> {
unsafe {
let handle = GetStdHandle(STD_OUTPUT_HANDLE);
if !is_valid_handle(&handle) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get output handle!",
));
}
Ok(handle)
}
}
/// Get the std_input_handle of the console
pub fn get_input_handle() -> Result<HANDLE> {
unsafe {
let handle = GetStdHandle(STD_INPUT_HANDLE);
if !is_valid_handle(&handle) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get input handle",
));
}
Ok(handle)
}
}
/// Checks if the console handle is an invalid handle value.
fn is_valid_handle(handle: &HANDLE) -> bool {
if *handle == INVALID_HANDLE_VALUE {
false
} else {
true
}
}

View File

@ -1,60 +0,0 @@
//! This module contains some basic winapi calls.
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
use winapi::um::wincon::{
GetLargestConsoleWindowSize, SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, SMALL_RECT,
};
use super::*;
/// Get the largest console window size possible.
pub fn get_largest_console_window_size() -> COORD {
let output_handle = handle::get_output_handle().unwrap();
unsafe { GetLargestConsoleWindowSize(output_handle) }
}
/// Set the console mode to the given console mode.
pub fn set_console_mode(handle: &HANDLE, console_mode: u32) -> bool {
unsafe {
return is_true(SetConsoleMode(*handle, console_mode));
}
}
/// Get the console mode.
pub fn get_console_mode(handle: &HANDLE, current_mode: &mut u32) -> bool {
unsafe {
return is_true(GetConsoleMode(*handle, &mut *current_mode));
}
}
/// Change the console text attribute.
pub fn set_console_text_attribute(value: u16) -> bool {
let handle = handle::get_current_out_handle().unwrap();
unsafe {
return is_true(SetConsoleTextAttribute(handle, value));
}
}
/// Change console info.
pub fn set_console_info(absolute: bool, rect: &SMALL_RECT) -> bool {
let handle = handle::get_current_out_handle().unwrap();
let absolute = match absolute {
true => 1,
false => 0,
};
unsafe {
return is_true(SetConsoleWindowInfo(handle, absolute, rect));
}
}
/// Parse integer to an bool
pub fn is_true(value: i32) -> bool {
if value == 0 {
return false;
} else {
return true;
}
}

View File

@ -1,44 +1,22 @@
//! This module contains the `windows` (unsafe) logic. //! This module contains the `windows` (unsafe) logic.
pub mod ansi_support; pub mod ansi_support;
pub mod csbi; mod cursor;
pub mod cursor;
pub mod handle;
pub mod kernel;
pub mod reading; pub mod reading;
pub mod terminal;
pub mod writing; pub mod writing;
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT}; use winapi::um::{
use winapi::um::winnt::HANDLE; wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT},
winnt::HANDLE,
};
use common::traits::Empty; pub use self::cursor::Cursor;
pub use crossterm_winapi::{
Console, ConsoleMode, Coord, Handle, HandleType, ScreenBuffer, ScreenBufferInfo, Size,
WindowPositions,
};
impl Empty for COORD { /// Exit the current process.
fn empty() -> COORD { pub fn exit() {
COORD { X: 0, Y: 0 } ::std::process::exit(256);
}
}
impl Empty for SMALL_RECT {
fn empty() -> SMALL_RECT {
SMALL_RECT {
Top: 0,
Right: 0,
Bottom: 0,
Left: 0,
}
}
}
impl Empty for CONSOLE_SCREEN_BUFFER_INFO {
fn empty() -> CONSOLE_SCREEN_BUFFER_INFO {
CONSOLE_SCREEN_BUFFER_INFO {
dwSize: COORD::empty(),
dwCursorPosition: COORD::empty(),
wAttributes: 0,
srWindow: SMALL_RECT::empty(),
dwMaximumWindowSize: COORD::empty(),
}
}
} }

View File

@ -1,24 +1,22 @@
use super::handle::get_current_in_handle; use crossterm_winapi::{Handle, HandleType};
use std::io::{self, Error, Result};
use std::{ use std::{
mem::{self, zeroed}, io::{self, Write},
ptr::{null, null_mut}, mem,
}; };
use winapi::{ use winapi::{
shared::minwindef::{LPVOID, ULONG}, shared::minwindef::{LPVOID, ULONG},
um::consoleapi::{ReadConsoleInputW, ReadConsoleW}, um::{
um::wincon::CONSOLE_READCONSOLE_CONTROL, consoleapi::ReadConsoleW,
um::wincon::{CHAR_INFO, CONSOLE_FONT_INFOEX, INPUT_RECORD, PCONSOLE_READCONSOLE_CONTROL}, wincon::{CONSOLE_READCONSOLE_CONTROL, PCONSOLE_READCONSOLE_CONTROL},
},
}; };
use std::io::Write;
/// Could be used to read a line from the stdin. /// Could be used to read a line from the stdin.
/// Note that this is a blocking call and it continues when user pressed enter. /// Note that this is a blocking call and it continues when user pressed enter.
pub fn read_line(buf: &mut Vec<u8>) -> io::Result<usize> { pub fn read_line(buf: &mut Vec<u8>) -> io::Result<usize> {
let handle = get_current_in_handle()?; let handle = Handle::current_in_handle()?;
let mut utf16 = vec![0u16; 0x1000]; let mut utf16 = vec![0u16; 0x1000];
let mut num = 0; let mut num = 0;

View File

@ -1,36 +0,0 @@
//! This module contains terminal specific logic.
use super::{csbi, handle};
/// Get the terminal size
pub fn terminal_size() -> (u16, u16) {
let handle = handle::get_output_handle().unwrap();
// if let Ok(csbi) = csbi::get_csbi_by_handle(&handle) {
// println!("right: {} left: {} bottom: {}, top: {} window top {} windows width {} csbi.dwCursorPosition.X {} csbi.dwCursorPosition.Y {}", csbi.srWindow.Right, csbi.srWindow.Left, csbi.srWindow.Bottom, csbi.srWindow.Top, csbi.dwSize.X,csbi.dwSize.Y, csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y);
// }
if let Ok(csbi) = csbi::get_csbi_by_handle(&handle) {
(
(csbi.srWindow.Right - csbi.srWindow.Left) as u16,
(csbi.srWindow.Bottom - csbi.srWindow.Top) as u16,
)
} else {
return (0, 0);
}
}
pub fn buffer_size() -> (u16, u16) {
let handle = handle::get_output_handle().unwrap();
if let Ok(csbi) = csbi::get_csbi_by_handle(&handle) {
((csbi.dwSize.X) as u16, (csbi.dwSize.Y) as u16)
} else {
return (0, 0);
}
}
/// Exit the current process.
pub fn exit() {
::std::process::exit(256);
}

View File

@ -3,62 +3,14 @@
use winapi::ctypes::c_void; use winapi::ctypes::c_void;
use winapi::shared::ntdef::NULL; use winapi::shared::ntdef::NULL;
use winapi::um::consoleapi::WriteConsoleW; use winapi::um::consoleapi::WriteConsoleW;
use winapi::um::wincon::{ use winapi::um::wincon::{WriteConsoleOutputA, CHAR_INFO, COORD, PSMALL_RECT};
FillConsoleOutputAttribute, FillConsoleOutputCharacterA, WriteConsoleOutputA, CHAR_INFO, COORD, use winapi::um::winnt::HANDLE;
PSMALL_RECT,
};
use super::{csbi, handle, kernel, HANDLE}; use crossterm_winapi::{is_true, ScreenBuffer};
use std::io::{self, Result}; use std::io::{self, Result};
use std::str; use std::str;
/// Fill a certain block with characters.
pub fn fill_console_output_character(
cells_written: &mut u32,
start_location: COORD,
cells_to_write: u32,
) -> bool {
let handle = handle::get_current_out_handle().unwrap();
unsafe {
// fill the cells in console with blanks
let success = FillConsoleOutputCharacterA(
handle,
' ' as i8,
cells_to_write,
start_location,
cells_written,
);
kernel::is_true(success)
}
}
/// Set console ouput attribute for certain block.
pub fn fill_console_output_attribute(
cells_written: &mut u32,
start_location: COORD,
cells_to_write: u32,
) -> bool {
// Get the position of the current console window
let (csbi, handle) = csbi::get_csbi_and_handle().unwrap();
let success;
unsafe {
success = FillConsoleOutputAttribute(
handle,
csbi.wAttributes,
cells_to_write,
start_location,
cells_written,
);
}
kernel::is_true(success)
}
/// Write console output. /// Write console output.
pub fn write_console_output( pub fn write_console_output(
write_buffer: &HANDLE, write_buffer: &HANDLE,
@ -68,7 +20,7 @@ pub fn write_console_output(
source_buffer: PSMALL_RECT, source_buffer: PSMALL_RECT,
) -> Result<()> { ) -> Result<()> {
unsafe { unsafe {
if !kernel::is_true( if !is_true(
WriteConsoleOutputA( WriteConsoleOutputA(
*write_buffer, // screen buffer to write to *write_buffer, // screen buffer to write to
copy_buffer.as_mut_ptr(), // buffer to copy into copy_buffer.as_mut_ptr(), // buffer to copy into
@ -77,10 +29,7 @@ pub fn write_console_output(
source_buffer, source_buffer,
), // screen buffer source rectangle ), // screen buffer source rectangle
) { ) {
return Err(io::Error::new( return Err(io::Error::last_os_error());
io::ErrorKind::Other,
"Could not write to terminal",
));
} }
} }
@ -88,15 +37,15 @@ pub fn write_console_output(
} }
/// Write utf8 buffer to console. /// Write utf8 buffer to console.
pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> ::std::io::Result<usize> { pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> io::Result<usize> {
// get string from u8[] and parse it to an c_str // get string from u8[] and parse it to an c_str
let utf8 = match str::from_utf8(buf) { let utf8 = match str::from_utf8(buf) {
Ok(string) => string, Ok(string) => string,
Err(_) => { Err(_) => {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::Other, io::ErrorKind::Other,
"Could not parse input to utf8 string.", "Could not parse to utf8 string",
)) ));
} }
}; };
@ -104,30 +53,29 @@ pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> ::std::io::Result<usize
let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void; let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void;
// get buffer info // get buffer info
let csbi = csbi::get_csbi_by_handle(handle)?; match ScreenBuffer::from(*handle).info() {
Ok(csbi) => {
// get current position // get current position
let _current_pos = COORD { let _current_pos = COORD {
X: csbi.dwCursorPosition.X, X: csbi.cursor_pos().x,
Y: csbi.dwCursorPosition.Y, Y: csbi.cursor_pos().y,
}; };
let mut cells_written: u32 = 0; let mut cells_written: u32 = 0;
let mut success = false;
// write to console // write to console
unsafe { unsafe {
success = kernel::is_true(WriteConsoleW( if !is_true(WriteConsoleW(
*handle, *handle,
utf16_ptr, utf16_ptr,
utf16.len() as u32, utf16.len() as u32,
&mut cells_written, &mut cells_written,
NULL, NULL,
)); )) {
} return Err(io::Error::last_os_error());
}
match success { }
// think this is wrong could be done better! Ok(utf8.as_bytes().len())
true => Ok(utf8.as_bytes().len()), }
false => Ok(0), Err(e) => Err(e),
} }
} }

View File

@ -6,6 +6,16 @@
//! //!
//! This crate supports all UNIX and Windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals] in the README. //! This crate supports all UNIX and Windows terminals down to windows 7 (not all terminals are tested see [Tested Terminals] in the README.
#[cfg(unix)]
extern crate libc;
#[cfg(unix)]
extern crate termios;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate crossterm_winapi;
#[macro_use] #[macro_use]
mod common; mod common;
@ -28,11 +38,3 @@ pub use self::style::{
pub use self::terminal::{terminal, Terminal}; pub use self::terminal::{terminal, Terminal};
pub use common::screen::{AlternateScreen, Screen}; pub use common::screen::{AlternateScreen, Screen};
pub use common::Crossterm; pub use common::Crossterm;
#[cfg(unix)]
extern crate libc;
#[cfg(unix)]
extern crate termios;
#[cfg(windows)]
extern crate winapi;

View File

@ -2,7 +2,7 @@
//! This module is used for Windows terminals that do not support ANSI escape codes. //! This module is used for Windows terminals that do not support ANSI escape codes.
//! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position. //! Note that the cursor position is 0 based. This means that we start counting at 0 when setting the cursor position.
use kernel::windows_kernel::cursor; use kernel::windows_kernel::Cursor;
use super::*; use super::*;
@ -11,17 +11,19 @@ pub struct WinApiCursor;
impl WinApiCursor { impl WinApiCursor {
pub fn new() -> Box<WinApiCursor> { pub fn new() -> Box<WinApiCursor> {
Box::from(WinApiCursor {}) Box::from(WinApiCursor)
} }
} }
impl ITerminalCursor for WinApiCursor { impl ITerminalCursor for WinApiCursor {
fn goto(&self, x: u16, y: u16, _stdout: &Option<&Arc<TerminalOutput>>) { fn goto(&self, x: u16, y: u16, _stdout: &Option<&Arc<TerminalOutput>>) {
cursor::set_console_cursor_position(x as i16, y as i16); let cursor = Cursor::new().unwrap();
cursor.goto(x as i16, y as i16);
} }
fn pos(&self) -> (u16, u16) { fn pos(&self) -> (u16, u16) {
cursor::pos() let cursor = Cursor::new().unwrap();
cursor.position().unwrap().into()
} }
fn move_up(&self, count: u16, _stdout: &Option<&Arc<TerminalOutput>>) { fn move_up(&self, count: u16, _stdout: &Option<&Arc<TerminalOutput>>) {
@ -45,19 +47,19 @@ impl ITerminalCursor for WinApiCursor {
} }
fn save_position(&self, _stdout: &Option<&Arc<TerminalOutput>>) { fn save_position(&self, _stdout: &Option<&Arc<TerminalOutput>>) {
cursor::save_cursor_pos(); Cursor::save_cursor_pos();
} }
fn reset_position(&self, _stdout: &Option<&Arc<TerminalOutput>>) { fn reset_position(&self, _stdout: &Option<&Arc<TerminalOutput>>) {
cursor::reset_to_saved_position(); Cursor::reset_to_saved_position();
} }
fn hide(&self, _stdout: &Option<&Arc<TerminalOutput>>) { fn hide(&self, _stdout: &Option<&Arc<TerminalOutput>>) {
cursor::cursor_visibility(false); Cursor::new().unwrap().set_visibility(false);
} }
fn show(&self, _stdout: &Option<&Arc<TerminalOutput>>) { fn show(&self, _stdout: &Option<&Arc<TerminalOutput>>) {
cursor::cursor_visibility(true); Cursor::new().unwrap().set_visibility(true);
} }
fn blink(&self, _blink: bool, _stdout: &Option<&Arc<TerminalOutput>>) {} fn blink(&self, _blink: bool, _stdout: &Option<&Arc<TerminalOutput>>) {}

View File

@ -2,7 +2,6 @@
use super::*; use super::*;
use kernel::windows_kernel::reading::read_line;
use std::char; use std::char;
use std::thread; use std::thread;
use winapi::um::winnt::INT; use winapi::um::winnt::INT;

View File

@ -1,5 +1,5 @@
use super::IStdout; use super::IStdout;
use kernel::windows_kernel::{handle, writing}; use kernel::windows_kernel::{writing, Handle};
use std::io; use std::io;
@ -18,7 +18,7 @@ impl IStdout for WinApiOutput {
} }
fn write(&self, buf: &[u8]) -> io::Result<usize> { fn write(&self, buf: &[u8]) -> io::Result<usize> {
let handle = handle::get_current_out_handle().unwrap(); let handle = Handle::current_out_handle().unwrap();
writing::write_char_buffer(&handle, buf) writing::write_char_buffer(&handle, buf)
} }

View File

@ -2,32 +2,34 @@
//! This module is used for non supporting `ANSI` Windows terminals. //! This module is used for non supporting `ANSI` Windows terminals.
use super::*; use super::*;
use kernel::windows_kernel::{csbi, kernel}; use kernel::windows_kernel::{Console, Handle, HandleType, ScreenBuffer};
use std::io;
use std::sync::{Once, ONCE_INIT};
use winapi::um::wincon; use winapi::um::wincon;
/// This struct is a WinApi implementation for color related actions. /// This struct is a WinApi implementation for color related actions.
pub struct WinApiColor { pub struct WinApiColor;
original_color: u16,
}
impl WinApiColor { impl WinApiColor {
pub fn new() -> WinApiColor { pub fn new() -> WinApiColor {
WinApiColor { WinApiColor
original_color: csbi::get_original_console_color(),
}
} }
} }
impl ITerminalColor for WinApiColor { impl ITerminalColor for WinApiColor {
fn set_fg(&self, fg_color: Color, _stdout: &Option<&Arc<TerminalOutput>>) { fn set_fg(&self, fg_color: Color, _stdout: &Option<&Arc<TerminalOutput>>) {
// init the original color in case it is not set.
let _ = init_console_color().unwrap();
let color_value = &self.color_value(fg_color, ColorType::Foreground); let color_value = &self.color_value(fg_color, ColorType::Foreground);
let csbi = csbi::get_csbi().unwrap(); let screen_buffer = ScreenBuffer::current().unwrap();
let csbi = screen_buffer.info().unwrap();
// Notice that the color values are stored in wAttribute. // 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. // So we need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16; let mut color: u16;
let attrs = csbi.wAttributes; let attrs = csbi.attributes();
let bg_color = attrs & 0x0070; let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | bg_color; color = color_value.parse::<u16>().unwrap() | bg_color;
@ -37,18 +39,24 @@ impl ITerminalColor for WinApiColor {
color = color | wincon::BACKGROUND_INTENSITY as u16; color = color | wincon::BACKGROUND_INTENSITY as u16;
} }
kernel::set_console_text_attribute(color); Console::from(**screen_buffer.get_handle())
.set_text_attribute(color)
.unwrap();
} }
fn set_bg(&self, bg_color: Color, _stdout: &Option<&Arc<TerminalOutput>>) { fn set_bg(&self, bg_color: Color, _stdout: &Option<&Arc<TerminalOutput>>) {
// init the original color in case it is not set.
let _ = init_console_color().unwrap();
let color_value = &self.color_value(bg_color, ColorType::Background); let color_value = &self.color_value(bg_color, ColorType::Background);
let (csbi, _handle) = csbi::get_csbi_and_handle().unwrap(); let screen_buffer = ScreenBuffer::current().unwrap();
let csbi = screen_buffer.info().unwrap();
// Notice that the color values are stored in wAttribute. // 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. // So wee need to use bitwise operators to check if the values exists or to get current console colors.
let mut color: u16; let mut color: u16;
let attrs = csbi.wAttributes; let attrs = csbi.attributes();
let fg_color = attrs & 0x0007; let fg_color = attrs & 0x0007;
color = fg_color | color_value.parse::<u16>().unwrap(); color = fg_color | color_value.parse::<u16>().unwrap();
@ -58,11 +66,17 @@ impl ITerminalColor for WinApiColor {
color = color | wincon::FOREGROUND_INTENSITY as u16; color = color | wincon::FOREGROUND_INTENSITY as u16;
} }
kernel::set_console_text_attribute(color); Console::from(**screen_buffer.get_handle())
.set_text_attribute(color)
.unwrap();
} }
fn reset(&self, _stdout: &Option<&Arc<TerminalOutput>>) { fn reset(&self, _stdout: &Option<&Arc<TerminalOutput>>) {
kernel::set_console_text_attribute(self.original_color); // init the original color in case it is not set.
let original_color = original_console_color();
Console::from(Handle::new(HandleType::CurrentOutputHandle).unwrap())
.set_text_attribute(original_color)
.unwrap();
} }
/// This will get the winapi color value from the Color and ColorType struct /// This will get the winapi color value from the Color and ColorType struct
@ -133,3 +147,21 @@ impl ITerminalColor for WinApiColor {
winapi_color.to_string() winapi_color.to_string()
} }
} }
fn init_console_color() -> io::Result<()> {
let screen_buffer = ScreenBuffer::current()?;
let attr = screen_buffer.info()?.attributes();
GET_ORIGINAL_CONSOLE_COLOR.call_once(|| {
unsafe { ORIGINAL_CONSOLE_COLOR = attr };
});
Ok(())
}
fn original_console_color() -> u16 {
return unsafe { ORIGINAL_CONSOLE_COLOR };
}
static GET_ORIGINAL_CONSOLE_COLOR: Once = ONCE_INIT;
static mut ORIGINAL_CONSOLE_COLOR: u16 = 0;

View File

@ -16,11 +16,11 @@ mod winapi_tests {
let stdout = Some(&screen.stdout); let stdout = Some(&screen.stdout);
let terminal = WinApiTerminal::new(); let terminal = WinApiTerminal::new();
terminal.set_size(10, 10, &stdout); terminal.set_size(20, 10, &stdout);
let (x, y) = terminal.terminal_size(&stdout); let (x, y) = terminal.terminal_size(&stdout);
assert_eq!(x, 10); assert_eq!(x, 20);
assert_eq!(y, 10); assert_eq!(y, 10);
} }
} }

View File

@ -4,10 +4,8 @@
//! Windows versions lower then windows 10 are not supporting ANSI codes. Those versions will use this implementation instead. //! Windows versions lower then windows 10 are not supporting ANSI codes. Those versions will use this implementation instead.
use super::*; use super::*;
use cursor::TerminalCursor;
use kernel::windows_kernel::{csbi, kernel, terminal, writing}; use kernel::windows_kernel::{Console, Coord, Cursor, Handle, ScreenBuffer, Size};
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT};
/// This struct is an winapi implementation for terminal related actions. /// This struct is an winapi implementation for terminal related actions.
pub struct WinApiTerminal; pub struct WinApiTerminal;
@ -19,57 +17,60 @@ impl WinApiTerminal {
} }
impl ITerminal for WinApiTerminal { impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType, stdout: &Option<&Arc<TerminalOutput>>) { fn clear(&self, clear_type: ClearType, _stdout: &Option<&Arc<TerminalOutput>>) {
let csbi = csbi::get_csbi().unwrap(); let screen_buffer = ScreenBuffer::current().unwrap();
let pos = TerminalCursor::new().pos(); let csbi = screen_buffer.info().unwrap();
let pos = csbi.cursor_pos();
let buffer_size = csbi.buffer_size();
let current_attribute = csbi.attributes();
match clear_type { match clear_type {
ClearType::All => { ClearType::All => {
clear_entire_screen(csbi, stdout); clear_entire_screen(buffer_size, current_attribute);
} }
ClearType::FromCursorDown => clear_after_cursor(pos, csbi, stdout), ClearType::FromCursorDown => clear_after_cursor(pos, buffer_size, current_attribute),
ClearType::FromCursorUp => clear_before_cursor(pos, csbi, stdout), ClearType::FromCursorUp => clear_before_cursor(pos, buffer_size, current_attribute),
ClearType::CurrentLine => clear_current_line(pos, csbi, stdout), ClearType::CurrentLine => clear_current_line(pos, buffer_size, current_attribute),
ClearType::UntilNewLine => clear_until_line(pos, csbi, stdout), ClearType::UntilNewLine => clear_until_line(pos, buffer_size, current_attribute),
}; };
} }
fn terminal_size(&self, _stdout: &Option<&Arc<TerminalOutput>>) -> (u16, u16) { fn terminal_size(&self, _stdout: &Option<&Arc<TerminalOutput>>) -> (u16, u16) {
terminal::terminal_size() let csbi = ScreenBuffer::current().unwrap();
csbi.info().unwrap().terminal_size().into()
} }
fn scroll_up(&self, count: i16, _stdout: &Option<&Arc<TerminalOutput>>) { fn scroll_up(&self, count: i16, _stdout: &Option<&Arc<TerminalOutput>>) {
let csbi = csbi::get_csbi().unwrap(); let csbi = ScreenBuffer::current().unwrap();
let mut window = csbi.info().unwrap().terminal_window();
// Set srctWindow to the current window size and location.
let mut srct_window = csbi.srWindow;
// Check whether the window is too close to the screen buffer top // Check whether the window is too close to the screen buffer top
if srct_window.Top >= count { if window.top >= count {
srct_window.Top -= count; // move top down window.top -= count; // move top down
srct_window.Bottom = count; // move bottom down window.bottom = count; // move bottom down
let success = kernel::set_console_info(false, &mut srct_window); Console::new()
if success { .unwrap()
panic!("Something went wrong when scrolling down"); .set_console_info(false, window)
} .unwrap();
} }
} }
fn scroll_down(&self, count: i16, _stdout: &Option<&Arc<TerminalOutput>>) { fn scroll_down(&self, count: i16, _stdout: &Option<&Arc<TerminalOutput>>) {
let csbi = csbi::get_csbi().unwrap(); let csbi = ScreenBuffer::current().unwrap();
// Set srctWindow to the current window size and location. let mut window = csbi.info().unwrap().terminal_window();
let mut srct_window = csbi.srWindow; let buffer_size = csbi.info().unwrap().buffer_size();
// Check whether the window is too close to the screen buffer top // Check whether the window is too close to the screen buffer top
if srct_window.Bottom < csbi.dwSize.Y - count { if window.bottom < buffer_size.height - count {
srct_window.Top += count; // move top down window.top += count; // move top down
srct_window.Bottom += count; // move bottom down window.bottom += count; // move bottom down
let success = kernel::set_console_info(true, &mut srct_window); Console::new()
if success { .unwrap()
panic!("Something went wrong when scrolling down"); .set_console_info(false, window)
} .unwrap();
} }
} }
@ -84,71 +85,70 @@ impl ITerminal for WinApiTerminal {
} }
// Get the position of the current console window // Get the position of the current console window
let csbi = csbi::get_csbi().unwrap(); let screen_buffer = ScreenBuffer::current().unwrap();
let mut success = false; let console = Console::from(**screen_buffer.get_handle());
let csbi = screen_buffer.info().unwrap();
let current_size = csbi.buffer_size();
let window = csbi.terminal_window();
let mut new_size = Size::new(current_size.width, current_size.height);
// If the buffer is smaller than this new window size, resize the // If the buffer is smaller than this new window size, resize the
// buffer to be large enough. Include window position. // buffer to be large enough. Include window position.
let mut resize_buffer = false; 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 current_size.width < window.left + width {
if csbi.srWindow.Left >= i16::max_value() - width { if window.left >= i16::max_value() - width {
panic!("Argument out of range when setting terminal width."); panic!("Argument out of range when setting terminal width.");
} }
size.X = csbi.srWindow.Left + width; new_size.width = window.left + width;
resize_buffer = true; resize_buffer = true;
} }
if csbi.dwSize.Y < csbi.srWindow.Top + height { if current_size.height < window.top + height {
if csbi.srWindow.Top >= i16::max_value() - height { if window.top >= i16::max_value() - height {
panic!("Argument out of range when setting terminal height"); panic!("Argument out of range when setting terminal height");
} }
size.Y = csbi.srWindow.Top + height; new_size.height = window.top + height;
resize_buffer = true; resize_buffer = true;
} }
if resize_buffer { if resize_buffer {
success = csbi::set_console_screen_buffer_size(size); if let Err(_) = screen_buffer.set_size(new_size.width, new_size.height) {
if !success {
panic!("Something went wrong when setting screen buffer size."); panic!("Something went wrong when setting screen buffer size.");
} }
} }
let mut fsr_window: SMALL_RECT = csbi.srWindow; let mut window = window.clone();
// Preserve the position, but change the size. // Preserve the position, but change the size.
fsr_window.Bottom = fsr_window.Top + height; window.bottom = window.top + height;
fsr_window.Right = fsr_window.Left + width; window.right = window.left + width;
console.set_console_info(true, window).unwrap();
let success = kernel::set_console_info(true, &fsr_window);
if success {
// If we resized the buffer, un-resize it. // If we resized the buffer, un-resize it.
if resize_buffer { if resize_buffer {
csbi::set_console_screen_buffer_size(csbi.dwSize); if let Err(_) = screen_buffer.set_size(current_size.width, current_size.height) {
panic!("Something went wrong when setting screen buffer size.");
}
} }
let bounds = kernel::get_largest_console_window_size(); let bounds = console.largest_window_size();
if width > bounds.X { if width > bounds.x {
panic!( panic!(
"Argument width: {} out of range when setting terminal width.", "Argument width: {} out of range when setting terminal width.",
width width
); );
} }
if height > bounds.Y { if height > bounds.y {
panic!( panic!(
"Argument height: {} out of range when setting terminal height", "Argument height: {} out of range when setting terminal height",
height height
); );
} }
} }
}
fn exit(&self, stdout: &Option<&Arc<TerminalOutput>>) { fn exit(&self, stdout: &Option<&Arc<TerminalOutput>>) {
if let Some(output) = stdout { if let Some(output) = stdout {
@ -160,36 +160,26 @@ impl ITerminal for WinApiTerminal {
} }
} }
pub fn clear_after_cursor( pub fn clear_after_cursor(location: Coord, buffer_size: Size, current_attribute: u16) {
pos: (u16, u16), let (mut x, mut y) = (location.x, location.y);
csbi: CONSOLE_SCREEN_BUFFER_INFO,
_stdout: &Option<&Arc<TerminalOutput>>,
) {
let (mut x, mut y) = pos;
// if cursor position is at the outer right position // if cursor position is at the outer right position
if x as i16 > csbi.dwSize.X { if x as i16 > buffer_size.width {
y += 1; y += 1;
x = 0; x = 0;
} }
// location where to start clearing // location where to start clearing
let start_location = COORD { let start_location = Coord::new(x, y);
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); // get sum cells before cursor
let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32;
clear(start_location, cells_to_write, current_attribute);
} }
pub fn clear_before_cursor( pub fn clear_before_cursor(location: Coord, buffer_size: Size, current_attribute: u16) {
pos: (u16, u16), let (xpos, ypos) = (location.x, location.y);
csbi: CONSOLE_SCREEN_BUFFER_INFO,
_stdout: &Option<&Arc<TerminalOutput>>,
) {
let (xpos, ypos) = pos;
// one cell after cursor position // one cell after cursor position
let x = 0; let x = 0;
@ -197,129 +187,68 @@ pub fn clear_before_cursor(
let y = 0; let y = 0;
// location where to start clearing // location where to start clearing
let start_location = COORD { let start_location = Coord::new(x, y);
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); // get sum cells before cursor
let cells_to_write = (buffer_size.width as u32 * ypos as u32) + (xpos as u32 + 1);
// clear everything before cursor position
clear(start_location, cells_to_write, current_attribute);
} }
pub fn clear_entire_screen( pub fn clear_entire_screen(buffer_size: Size, current_attribute: u16) {
csbi: CONSOLE_SCREEN_BUFFER_INFO, // get sum cells before cursor
stdout: &Option<&Arc<TerminalOutput>>, let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32;
) {
// position x at start
let x = 0;
// position y at start
let y = 0;
// location where to start clearing // location where to start clearing
let start_location = COORD { let start_location = Coord::new(0, 0);
X: x as i16,
Y: y as i16, // clear the entire screen
}; clear(start_location, cells_to_write, current_attribute);
// put the cursor back at cell 0,0
let cursor = Cursor::new().unwrap();
cursor.goto(0, 0);
}
pub fn clear_current_line(location: Coord, buffer_size: Size, current_attribute: u16) {
// location where to start clearing
let start_location = Coord::new(0, location.y);
// get sum cells before cursor // get sum cells before cursor
let cells_to_write = buffer_size.width as u32;
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32; // clear the whole current line
clear(start_location, cells_to_write, current_attribute);
clear(start_location, cells_to_write); // put the cursor back at cell 1 on current row
let cursor = Cursor::new().unwrap();
match stdout { cursor.goto(0, location.y);
Some(ref output) => {
// put the cursor back at (0, 0)
TerminalCursor::from_output(output).goto(0, 0);
}
None => {
// put the cursor back at (0, 0)
TerminalCursor::new().goto(0, 0);
}
}
} }
pub fn clear_current_line( pub fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16) {
pos: (u16, u16), let (x, y) = (location.x, location.y);
csbi: CONSOLE_SCREEN_BUFFER_INFO,
stdout: &Option<&Arc<TerminalOutput>>,
) {
// position x at start
let x = 0;
// position y at start
let y = pos.1;
// location where to start clearing // location where to start clearing
let start_location = COORD { let start_location = Coord::new(x, y);
X: x as i16,
Y: y as i16,
};
// get sum cells before cursor // get sum cells before cursor
let cells_to_write = (buffer_size.width - x as i16) as u32;
let cells_to_write = csbi.dwSize.X as u32; // clear until the current line
clear(start_location, cells_to_write, current_attribute);
clear(start_location, cells_to_write); // put the cursor back at original cursor position before we did the clearing
let cursor = Cursor::new().unwrap();
// put the cursor back at 1 cell on current row cursor.goto(x, y);
match stdout {
Some(ref output) => {
// put the cursor back at (0, 0)
TerminalCursor::from_output(output).goto(0, y);
}
None => {
// put the cursor back at (0, 0)
TerminalCursor::new().goto(0, y);
}
}
} }
pub fn clear_until_line( fn clear(start_location: Coord, cells_to_write: u32, current_attribute: u16) {
pos: (u16, u16), let console = Console::from(Handle::current_out_handle().unwrap());
csbi: CONSOLE_SCREEN_BUFFER_INFO, let _ = console
stdout: &Option<&Arc<TerminalOutput>>, .fill_whit_character(start_location, cells_to_write, ' ')
) { .unwrap();
let (x, y) = pos; console
.fill_whit_attribute(start_location, cells_to_write, current_attribute)
// location where to start clearing .unwrap();
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
match stdout {
Some(ref output) => {
// put the cursor back at (0, 0)
TerminalCursor::from_output(output).goto(x, y);
}
None => {
// put the cursor back at (0, 0)
TerminalCursor::new().goto(x, y);
}
}
}
fn clear(start_location: COORD, cells_to_write: u32) {
let mut cells_written = 0;
let mut success = false;
success =
writing::fill_console_output_character(&mut cells_written, start_location, cells_to_write);
if !success {
panic!("Could not clear screen after cursor");
}
cells_written = 0;
success =
writing::fill_console_output_attribute(&mut cells_written, start_location, cells_to_write);
if !success {
panic!("Could not reset attributes after cursor");
}
} }