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