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