diff --git a/Cargo.toml b/Cargo.toml index 7f1ebb8..139213f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/docs/some_common_bugs.txt b/docs/some_common_bugs.txt new file mode 100644 index 0000000..5b5f519 --- /dev/null +++ b/docs/some_common_bugs.txt @@ -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. \ No newline at end of file diff --git a/examples/color/mod.rs b/examples/color/mod.rs index 5eec6e1..172ee6c 100644 --- a/examples/color/mod.rs +++ b/examples/color/mod.rs @@ -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. diff --git a/examples/cursor/mod.rs b/examples/cursor/mod.rs index c50f3be..ead5d9e 100644 --- a/examples/cursor/mod.rs +++ b/examples/cursor/mod.rs @@ -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); } diff --git a/examples/examples.rs b/examples/examples.rs index f58a8f7..e29f7f5 100644 --- a/examples/examples.rs +++ b/examples/examples.rs @@ -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() { } \ No newline at end of file +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(); +} diff --git a/examples/input/keyboard/async_input.rs b/examples/input/keyboard/async_input.rs index 4de32e0..807dda4 100644 --- a/examples/input/keyboard/async_input.rs +++ b/examples/input/keyboard/async_input.rs @@ -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(); diff --git a/examples/input/keyboard/input.rs b/examples/input/keyboard/input.rs index c391ced..d176521 100644 --- a/examples/input/keyboard/input.rs +++ b/examples/input/keyboard/input.rs @@ -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() { diff --git a/examples/program_examples/snake/src/main.rs b/examples/program_examples/snake/src/main.rs index d2b0d43..90b6c4c 100644 --- a/examples/program_examples/snake/src/main.rs +++ b/examples/program_examples/snake/src/main.rs @@ -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(); } diff --git a/examples/terminal/terminal.rs b/examples/terminal/terminal.rs index 8e474a2..644b803 100644 --- a/examples/terminal/terminal.rs +++ b/examples/terminal/terminal.rs @@ -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(); } diff --git a/src/common/commands/mod.rs b/src/common/commands/mod.rs index afb595c..ac029b1 100644 --- a/src/common/commands/mod.rs +++ b/src/common/commands/mod.rs @@ -18,8 +18,8 @@ pub trait IStateCommand { } pub trait IEnableAnsiCommand { - fn enable(&self) -> bool; - fn disable(&self) -> bool; + fn enable(&self) -> io::Result; + fn disable(&self) -> io::Result<()>; } // This trait provides an interface for switching to alternate screen and back. diff --git a/src/common/commands/win_commands.rs b/src/common/commands/win_commands.rs index 4c89620..34dcb12 100644 --- a/src/common/commands/win_commands.rs +++ b/src/common/commands/win_commands.rs @@ -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 { // 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(()) } } diff --git a/src/common/functions.rs b/src/common/functions.rs index 8f6c7c7..1d0e43c 100644 --- a/src/common/functions.rs +++ b/src/common/functions.rs @@ -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(winapi_impl: T, unix_impl: T) -> Option { let mut term: Option = 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); diff --git a/src/kernel/windows_kernel/ansi_support.rs b/src/kernel/windows_kernel/ansi_support.rs index cef8c75..29aa96d 100644 --- a/src/kernel/windows_kernel/ansi_support.rs +++ b/src/kernel/windows_kernel/ansi_support.rs @@ -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); diff --git a/src/kernel/windows_kernel/csbi.rs b/src/kernel/windows_kernel/csbi.rs deleted file mode 100644 index 219d6cc..0000000 --- a/src/kernel/windows_kernel/csbi.rs +++ /dev/null @@ -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 { - 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 { - 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::() 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 }; -} diff --git a/src/kernel/windows_kernel/cursor.rs b/src/kernel/windows_kernel/cursor.rs index 0036ff7..f549065 100644 --- a/src/kernel/windows_kernel/cursor.rs +++ b/src/kernel/windows_kernel/cursor.rs @@ -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 >= ::max_value() { - panic!( - "Argument Out of Range Exception when setting cursor position to X: {}", - x - ); +impl Cursor { + pub fn new() -> io::Result { + Ok(Cursor { + screen_buffer: ScreenBuffer::from(Handle::new(HandleType::CurrentOutputHandle)?), + }) } - if y < 0 || y >= ::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 { + 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 >= ::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 >= ::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 for Cursor { + fn from(handle: Handle) -> Self { + Cursor { + screen_buffer: ScreenBuffer::from(handle), + } + } +} + +impl From for Cursor { + fn from(handle: HANDLE) -> Self { + Cursor { + screen_buffer: ScreenBuffer::from(handle), + } } - Ok(()) } diff --git a/src/kernel/windows_kernel/handle.rs b/src/kernel/windows_kernel/handle.rs deleted file mode 100644 index b3cdd9b..0000000 --- a/src/kernel/windows_kernel/handle.rs +++ /dev/null @@ -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 { - let utf16: Vec = "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 { - let utf16: Vec = "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 { - 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 { - 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 - } -} diff --git a/src/kernel/windows_kernel/kernel.rs b/src/kernel/windows_kernel/kernel.rs deleted file mode 100644 index 9c50500..0000000 --- a/src/kernel/windows_kernel/kernel.rs +++ /dev/null @@ -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; - } -} diff --git a/src/kernel/windows_kernel/mod.rs b/src/kernel/windows_kernel/mod.rs index 13c3f06..7588c8c 100644 --- a/src/kernel/windows_kernel/mod.rs +++ b/src/kernel/windows_kernel/mod.rs @@ -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); } diff --git a/src/kernel/windows_kernel/reading.rs b/src/kernel/windows_kernel/reading.rs index 1d54a62..a17f12e 100644 --- a/src/kernel/windows_kernel/reading.rs +++ b/src/kernel/windows_kernel/reading.rs @@ -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) -> io::Result { - let handle = get_current_in_handle()?; + let handle = Handle::current_in_handle()?; let mut utf16 = vec![0u16; 0x1000]; let mut num = 0; diff --git a/src/kernel/windows_kernel/terminal.rs b/src/kernel/windows_kernel/terminal.rs deleted file mode 100644 index 8dce784..0000000 --- a/src/kernel/windows_kernel/terminal.rs +++ /dev/null @@ -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); -} diff --git a/src/kernel/windows_kernel/writing.rs b/src/kernel/windows_kernel/writing.rs index 40565a4..feaa176 100644 --- a/src/kernel/windows_kernel/writing.rs +++ b/src/kernel/windows_kernel/writing.rs @@ -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 { +pub fn write_char_buffer(handle: &HANDLE, buf: &[u8]) -> io::Result { // 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 { + // 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), } } diff --git a/src/lib.rs b/src/lib.rs index ee8e022..a7e8f03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/modules/cursor/winapi_cursor.rs b/src/modules/cursor/winapi_cursor.rs index 10b00d4..8ddcec3 100644 --- a/src/modules/cursor/winapi_cursor.rs +++ b/src/modules/cursor/winapi_cursor.rs @@ -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 { - Box::from(WinApiCursor {}) + Box::from(WinApiCursor) } } impl ITerminalCursor for WinApiCursor { fn goto(&self, x: u16, y: u16, _stdout: &Option<&Arc>) { - 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>) { @@ -45,19 +47,19 @@ impl ITerminalCursor for WinApiCursor { } fn save_position(&self, _stdout: &Option<&Arc>) { - cursor::save_cursor_pos(); + Cursor::save_cursor_pos(); } fn reset_position(&self, _stdout: &Option<&Arc>) { - cursor::reset_to_saved_position(); + Cursor::reset_to_saved_position(); } fn hide(&self, _stdout: &Option<&Arc>) { - cursor::cursor_visibility(false); + Cursor::new().unwrap().set_visibility(false); } fn show(&self, _stdout: &Option<&Arc>) { - cursor::cursor_visibility(true); + Cursor::new().unwrap().set_visibility(true); } fn blink(&self, _blink: bool, _stdout: &Option<&Arc>) {} diff --git a/src/modules/input/windows_input.rs b/src/modules/input/windows_input.rs index e4654e4..d6a1daa 100644 --- a/src/modules/input/windows_input.rs +++ b/src/modules/input/windows_input.rs @@ -2,7 +2,6 @@ use super::*; -use kernel::windows_kernel::reading::read_line; use std::char; use std::thread; use winapi::um::winnt::INT; diff --git a/src/modules/output/winapi_output.rs b/src/modules/output/winapi_output.rs index 346eb6d..c8afe7c 100644 --- a/src/modules/output/winapi_output.rs +++ b/src/modules/output/winapi_output.rs @@ -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 { - let handle = handle::get_current_out_handle().unwrap(); + let handle = Handle::current_out_handle().unwrap(); writing::write_char_buffer(&handle, buf) } diff --git a/src/modules/style/winapi_color.rs b/src/modules/style/winapi_color.rs index 72a5759..e48cccf 100644 --- a/src/modules/style/winapi_color.rs +++ b/src/modules/style/winapi_color.rs @@ -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>) { + // 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::().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>) { + // 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::().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>) { - 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; diff --git a/src/modules/terminal/test.rs b/src/modules/terminal/test.rs index 8d153df..65208f3 100644 --- a/src/modules/terminal/test.rs +++ b/src/modules/terminal/test.rs @@ -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); } } diff --git a/src/modules/terminal/winapi_terminal.rs b/src/modules/terminal/winapi_terminal.rs index 87b2e3f..0c5f94a 100644 --- a/src/modules/terminal/winapi_terminal.rs +++ b/src/modules/terminal/winapi_terminal.rs @@ -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>) { - let csbi = csbi::get_csbi().unwrap(); - let pos = TerminalCursor::new().pos(); + fn clear(&self, clear_type: ClearType, _stdout: &Option<&Arc>) { + 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>) -> (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>) { - 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>) { - 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>, -) { - 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>, -) { - 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>, -) { - // 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>, -) { - // 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>, -) { - 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(); }