1 use std::borrow::ToOwned;
2 use std::io::{self, Error, Result};
3 use std::str;
4 
5 use winapi::ctypes::c_void;
6 use winapi::shared::minwindef::DWORD;
7 use winapi::shared::ntdef::NULL;
8 use winapi::um::consoleapi::{GetNumberOfConsoleInputEvents, ReadConsoleInputW, WriteConsoleW};
9 use winapi::um::wincon::{
10     FillConsoleOutputAttribute, FillConsoleOutputCharacterA, GetLargestConsoleWindowSize,
11     SetConsoleTextAttribute, SetConsoleWindowInfo, COORD, INPUT_RECORD, SMALL_RECT,
12 };
13 
14 use super::{is_true, Coord, Handle, HandleType, InputRecord, WindowPositions};
15 
16 /// Could be used to do some basic things with the console.
17 pub struct Console {
18     handle: Handle,
19 }
20 
21 impl Console {
22     /// Create new instance of `Console`.
23     ///
24     /// This created instance will use the default output handle (STD_OUTPUT_HANDLE) as handle for the function call it wraps.
output() -> Result<Console>25     pub fn output() -> Result<Console> {
26         Ok(Console {
27             handle: Handle::new(HandleType::OutputHandle)?,
28         })
29     }
30 
31     /// Sets the attributes of characters written to the console screen buffer by the WriteFile or WriteConsole function, or echoed by the ReadFile or ReadConsole function.
32     /// This function affects text written after the function call.
33     ///
34     /// parameter: [wAttributes]
35     /// Wraps the underlying function call: [SetConsoleTextAttribute]
36     /// link: [https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute]
set_text_attribute(&self, value: u16) -> Result<()>37     pub fn set_text_attribute(&self, value: u16) -> Result<()> {
38         unsafe {
39             if !is_true(SetConsoleTextAttribute(*self.handle, value)) {
40                 return Err(Error::last_os_error());
41             }
42         }
43         Ok(())
44     }
45 
46     /// Sets the current size and position of a console screen buffer's window.
47     ///
48     /// Wraps the underlying function call: [SetConsoleTextAttribute]
49     /// link: [https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute]
set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()>50     pub fn set_console_info(&self, absolute: bool, rect: WindowPositions) -> Result<()> {
51         let absolute = match absolute {
52             true => 1,
53             false => 0,
54         };
55         let a = SMALL_RECT::from(rect);
56 
57         unsafe {
58             if !is_true(SetConsoleWindowInfo(*self.handle, absolute, &a)) {
59                 return Err(Error::last_os_error());
60             }
61         }
62 
63         Ok(())
64     }
65 
66     /// Writes a character to the console screen buffer a specified number of times, beginning at the specified coordinates
67     ///
68     /// Wraps the underlying function call: [FillConsoleOutputCharacterA]
69     /// link: [https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputcharacter]
fill_whit_character( &self, start_location: Coord, cells_to_write: u32, filling_char: char, ) -> Result<u32>70     pub fn fill_whit_character(
71         &self,
72         start_location: Coord,
73         cells_to_write: u32,
74         filling_char: char,
75     ) -> Result<u32> {
76         let mut chars_written = 0;
77         unsafe {
78             // fill the cells in console with blanks
79             if !is_true(FillConsoleOutputCharacterA(
80                 *self.handle,
81                 filling_char as i8,
82                 cells_to_write,
83                 COORD::from(start_location),
84                 &mut chars_written,
85             )) {
86                 return Err(Error::last_os_error());
87             }
88 
89             Ok(chars_written)
90         }
91     }
92 
93     /// Sets the character attributes for a specified number of character cells, beginning at the specified coordinates in a screen buffer.
94     ///
95     /// Wraps the underlying function call: [FillConsoleOutputAttribute]
96     /// link: [https://docs.microsoft.com/en-us/windows/console/fillconsoleoutputattribute]
fill_whit_attribute( &self, start_location: Coord, cells_to_write: u32, dw_attribute: u16, ) -> Result<u32>97     pub fn fill_whit_attribute(
98         &self,
99         start_location: Coord,
100         cells_to_write: u32,
101         dw_attribute: u16,
102     ) -> Result<u32> {
103         let mut cells_written = 0;
104         // Get the position of the current console window
105         unsafe {
106             if !is_true(FillConsoleOutputAttribute(
107                 *self.handle,
108                 dw_attribute,
109                 cells_to_write,
110                 COORD::from(start_location),
111                 &mut cells_written,
112             )) {
113                 return Err(Error::last_os_error());
114             }
115         }
116 
117         Ok(cells_written)
118     }
119 
120     /// Retrieves the size of the largest possible console window, based on the current text and the size of the display.
121     ///
122     /// Wraps the underlying function call: [GetLargestConsoleWindowSize]
123     /// link: [https://docs.microsoft.com/en-us/windows/console/getlargestconsolewindowsize]
largest_window_size(&self) -> Coord124     pub fn largest_window_size(&self) -> Coord {
125         Coord::from(unsafe { GetLargestConsoleWindowSize(*self.handle) })
126     }
127 
128     /// Writes a character string to a console screen buffer beginning at the current cursor location.
129     ///
130     /// Wraps the underlying function call: [WriteConsoleW]
131     /// link: [https://docs.microsoft.com/en-us/windows/console/writeconsole]
write_char_buffer(&self, buf: &[u8]) -> Result<usize>132     pub fn write_char_buffer(&self, buf: &[u8]) -> Result<usize> {
133         // get string from u8[] and parse it to an c_str
134         let utf8 = match str::from_utf8(buf) {
135             Ok(string) => string,
136             Err(_) => {
137                 return Err(io::Error::new(
138                     io::ErrorKind::Other,
139                     "Could not parse to utf8 string",
140                 ));
141             }
142         };
143 
144         let utf16: Vec<u16> = utf8.encode_utf16().collect();
145         let utf16_ptr: *const c_void = utf16.as_ptr() as *const _ as *const c_void;
146 
147         let mut cells_written: u32 = 0;
148         // write to console
149         unsafe {
150             if !is_true(WriteConsoleW(
151                 *self.handle,
152                 utf16_ptr,
153                 utf16.len() as u32,
154                 &mut cells_written,
155                 NULL,
156             )) {
157                 return Err(io::Error::last_os_error());
158             }
159         }
160         Ok(utf8.as_bytes().len())
161     }
162 
read_single_input_event(&self) -> Result<InputRecord>163     pub fn read_single_input_event(&self) -> Result<InputRecord> {
164         let mut buf: Vec<INPUT_RECORD> = Vec::with_capacity(1);
165         let mut size = 0;
166 
167         return Ok(self.read_input(&mut buf, 1, &mut size)?.1[0].to_owned());
168     }
169 
read_console_input(&self) -> Result<(u32, Vec<InputRecord>)>170     pub fn read_console_input(&self) -> Result<(u32, Vec<InputRecord>)> {
171         let buf_len = self.number_of_console_input_events()?;
172 
173         // Fast-skipping all the code below if there is nothing to read at all
174         if buf_len == 0 {
175             return Ok((0, vec![]));
176         }
177 
178         let mut buf: Vec<INPUT_RECORD> = Vec::with_capacity(buf_len as usize);
179         let mut size = 0;
180 
181         self.read_input(&mut buf, buf_len, &mut size)
182     }
183 
number_of_console_input_events(&self) -> Result<u32>184     pub fn number_of_console_input_events(&self) -> Result<u32> {
185         let mut buf_len: DWORD = 0;
186         if !is_true(unsafe { GetNumberOfConsoleInputEvents(*self.handle, &mut buf_len) }) {
187             return Err(Error::last_os_error());
188         }
189 
190         Ok(buf_len)
191     }
192 
read_input( &self, buf: &mut Vec<INPUT_RECORD>, buf_len: u32, bytes_written: &mut u32, ) -> Result<(u32, Vec<InputRecord>)>193     fn read_input(
194         &self,
195         buf: &mut Vec<INPUT_RECORD>,
196         buf_len: u32,
197         bytes_written: &mut u32,
198     ) -> Result<(u32, Vec<InputRecord>)> {
199         if !is_true(unsafe {
200             ReadConsoleInputW(*self.handle, buf.as_mut_ptr(), buf_len, bytes_written)
201         }) {
202             return Err(Error::last_os_error());
203         } else {
204             unsafe {
205                 buf.set_len(buf_len as usize);
206             }
207         }
208 
209         Ok((
210             buf_len,
211             buf[..(buf_len as usize)]
212                 .iter()
213                 .map(|x| InputRecord::from(*x))
214                 .collect::<Vec<InputRecord>>(),
215         ))
216     }
217 }
218 
219 impl From<Handle> for Console {
220     /// Create a `Console` instance who's functions will be executed on the the given `Handle`
from(handle: Handle) -> Self221     fn from(handle: Handle) -> Self {
222         Console { handle }
223     }
224 }
225