Fixed readline bug (#65)

* Fixed `read_line()` bug Windows
This commit is contained in:
Timon 2018-12-28 05:58:09 -08:00 committed by GitHub
parent e0136891e7
commit ddcda09602
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 201 additions and 219 deletions

View File

@ -14,12 +14,4 @@ extern crate crossterm;
//mod some_types;
//mod terminal;
use crossterm::style::{style, Color};
fn main() {
let styled_object = style("'Red' text on 'White' background")
.with(Color::Rgb { r: 0xFF, g: 0, b: 0 })
.on(Color::Rgb { r: 0xFF, g: 0xFF, b: 0xFF });
println!("{}", styled_object);
}
fn main() { }

View File

@ -42,7 +42,8 @@ fn main() {
thread::sleep(time::Duration::from_millis(100));
count += 1;
}
}).join();
})
.join();
for thread in threads {
thread.join();

View File

@ -121,7 +121,8 @@ fn handle_incoming_logs(more_jobs_rx: SyncFlagRx, queue: WorkQueue<String>) {
}
std::thread::yield_now();
}
}).join();
})
.join();
}
// start different threads that log contiguously.

View File

@ -87,7 +87,10 @@ pub fn write(stdout: &Option<&Arc<TerminalOutput>>, string: String) -> io::Resul
pub fn write_str(stdout: &Option<&Arc<TerminalOutput>>, string: &str) -> io::Result<usize> {
match stdout {
None => match io::stdout().flush() {
Ok(_) => { write!(io::stdout(), "{}", string)?; Ok(string.len()) },
Ok(_) => {
write!(io::stdout(), "{}", string)?;
Ok(string.len())
}
Err(e) => Err(e),
},
Some(output) => output.write_str(string),

View File

@ -44,7 +44,8 @@ impl AlternateScreen {
functions::get_module::<Box<commands::IAlternateScreenCommand + Sync + Send>>(
Box::from(commands::win_commands::ToAlternateScreenCommand::new()),
Box::from(commands::shared_commands::ToAlternateScreenCommand::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let command = Box::from(commands::shared_commands::ToAlternateScreenCommand::new());

View File

@ -21,7 +21,7 @@ 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_handle()?, &mut csbi) }
unsafe { success = GetConsoleScreenBufferInfo(handle::get_current_out_handle()?, &mut csbi) }
if success == 0 {
return Err(io::Error::new(
@ -35,7 +35,7 @@ pub fn get_csbi() -> Result<CONSOLE_SCREEN_BUFFER_INFO> {
/// 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_handle()?;
let handle = handle::get_current_out_handle()?;
let csbi = get_csbi_by_handle(&handle)?;
return Ok((csbi, handle));
@ -59,7 +59,7 @@ pub fn get_csbi_by_handle(handle: &HANDLE) -> Result<CONSOLE_SCREEN_BUFFER_INFO>
/// Set the console screen buffer size
pub fn set_console_screen_buffer_size(size: COORD) -> bool {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
unsafe {
if !kernel::is_true(SetConsoleScreenBufferSize(handle, size)) {

View File

@ -29,7 +29,7 @@ pub fn save_cursor_pos() {
/// get the current cursor position.
pub fn pos() -> (u16, u16) {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
if let Ok(csbi) = csbi::get_csbi_by_handle(&handle) {
(
@ -57,7 +57,7 @@ pub fn set_console_cursor_position(x: i16, y: i16) {
);
}
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
let position = COORD { X: x, Y: y };
@ -80,7 +80,7 @@ pub fn set_console_cursor_position(x: i16, y: i16) {
/// change the cursor visibility.
pub fn cursor_visibility(visable: bool) -> io::Result<()> {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
let cursor_info = CONSOLE_CURSOR_INFO {
dwSize: 100,

View File

@ -1,49 +1,61 @@
//! This module contains some logic for working with the console handle.
use super::*;
use winapi::shared::minwindef::DWORD;
use winapi::um::errhandlingapi::GetLastError;
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_WRITE, GENERIC_READ, GENERIC_WRITE};
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_handle() -> Result<HANDLE> {
let dw: DWORD = 0;
unsafe {
let utf16: Vec<u16> = "CONOUT$\0".encode_utf16().collect();
let utf16_ptr: *const u16 = utf16.as_ptr();
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 = CreateFileW(
let handle = unsafe {
CreateFileW(
utf16_ptr,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
null_mut(),
OPEN_EXISTING,
dw,
0,
null_mut(),
);
)
};
if !is_valid_handle(&handle) {
unsafe {
let error = GetLastError();
return Err(io::Error::new(
io::ErrorKind::Other,
format!(
"Could not get output handle current handle!, error code: {}",
error
).as_ref(),
));
}
}
Ok(handle)
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

View File

@ -30,7 +30,7 @@ pub fn get_console_mode(handle: &HANDLE, current_mode: &mut u32) -> bool {
/// Change the console text attribute.
pub fn set_console_text_attribute(value: u16) -> bool {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
unsafe {
return is_true(SetConsoleTextAttribute(handle, value));
@ -39,7 +39,7 @@ pub fn set_console_text_attribute(value: u16) -> bool {
/// Change console info.
pub fn set_console_info(absolute: bool, rect: &SMALL_RECT) -> bool {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
let absolute = match absolute {
true => 1,

View File

@ -5,6 +5,7 @@ pub mod csbi;
pub mod cursor;
pub mod handle;
pub mod kernel;
pub mod reading;
pub mod terminal;
pub mod writing;

View File

@ -1,84 +1,73 @@
//use { Context, ScreenManager };
//use std::rc::Rc;
//use std::sync::Mutex;
//
//use winapi::um::consoleapi::ReadConsoleW;
//use winapi::um::winnt::HANDLE;
//use winapi::um::wincon::{ COORD, PSMALL_RECT, ReadConsoleOutputA, CHAR_INFO, };
//use winapi::shared::minwindef::{ DWORD, LPDWORD, LPVOID };
//use winapi::shared::ntdef::NULL;
//
//use super::kernel;
//use winapi::ctypes::c_void;
//
//pub fn read(buf: &mut [u8], stdout: &Rc<Mutex<ScreenManager>>) {
//// // Read more if the buffer is empty
//// let mut utf16: Vec<u16> = Vec::new();
//// let mut num: DWORD = 0;
////
//// let handle = kernel::get_current_handle(&stdout);
////
//// unsafe {
//// ReadConsoleW(handle,
//// utf16.as_mut_ptr() as LPVOID,
//// utf16.len() as u32,
//// &mut num as LPDWORD,
//// ptr::mut_null())
//// };
////
//// utf16.truncate(num as uint);
//// let utf8 = match from_utf16(utf16.as_slice()) {
//// Some(utf8) => utf8.into_bytes(),
//// None => {}
//// };
////
//// panic!(utf8);
//
//}
//
//pub fn read_line(stdout: &Rc<Mutex<ScreenManager>>) -> ::std::io::Result<String>
//{
// const BUFFER_LENGHT: u32 = 1024;
// let mut buffer: &mut [CHAR_INFO; BUFFER_LENGHT as usize] = unsafe {::std::mem::zeroed() };
//
// let handle = kernel::get_current_handle(&stdout);
//
// let mut dw_mode: DWORD = 0;
// let console_mode = kernel::get_console_mode(&handle, &mut dw_mode);
//
// let ptr = buffer.as_ptr() as *const _ as *mut c_void;
// let mut chars_read: u32 = 0;
//
// panic!();
// unsafe
// {
// ReadConsoleW(handle, ptr, BUFFER_LENGHT , &mut chars_read, unsafe {::std::mem::zeroed() });
// }
//
// Ok(String::new())
//}
//
///// Read the console outptut.
//pub fn read_console_output(
// read_buffer: &HANDLE,
// copy_buffer: &mut [CHAR_INFO; 160],
// buffer_size: COORD,
// buffer_coord: COORD,
// source_buffer: PSMALL_RECT,
//) {
//
//
// unsafe {
// if !kernel::is_true(
// ReadConsoleOutputA(
// *read_buffer, // screen buffer to read from
// copy_buffer.as_mut_ptr(), // buffer to copy into
// buffer_size, // col-row size of chiBuffer
// buffer_coord, // top left dest. cell in chiBuffer
// source_buffer,
// ), // screen buffer source rectangle
// ) {
// panic!("Cannot read console output");
// }
// }
//}
use super::handle::get_current_in_handle;
use std::io::{self, Error, Result};
use std::{
mem::{self, zeroed},
ptr::{null, null_mut},
};
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},
};
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 mut utf16 = vec![0u16; 0x1000];
let mut num = 0;
let mut input_control = readconsole_input_control(CTRL_Z_MASK);
unsafe {
ReadConsoleW(
handle,
utf16.as_mut_ptr() as LPVOID,
utf16.len() as u32,
&mut num,
&mut input_control as PCONSOLE_READCONSOLE_CONTROL,
)
};
utf16.truncate(num as usize);
let mut data = match String::from_utf16(&utf16) {
Ok(utf8) => utf8.into_bytes(),
Err(..) => return Err(invalid_encoding()),
};
if let Some(&last_byte) = data.last() {
if last_byte == CTRL_Z {
data.pop();
}
};
let a = &data
.into_iter()
.filter(|&x| x != 10 || x != 13)
.collect::<Vec<u8>>();
buf.write(a);
Ok(num as usize)
}
pub fn readconsole_input_control(wakeup_mask: ULONG) -> CONSOLE_READCONSOLE_CONTROL {
CONSOLE_READCONSOLE_CONTROL {
nLength: mem::size_of::<CONSOLE_READCONSOLE_CONTROL>() as ULONG,
nInitialChars: 0,
dwCtrlWakeupMask: wakeup_mask,
dwControlKeyState: 0,
}
}
fn invalid_encoding() -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, "text was not valid unicode")
}
const CTRL_Z: u8 = 0x1A;
const CTRL_Z_MASK: ULONG = 0x4000000; //1 << 0x1A

View File

@ -19,7 +19,7 @@ pub fn fill_console_output_character(
start_location: COORD,
cells_to_write: u32,
) -> bool {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
unsafe {
// fill the cells in console with blanks

View File

@ -12,17 +12,20 @@ mod common;
mod kernel;
mod modules;
pub use modules::terminal;
pub use modules::cursor;
pub use modules::input;
pub use modules::output;
pub use modules::style;
pub use modules::terminal;
pub use self::style::{color, style, Color, ColorType, Attribute, TerminalColor, ObjectStyle, StyledObject, DisplayableObject};
pub use self::cursor::{cursor, TerminalCursor};
pub use self::input::{input, TerminalInput, AsyncReader, KeyEvent};
pub use self::terminal::{terminal, Terminal};
pub use self::input::{input, AsyncReader, KeyEvent, TerminalInput};
pub use self::output::TerminalOutput;
pub use self::style::{
color, style, Attribute, Color, ColorType, DisplayableObject, ObjectStyle, StyledObject,
TerminalColor,
};
pub use self::terminal::{terminal, Terminal};
pub use common::screen::{AlternateScreen, Screen};
pub use common::Crossterm;

View File

@ -40,7 +40,8 @@ impl<'stdout> TerminalCursor<'stdout> {
let cursor = functions::get_module::<Box<ITerminalCursor + Sync + Send>>(
WinApiCursor::new(),
AnsiCursor::new(),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let cursor = AnsiCursor::new() as Box<ITerminalCursor + Sync + Send>;
@ -72,7 +73,8 @@ impl<'stdout> TerminalCursor<'stdout> {
let cursor = functions::get_module::<Box<ITerminalCursor + Sync + Send>>(
WinApiCursor::new(),
AnsiCursor::new(),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let cursor = AnsiCursor::new() as Box<ITerminalCursor + Sync + Send>;

View File

@ -2,8 +2,8 @@
//! Like reading a line, reading a character and reading asynchronously.
use super::*;
use Screen;
use std::{thread, time::Duration};
use Screen;
/// Struct that stores a platform-specific implementation for input related actions.
///
@ -78,6 +78,9 @@ impl<'stdout> TerminalInput<'stdout> {
/// Read one line from the user input.
///
/// Note that this function only works when rawscreen is not turned on.
/// When you do want to read a line in raw mode please checkout `read_async` or `read_async_until`.
///
/// ```rust
/// let input = input();
/// match input.read_line() {
@ -86,7 +89,17 @@ impl<'stdout> TerminalInput<'stdout> {
/// }
/// ```
pub fn read_line(&self) -> io::Result<String> {
self.terminal_input.read_line(&self.stdout)
if let Some(stdout) = self.stdout {
if stdout.is_in_raw_mode {
return Err(Error::new(ErrorKind::Other, "Crossterm does not support readline in raw mode this should be done instead whit `read_async` or `read_async_until`"));
}
}
let mut rv = String::new();
io::stdin().read_line(&mut rv)?;
let len = rv.trim_right_matches(&['\r', '\n'][..]).len();
rv.truncate(len);
Ok(rv)
}
/// Read one character from the user input
@ -198,15 +211,21 @@ impl<'stdout> TerminalInput<'stdout> {
let pressed_key: Option<Result<u8, Error>> = stdin.next();
match pressed_key {
Some(Ok(value)) => {
match key_event {
KeyEvent::OnKeyPress(ascii_code) => if value == ascii_code { break; },
KeyEvent::OnEnter => if value == b'\r' { break; },
KeyEvent::OnAnyKeyPress => {
Some(Ok(value)) => match key_event {
KeyEvent::OnKeyPress(ascii_code) => {
if value == ascii_code {
break;
}
}
}
KeyEvent::OnEnter => {
if value == b'\r' {
break;
}
}
KeyEvent::OnAnyKeyPress => {
break;
}
},
_ => {}
}

View File

@ -13,7 +13,7 @@ use self::unix_input::UnixInput;
#[cfg(target_os = "windows")]
use self::windows_input::WindowsInput;
pub use self::input::{input, from_screen, TerminalInput};
pub use self::input::{from_screen, input, TerminalInput};
use std::io::{self, Error, ErrorKind, Read};
use std::sync::{mpsc, Arc};
@ -29,8 +29,6 @@ use TerminalOutput;
/// This trait is implemented for Windows and UNIX systems.
/// Unix is using the 'TTY' and windows is using 'libc' C functions to read the input.
trait ITerminalInput {
/// Read one line from the user input
fn read_line(&self, stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<String>;
/// Read one character from the user input
fn read_char(&self, stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<char>;
/// Read the input asynchronously from the user.

View File

@ -15,14 +15,6 @@ impl UnixInput {
}
impl ITerminalInput for UnixInput {
fn read_line(&self, __stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<String> {
let mut rv = String::new();
io::stdin().read_line(&mut rv)?;
let len = rv.trim_right_matches(&['\r', '\n'][..]).len();
rv.truncate(len);
Ok(rv)
}
fn read_char(&self, __stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<char> {
read_char()
}

View File

@ -2,56 +2,20 @@
use super::*;
use winapi::um::winnt::INT;
use kernel::windows_kernel::reading::read_line;
use std::char;
use std::thread;
use winapi::um::winnt::INT;
pub struct WindowsInput;
impl WindowsInput {
pub fn new() -> WindowsInput {
WindowsInput {}
WindowsInput
}
}
impl ITerminalInput for WindowsInput {
fn read_line(&self, stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<String> {
let mut chars: Vec<char> = Vec::new();
loop {
let is_raw_screen = match stdout {
Some(output) => output.is_in_raw_mode,
None => false,
};
// _getwch is without echo and _getwche is with echo
let pressed_char = unsafe {
if is_raw_screen {
_getwch()
} else {
_getwche()
}
};
// if 0 or 0xe0 we need to listen again because the next key will be an special key
if pressed_char != 0 || pressed_char != 0xe0 {
match char::from_u32(pressed_char as u32) {
Some(c) => {
if is_line_end(c) {
break;
} else {
chars.push(c);
}
}
None => panic!("Some error needs to be returned"),
};
}
}
return Ok(chars.into_iter().collect());
}
fn read_char(&self, stdout: &Option<&Arc<TerminalOutput>>) -> io::Result<char> {
let is_raw_screen = match stdout {
Some(output) => output.is_in_raw_mode,

View File

@ -15,8 +15,8 @@ use self::winapi_output::WinApiOutput;
pub use self::output::TerminalOutput;
use std::io;
use super::functions;
use std::io;
/// This trait defines represents an stdout of an screen.
/// This trait can be implemented so that an concrete implementation of the IStdout can forfill

View File

@ -37,7 +37,8 @@ impl TerminalOutput {
functions::get_module::<Box<IStdout + Send + Sync>>(
Box::from(WinApiOutput::new()),
Box::from(AnsiOutput::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let stdout = Box::from(AnsiOutput::new()) as Box<IStdout + Send + Sync>;
@ -86,7 +87,8 @@ impl Default for TerminalOutput {
let stdout = functions::get_module::<Box<IStdout + Send + Sync>>(
Box::from(WinApiOutput::new()),
Box::from(AnsiOutput::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let stdout = Box::from(AnsiOutput::new()) as Box<IStdout + Send + Sync>;

View File

@ -54,11 +54,13 @@ fn write_str_ansi() {
fn is_valid_write(result: ::std::io::Result<usize>, str_length: usize) {
match result {
Err(_) => assert!(false),
Ok(length) => if str_length == length {
assert!(true)
} else {
assert!(false)
},
Ok(length) => {
if str_length == length {
assert!(true)
} else {
assert!(false)
}
}
};
}

View File

@ -18,7 +18,7 @@ impl IStdout for WinApiOutput {
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
let handle = handle::get_current_handle().unwrap();
let handle = handle::get_current_out_handle().unwrap();
writing::write_char_buffer(&handle, buf)
}

View File

@ -39,7 +39,8 @@ impl<'stdout> TerminalColor<'stdout> {
let color = functions::get_module::<Box<ITerminalColor + Sync + Send>>(
Box::from(WinApiColor::new()),
Box::from(AnsiColor::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let color = Box::from(AnsiColor::new()) as Box<ITerminalColor + Sync + Send>;
@ -71,7 +72,8 @@ impl<'stdout> TerminalColor<'stdout> {
let color = functions::get_module::<Box<ITerminalColor + Sync + Send>>(
Box::from(WinApiColor::new()),
Box::from(AnsiColor::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let color = Box::from(AnsiColor::new()) as Box<ITerminalColor + Sync + Send>;

View File

@ -111,11 +111,7 @@ pub enum Color {
Grey,
White,
Rgb {
r: u8,
g: u8,
b: u8,
},
Rgb { r: u8, g: u8, b: u8 },
AnsiValue(u8),
}

View File

@ -101,8 +101,8 @@ impl ITerminalColor for WinApiColor {
Color::White => fg_intensity | fg_red | fg_green | fg_blue,
/* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/
Color::Rgb{ r: _, g: _, b: _ } => { 0 }
Color::AnsiValue(_val) => { 0 }
Color::Rgb { r: _, g: _, b: _ } => 0,
Color::AnsiValue(_val) => 0,
};
}
ColorType::Background => {
@ -124,8 +124,8 @@ impl ITerminalColor for WinApiColor {
Color::White => bg_intensity | bg_red | bg_green | bg_blue,
/* WinApi will be used for systems that do not support ANSI, those are windows version less then 10. RGB and 255 (AnsiBValue) colors are not supported in that case.*/
Color::Rgb{ r: _, g: _, b: _ } => { 0 }
Color::AnsiValue(_val) => { 0 }
Color::Rgb { r: _, g: _, b: _ } => 0,
Color::AnsiValue(_val) => 0,
};
}
};

View File

@ -13,7 +13,7 @@ use self::ansi_terminal::AnsiTerminal;
#[cfg(target_os = "windows")]
use self::winapi_terminal::WinApiTerminal;
pub use self::terminal::{terminal, from_screen, Terminal};
pub use self::terminal::{from_screen, terminal, Terminal};
use super::functions;
use std::sync::Arc;

View File

@ -32,7 +32,8 @@ impl<'stdout> Terminal<'stdout> {
let terminal = functions::get_module::<Box<ITerminal + Sync + Send>>(
Box::new(WinApiTerminal::new()),
Box::new(AnsiTerminal::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let terminal = Box::from(AnsiTerminal::new()) as Box<ITerminal + Sync + Send>;
@ -64,7 +65,8 @@ impl<'stdout> Terminal<'stdout> {
let terminal = functions::get_module::<Box<ITerminal + Sync + Send>>(
Box::new(WinApiTerminal::new()),
Box::new(AnsiTerminal::new()),
).unwrap();
)
.unwrap();
#[cfg(not(target_os = "windows"))]
let terminal = Box::from(AnsiTerminal::new()) as Box<ITerminal + Sync + Send>;