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"]
readme = "README.md"
[dependencies]
[target.'cfg(windows)'.dependencies]
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]
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() {
// Create a styled object.
// 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.
println!("Colored text: {}", styledobject);
@ -26,7 +26,7 @@ pub fn paint_foreground() {
pub fn paint_background() {
// Create a styled object.
// 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.
println!("Colored text: {}", styledobject);
@ -109,7 +109,10 @@ pub fn print_all_foreground_colors() {
);
#[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.

View File

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

View File

@ -8,10 +8,21 @@
extern crate crossterm;
// modules that could be test
//mod color;
//mod cursor;
//mod input;
mod color;
mod cursor;
mod input;
//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
let input = crossterm.input();
let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor();
let cursor = crossterm.cursor();
let mut stdin = input.read_until_async(b'\r').bytes();
for i in 0..100 {
for _i in 0..100 {
terminal.clear(ClearType::All);
cursor.goto(1, 1);
let a = stdin.next();
@ -68,7 +68,7 @@ pub fn read_async_demo() {
// init some modules we use for this demo
let input = crossterm.input();
let terminal = crossterm.terminal();
let mut cursor = crossterm.cursor();
let cursor = crossterm.cursor();
// this will setup the async reading.
let mut stdin = input.read_async().bytes();

View File

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

View File

@ -131,7 +131,7 @@ fn game_over_screen()
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.show();
}

View File

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

View File

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

View File

@ -2,12 +2,12 @@
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::um::wincon;
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,
/// 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 {
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.
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 {
let output_handle = handle::get_output_handle().unwrap();
let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode) {
return false;
}
let mut dw_mode = console_mode.mode()?;
dw_mode |= self.mask;
if !kernel::set_console_mode(&output_handle, dw_mode) {
return false;
}
return true;
console_mode.set_mode(dw_mode)?;
Ok(true)
}
}
fn disable(&self) -> bool {
fn disable(&self) -> Result<()> {
if ansi_support::ansi_enabled() {
let output_handle = handle::get_output_handle().unwrap();
let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0;
if !kernel::get_console_mode(&output_handle, &mut dw_mode) {
return false;
}
let mut dw_mode = console_mode.mode()?;
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);
}
return true;
Ok(())
}
}
@ -84,48 +76,26 @@ impl RawModeCommand {
impl RawModeCommand {
/// Enables raw mode.
pub fn enable(&mut self) -> Result<()> {
let mut dw_mode: DWORD = 0;
let stdout = handle::get_output_handle().unwrap();
let console_mode = ConsoleMode::new()?;
if !kernel::get_console_mode(&stdout, &mut dw_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not get console mode when enabling raw mode",
));
}
let mut dw_mode = console_mode.mode()?;
let new_mode = dw_mode & !self.mask;
if !kernel::set_console_mode(&stdout, new_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when enabling raw mode",
));
}
console_mode.set_mode(new_mode)?;
Ok(())
}
/// Disables raw mode.
pub fn disable(&self) -> Result<()> {
let stdout = handle::get_output_handle().unwrap();
let console_mode = ConsoleMode::new()?;
let mut dw_mode: DWORD = 0;
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 mut dw_mode = console_mode.mode()?;
let new_mode = dw_mode | self.mask;
if !kernel::set_console_mode(&stdout, new_mode) {
return Err(Error::new(
ErrorKind::Other,
"Could not set console mode when disabling raw mode",
));
}
console_mode.set_mode(new_mode)?;
return Ok(());
}
@ -143,21 +113,14 @@ impl ToAlternateScreenCommand {
impl IAlternateScreenCommand for ToAlternateScreenCommand {
fn enable(&self, _stdout: &mut TerminalOutput) -> Result<()> {
let _handle = handle::get_output_handle()?;
// 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)?;
let alternate_screen = ScreenBuffer::create();
alternate_screen.show();
Ok(())
}
fn disable(&self, _stdout: &TerminalOutput) -> Result<()> {
let handle = handle::get_output_handle()?;
csbi::set_active_screen_buffer(handle);
let screen_buffer = ScreenBuffer::from(Handle::output_handle()?);
screen_buffer.show()?;
Ok(())
}
}

View File

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

View File

@ -13,7 +13,7 @@ use common::commands::IEnableAnsiCommand;
pub fn try_enable_ansi_support() -> bool {
ENABLE_ANSI.call_once(|| {
let command = EnableAnsiCommand::new();
let success = command.enable();
let success = command.enable().unwrap();
set_is_windows_ansi_supportable(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.
use winapi::shared::minwindef::{FALSE, TRUE};
use winapi::um::wincon::{
SetConsoleCursorInfo, SetConsoleCursorPosition, CONSOLE_CURSOR_INFO, COORD,
use crossterm_winapi::{is_true, Coord, Handle, HandleType, ScreenBuffer};
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;
use std::io::{self, Result};
/// This stores the cursor pos, at program level. So it can be recalled later.
static mut SAVED_CURSOR_POS: (u16, u16) = (0, 0);
/// Reset to saved cursor position
pub fn reset_to_saved_position() {
unsafe {
set_console_cursor_position(SAVED_CURSOR_POS.0 as i16, SAVED_CURSOR_POS.1 as i16);
}
pub struct Cursor {
screen_buffer: ScreenBuffer,
}
/// Save current cursor position to recall later.
pub fn save_cursor_pos() {
let position = pos();
unsafe {
SAVED_CURSOR_POS = (position.0, position.1);
}
}
/// get the current cursor position.
pub fn pos() -> (u16, u16) {
let handle = handle::get_current_out_handle().unwrap();
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.
pub fn set_console_cursor_position(x: i16, y: i16) {
if x < 0 || x >= <i16>::max_value() {
panic!(
"Argument Out of Range Exception when setting cursor position to X: {}",
x
);
impl Cursor {
pub fn new() -> io::Result<Cursor> {
Ok(Cursor {
screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?),
})
}
if y < 0 || y >= <i16>::max_value() {
panic!(
"Argument Out of Range Exception when setting cursor position to Y: {}",
y
);
/// get the current cursor position.
pub fn position(&self) -> Result<Coord> {
Ok(self.screen_buffer.info()?.cursor_pos())
}
let handle = handle::get_current_out_handle().unwrap();
let position = COORD { X: x, Y: y };
unsafe {
let success = SetConsoleCursorPosition(handle, position);
if success == 0 {
panic!("Argument out of range when trying to set cursor position.");
}
}
}
//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.
pub fn cursor_visibility(visable: bool) -> io::Result<()> {
let handle = handle::get_current_out_handle().unwrap();
let cursor_info = CONSOLE_CURSOR_INFO {
dwSize: 100,
bVisible: if visable { TRUE } else { FALSE },
};
unsafe {
if !kernel::is_true(SetConsoleCursorInfo(handle, &cursor_info)) {
/// Set the cursor position to the given x and y. Note that this is 0 based.
pub fn goto(&self, x: i16, y: i16) -> io::Result<()> {
if x < 0 || x >= <i16>::max_value() {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not get console screen buffer info",
format!(
"Argument Out of Range Exception when setting cursor position to X: {}",
x
),
));
}
if y < 0 || y >= <i16>::max_value() {
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Argument Out of Range Exception when setting cursor position to Y: {}",
y
),
));
}
let position = COORD { X: x, Y: y };
unsafe {
if !is_true(SetConsoleCursorPosition(
**self.screen_buffer.get_handle(),
position,
)) {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
/// change the cursor visibility.
pub fn set_visibility(&self, visable: bool) -> io::Result<()> {
let cursor_info = CONSOLE_CURSOR_INFO {
dwSize: 100,
bVisible: if visable { TRUE } else { FALSE },
};
unsafe {
if !is_true(SetConsoleCursorInfo(
**self.screen_buffer.get_handle(),
&cursor_info,
)) {
return Err(io::Error::last_os_error());
}
}
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),
}
}
Ok(())
}

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.
pub mod ansi_support;
pub mod csbi;
pub mod cursor;
pub mod handle;
pub mod kernel;
mod cursor;
pub mod reading;
pub mod terminal;
pub mod writing;
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT};
use winapi::um::winnt::HANDLE;
use winapi::um::{
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 {
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(),
}
}
/// Exit the current process.
pub fn exit() {
::std::process::exit(256);
}

View File

@ -1,24 +1,22 @@
use super::handle::get_current_in_handle;
use std::io::{self, Error, Result};
use crossterm_winapi::{Handle, HandleType};
use std::{
mem::{self, zeroed},
ptr::{null, null_mut},
io::{self, Write},
mem,
};
use winapi::{
shared::minwindef::{LPVOID, ULONG},
um::consoleapi::{ReadConsoleInputW, ReadConsoleW},
um::wincon::CONSOLE_READCONSOLE_CONTROL,
um::wincon::{CHAR_INFO, CONSOLE_FONT_INFOEX, INPUT_RECORD, PCONSOLE_READCONSOLE_CONTROL},
um::{
consoleapi::ReadConsoleW,
wincon::{CONSOLE_READCONSOLE_CONTROL, PCONSOLE_READCONSOLE_CONTROL},
},
};
use std::io::Write;
/// Could be used to read a line from the stdin.
/// 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> {
let handle = get_current_in_handle()?;
let handle = Handle::current_in_handle()?;
let mut utf16 = vec![0u16; 0x1000];
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::shared::ntdef::NULL;
use winapi::um::consoleapi::WriteConsoleW;
use winapi::um::wincon::{
FillConsoleOutputAttribute, FillConsoleOutputCharacterA, WriteConsoleOutputA, CHAR_INFO, COORD,
PSMALL_RECT,
};
use winapi::um::wincon::{WriteConsoleOutputA, CHAR_INFO, COORD, PSMALL_RECT};
use winapi::um::winnt::HANDLE;
use super::{csbi, handle, kernel, HANDLE};
use crossterm_winapi::{is_true, ScreenBuffer};
use std::io::{self, Result};
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.
pub fn write_console_output(
write_buffer: &HANDLE,
@ -68,7 +20,7 @@ pub fn write_console_output(
source_buffer: PSMALL_RECT,
) -> Result<()> {
unsafe {
if !kernel::is_true(
if !is_true(
WriteConsoleOutputA(
*write_buffer, // screen buffer to write to
copy_buffer.as_mut_ptr(), // buffer to copy into
@ -77,10 +29,7 @@ pub fn write_console_output(
source_buffer,
), // screen buffer source rectangle
) {
return Err(io::Error::new(
io::ErrorKind::Other,
"Could not write to terminal",
));
return Err(io::Error::last_os_error());
}
}
@ -88,15 +37,15 @@ pub fn write_console_output(
}
/// 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
let utf8 = match str::from_utf8(buf) {
Ok(string) => string,
Err(_) => {
return Err(io::Error::new(
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;
// get buffer info
let csbi = csbi::get_csbi_by_handle(handle)?;
match ScreenBuffer::from(*handle).info() {
Ok(csbi) => {
// get current position
let _current_pos = COORD {
X: csbi.cursor_pos().x,
Y: csbi.cursor_pos().y,
};
// get current position
let _current_pos = COORD {
X: csbi.dwCursorPosition.X,
Y: csbi.dwCursorPosition.Y,
};
let mut cells_written: u32 = 0;
let mut success = false;
// write to console
unsafe {
success = kernel::is_true(WriteConsoleW(
*handle,
utf16_ptr,
utf16.len() as u32,
&mut cells_written,
NULL,
));
}
match success {
// think this is wrong could be done better!
true => Ok(utf8.as_bytes().len()),
false => Ok(0),
let mut cells_written: u32 = 0;
// write to console
unsafe {
if !is_true(WriteConsoleW(
*handle,
utf16_ptr,
utf16.len() as u32,
&mut cells_written,
NULL,
)) {
return Err(io::Error::last_os_error());
}
}
Ok(utf8.as_bytes().len())
}
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.
#[cfg(unix)]
extern crate libc;
#[cfg(unix)]
extern crate termios;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate crossterm_winapi;
#[macro_use]
mod common;
@ -28,11 +38,3 @@ pub use self::style::{
pub use self::terminal::{terminal, Terminal};
pub use common::screen::{AlternateScreen, Screen};
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.
//! 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::*;
@ -11,17 +11,19 @@ pub struct WinApiCursor;
impl WinApiCursor {
pub fn new() -> Box<WinApiCursor> {
Box::from(WinApiCursor {})
Box::from(WinApiCursor)
}
}
impl ITerminalCursor for WinApiCursor {
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) {
cursor::pos()
let cursor = Cursor::new().unwrap();
cursor.position().unwrap().into()
}
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>>) {
cursor::save_cursor_pos();
Cursor::save_cursor_pos();
}
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>>) {
cursor::cursor_visibility(false);
Cursor::new().unwrap().set_visibility(false);
}
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>>) {}

View File

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

View File

@ -1,5 +1,5 @@
use super::IStdout;
use kernel::windows_kernel::{handle, writing};
use kernel::windows_kernel::{writing, Handle};
use std::io;
@ -18,7 +18,7 @@ impl IStdout for WinApiOutput {
}
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)
}

View File

@ -2,32 +2,34 @@
//! This module is used for non supporting `ANSI` Windows terminals.
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;
/// This struct is a WinApi implementation for color related actions.
pub struct WinApiColor {
original_color: u16,
}
pub struct WinApiColor;
impl WinApiColor {
pub fn new() -> WinApiColor {
WinApiColor {
original_color: csbi::get_original_console_color(),
}
WinApiColor
}
}
impl ITerminalColor for WinApiColor {
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 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.
// 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 attrs = csbi.attributes();
let bg_color = attrs & 0x0070;
color = color_value.parse::<u16>().unwrap() | bg_color;
@ -37,18 +39,24 @@ impl ITerminalColor for WinApiColor {
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>>) {
// 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 (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.
// 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 attrs = csbi.attributes();
let fg_color = attrs & 0x0007;
color = fg_color | color_value.parse::<u16>().unwrap();
@ -58,11 +66,17 @@ impl ITerminalColor for WinApiColor {
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>>) {
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
@ -133,3 +147,21 @@ impl ITerminalColor for WinApiColor {
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 terminal = WinApiTerminal::new();
terminal.set_size(10, 10, &stdout);
terminal.set_size(20, 10, &stdout);
let (x, y) = terminal.terminal_size(&stdout);
assert_eq!(x, 10);
assert_eq!(x, 20);
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.
use super::*;
use cursor::TerminalCursor;
use kernel::windows_kernel::{csbi, kernel, terminal, writing};
use winapi::um::wincon::{CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT};
use kernel::windows_kernel::{Console, Coord, Cursor, Handle, ScreenBuffer, Size};
/// This struct is an winapi implementation for terminal related actions.
pub struct WinApiTerminal;
@ -19,57 +17,60 @@ impl WinApiTerminal {
}
impl ITerminal for WinApiTerminal {
fn clear(&self, clear_type: ClearType, stdout: &Option<&Arc<TerminalOutput>>) {
let csbi = csbi::get_csbi().unwrap();
let pos = TerminalCursor::new().pos();
fn clear(&self, clear_type: ClearType, _stdout: &Option<&Arc<TerminalOutput>>) {
let screen_buffer = ScreenBuffer::current().unwrap();
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 {
ClearType::All => {
clear_entire_screen(csbi, stdout);
clear_entire_screen(buffer_size, current_attribute);
}
ClearType::FromCursorDown => clear_after_cursor(pos, csbi, stdout),
ClearType::FromCursorUp => clear_before_cursor(pos, csbi, stdout),
ClearType::CurrentLine => clear_current_line(pos, csbi, stdout),
ClearType::UntilNewLine => clear_until_line(pos, csbi, stdout),
ClearType::FromCursorDown => clear_after_cursor(pos, buffer_size, current_attribute),
ClearType::FromCursorUp => clear_before_cursor(pos, buffer_size, current_attribute),
ClearType::CurrentLine => clear_current_line(pos, buffer_size, current_attribute),
ClearType::UntilNewLine => clear_until_line(pos, buffer_size, current_attribute),
};
}
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>>) {
let csbi = csbi::get_csbi().unwrap();
// Set srctWindow to the current window size and location.
let mut srct_window = csbi.srWindow;
let csbi = ScreenBuffer::current().unwrap();
let mut window = csbi.info().unwrap().terminal_window();
// Check whether the window is too close to the screen buffer top
if srct_window.Top >= count {
srct_window.Top -= count; // move top down
srct_window.Bottom = count; // move bottom down
if window.top >= count {
window.top -= count; // move top down
window.bottom = count; // move bottom down
let success = kernel::set_console_info(false, &mut srct_window);
if success {
panic!("Something went wrong when scrolling down");
}
Console::new()
.unwrap()
.set_console_info(false, window)
.unwrap();
}
}
fn scroll_down(&self, count: i16, _stdout: &Option<&Arc<TerminalOutput>>) {
let csbi = csbi::get_csbi().unwrap();
// Set srctWindow to the current window size and location.
let mut srct_window = csbi.srWindow;
let csbi = ScreenBuffer::current().unwrap();
let mut window = csbi.info().unwrap().terminal_window();
let buffer_size = csbi.info().unwrap().buffer_size();
// 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
if window.bottom < buffer_size.height - count {
window.top += count; // move top down
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");
}
Console::new()
.unwrap()
.set_console_info(false, window)
.unwrap();
}
}
@ -84,69 +85,68 @@ impl ITerminal for WinApiTerminal {
}
// Get the position of the current console window
let csbi = csbi::get_csbi().unwrap();
let mut success = false;
let screen_buffer = ScreenBuffer::current().unwrap();
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
// 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 {
if current_size.width < window.left + width {
if window.left >= i16::max_value() - 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;
}
if csbi.dwSize.Y < csbi.srWindow.Top + height {
if csbi.srWindow.Top >= i16::max_value() - height {
if current_size.height < window.top + height {
if window.top >= i16::max_value() - 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;
}
if resize_buffer {
success = csbi::set_console_screen_buffer_size(size);
if !success {
if let Err(_) = screen_buffer.set_size(new_size.width, new_size.height) {
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.
fsr_window.Bottom = fsr_window.Top + height;
fsr_window.Right = fsr_window.Left + width;
window.bottom = window.top + height;
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 resize_buffer {
csbi::set_console_screen_buffer_size(csbi.dwSize);
// If we resized the buffer, un-resize it.
if resize_buffer {
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 {
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
);
}
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
);
}
}
@ -160,36 +160,26 @@ impl ITerminal for WinApiTerminal {
}
}
pub fn clear_after_cursor(
pos: (u16, u16),
csbi: CONSOLE_SCREEN_BUFFER_INFO,
_stdout: &Option<&Arc<TerminalOutput>>,
) {
let (mut x, mut y) = pos;
pub fn clear_after_cursor(location: Coord, buffer_size: Size, current_attribute: u16) {
let (mut x, mut y) = (location.x, location.y);
// 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;
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;
let start_location = Coord::new(x, y);
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(
pos: (u16, u16),
csbi: CONSOLE_SCREEN_BUFFER_INFO,
_stdout: &Option<&Arc<TerminalOutput>>,
) {
let (xpos, ypos) = pos;
pub fn clear_before_cursor(location: Coord, buffer_size: Size, current_attribute: u16) {
let (xpos, ypos) = (location.x, location.y);
// one cell after cursor position
let x = 0;
@ -197,129 +187,68 @@ pub fn clear_before_cursor(
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);
let start_location = Coord::new(x, y);
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(
csbi: CONSOLE_SCREEN_BUFFER_INFO,
stdout: &Option<&Arc<TerminalOutput>>,
) {
// position x at start
let x = 0;
// position y at start
let y = 0;
pub fn clear_entire_screen(buffer_size: Size, current_attribute: u16) {
// get sum cells before cursor
let cells_to_write = buffer_size.width as u32 * buffer_size.height as u32;
// location where to start clearing
let start_location = COORD {
X: x as i16,
Y: y as i16,
};
// get sum cells before cursor
let start_location = Coord::new(0, 0);
let cells_to_write = csbi.dwSize.X as u32 * csbi.dwSize.Y as u32;
// clear the entire screen
clear(start_location, cells_to_write, current_attribute);
clear(start_location, cells_to_write);
match stdout {
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);
}
}
// put the cursor back at cell 0,0
let cursor = Cursor::new().unwrap();
cursor.goto(0, 0);
}
pub fn clear_current_line(
pos: (u16, u16),
csbi: CONSOLE_SCREEN_BUFFER_INFO,
stdout: &Option<&Arc<TerminalOutput>>,
) {
// position x at start
let x = 0;
// position y at start
let y = pos.1;
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
let cells_to_write = buffer_size.width as u32;
// clear the whole current line
clear(start_location, cells_to_write, current_attribute);
// put the cursor back at cell 1 on current row
let cursor = Cursor::new().unwrap();
cursor.goto(0, location.y);
}
pub fn clear_until_line(location: Coord, buffer_size: Size, current_attribute: u16) {
let (x, y) = (location.x, location.y);
// location where to start clearing
let start_location = COORD {
X: x as i16,
Y: y as i16,
};
let start_location = Coord::new(x, y);
// 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 1 cell on current row
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);
}
}
// put the cursor back at original cursor position before we did the clearing
let cursor = Cursor::new().unwrap();
cursor.goto(x, y);
}
pub fn clear_until_line(
pos: (u16, u16),
csbi: CONSOLE_SCREEN_BUFFER_INFO,
stdout: &Option<&Arc<TerminalOutput>>,
) {
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
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");
}
fn clear(start_location: Coord, cells_to_write: u32, current_attribute: u16) {
let console = Console::from(Handle::current_out_handle().unwrap());
let _ = console
.fill_whit_character(start_location, cells_to_write, ' ')
.unwrap();
console
.fill_whit_attribute(start_location, cells_to_write, current_attribute)
.unwrap();
}