1 // Licensed under the Apache License, Version 2.0
2 // <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
4 // All files in the project carrying such notice may not be copied, modified, or distributed
5 // except according to those terms.
6 use error::{Error, Result};
7 use handle::Handle;
8 use std::{
9     mem::{size_of_val, zeroed},
10     os::windows::io::FromRawHandle,
11     ptr::{null, null_mut},
12 };
13 use wide::ToWide;
14 use winapi::{
15     um::{
16         consoleapi::{AllocConsole, GetConsoleCP, GetConsoleOutputCP, GetNumberOfConsoleInputEvents, ReadConsoleInputW},
17         fileapi::{CreateFileW, OPEN_EXISTING},
18         handleapi::INVALID_HANDLE_VALUE,
19         wincon::{AttachConsole, CHAR_INFO, CONSOLE_FONT_INFOEX, CONSOLE_SCREEN_BUFFER_INFO, CONSOLE_SCREEN_BUFFER_INFOEX, CONSOLE_TEXTMODE_BUFFER, COORD, CreateConsoleScreenBuffer, FlushConsoleInputBuffer, FOCUS_EVENT, FreeConsole, GetConsoleScreenBufferInfo, GetConsoleScreenBufferInfoEx, GetCurrentConsoleFont, INPUT_RECORD, KEY_EVENT, MENU_EVENT, MOUSE_EVENT, SetConsoleActiveScreenBuffer, SetConsoleCP, SetConsoleOutputCP, SetConsoleScreenBufferInfoEx, SMALL_RECT, WINDOW_BUFFER_SIZE_EVENT, WriteConsoleOutputW},
20         winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE, HANDLE},
21     },
22     shared::minwindef::{DWORD, FALSE},
23 };
24 
25 pub struct ScreenBuffer(Handle);
26 impl ScreenBuffer {
new() -> Result<ScreenBuffer>27     pub fn new() -> Result<ScreenBuffer> {
28         let handle = unsafe { CreateConsoleScreenBuffer(
29             GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
30             null(), CONSOLE_TEXTMODE_BUFFER, null_mut(),
31         )};
32         if handle == INVALID_HANDLE_VALUE { return Error::last() }
33         unsafe { Ok(ScreenBuffer(Handle::new(handle))) }
34     }
35     /// Gets the actual active console screen buffer
from_conout() -> Result<ScreenBuffer>36     pub fn from_conout() -> Result<ScreenBuffer> {
37         let handle = unsafe { CreateFileW(
38             "CONOUT$".to_wide_null().as_ptr(), GENERIC_READ | GENERIC_WRITE,
39             FILE_SHARE_READ, null_mut(), OPEN_EXISTING,
40             0, null_mut(),
41         )};
42         if handle == INVALID_HANDLE_VALUE { return Error::last() }
43         unsafe { Ok(ScreenBuffer(Handle::new(handle))) }
44     }
set_active(&self) -> Result<()>45     pub fn set_active(&self) -> Result<()> {
46         let res = unsafe { SetConsoleActiveScreenBuffer(*self.0) };
47         if res == 0 { return Error::last() }
48         Ok(())
49     }
info(&self) -> Result<ScreenBufferInfo>50     pub fn info(&self) -> Result<ScreenBufferInfo> {
51         let mut info = ScreenBufferInfo(unsafe { zeroed() });
52         let res = unsafe { GetConsoleScreenBufferInfo(*self.0, &mut info.0) };
53         if res == 0 { return Error::last() }
54         Ok(info)
55     }
info_ex(&self) -> Result<ScreenBufferInfoEx>56     pub fn info_ex(&self) -> Result<ScreenBufferInfoEx> {
57         let mut info: CONSOLE_SCREEN_BUFFER_INFOEX = unsafe { zeroed() };
58         info.cbSize = size_of_val(&info) as u32;
59         let res = unsafe { GetConsoleScreenBufferInfoEx(*self.0, &mut info) };
60         if res == 0 { return Error::last() }
61         // Yes, this is important
62         info.srWindow.Right += 1;
63         info.srWindow.Bottom += 1;
64         Ok(ScreenBufferInfoEx(info))
65     }
set_info_ex(&self, mut info: ScreenBufferInfoEx) -> Result<()>66     pub fn set_info_ex(&self, mut info: ScreenBufferInfoEx) -> Result<()> {
67         let res = unsafe { SetConsoleScreenBufferInfoEx(*self.0, &mut info.0) };
68         if res == 0 { return Error::last() }
69         Ok(())
70     }
71     // pub fn font_ex(&self) -> Result<FontEx> {
72         // unsafe {
73             // let mut info = zeroed();
74             // info.cbSize = size_of_val(&info);
75             // let res = GetCurrentConsoleFontEx(*self.0, w::FALSE, &mut info);
76             // if res == 0 { return Error::last() }
77             // Ok(FontEx(info))
78         // }
79     // }
write_output(&self, buf: &[CharInfo], size: (i16, i16), pos: (i16, i16)) -> Result<()>80     pub fn write_output(&self, buf: &[CharInfo], size: (i16, i16), pos: (i16, i16)) -> Result<()> {
81         assert!(buf.len() == (size.0 as usize) * (size.1 as usize));
82         let mut rect = SMALL_RECT {
83             Left: pos.0,
84             Top: pos.1,
85             Right: pos.0 + size.0,
86             Bottom: pos.1 + size.1,
87         };
88         let size = COORD { X: size.0, Y: size.1 };
89         let pos = COORD { X: 0, Y: 0 };
90         let res = unsafe { WriteConsoleOutputW(
91             *self.0, buf.as_ptr() as *const CHAR_INFO, size, pos, &mut rect
92         )};
93         if res == 0 { return Error::last() }
94         Ok(())
95     }
font_size(&self) -> Result<(i16, i16)>96     pub fn font_size(&self) -> Result<(i16, i16)> {
97         unsafe {
98             let mut font = zeroed();
99             let res = GetCurrentConsoleFont(*self.0, FALSE, &mut font);
100             if res == 0 { return Error::last() }
101             Ok((font.dwFontSize.X, font.dwFontSize.Y))
102         }
103     }
104 }
105 impl FromRawHandle for ScreenBuffer {
from_raw_handle(handle: HANDLE) -> ScreenBuffer106     unsafe fn from_raw_handle(handle: HANDLE) -> ScreenBuffer {
107         ScreenBuffer(Handle::new(handle))
108     }
109 }
110 pub struct InputBuffer(Handle);
111 impl InputBuffer {
112     /// Gets the actual active console input buffer
from_conin() -> Result<InputBuffer>113     pub fn from_conin() -> Result<InputBuffer> {
114         let handle = unsafe { CreateFileW(
115             "CONIN$".to_wide_null().as_ptr(), GENERIC_READ | GENERIC_WRITE,
116             FILE_SHARE_READ | FILE_SHARE_WRITE, null_mut(), OPEN_EXISTING,
117             0, null_mut(),
118         )};
119         if handle == INVALID_HANDLE_VALUE { Error::last() }
120         else { unsafe { Ok(InputBuffer::from_raw_handle(handle)) } }
121     }
122     /// The number of input that is available to read
available_input(&self) -> Result<u32>123     pub fn available_input(&self) -> Result<u32> {
124         let mut num = 0;
125         let res = unsafe { GetNumberOfConsoleInputEvents(*self.0, &mut num) };
126         if res == 0 { return Error::last() }
127         Ok(num)
128     }
129     /// Reads a bunch of input events
read_input(&self) -> Result<Vec<Input>>130     pub fn read_input(&self) -> Result<Vec<Input>> {
131         let mut buf: [INPUT_RECORD; 0x1000] = unsafe { zeroed() };
132         let mut size = 0;
133         let res = unsafe { ReadConsoleInputW(
134             *self.0, buf.as_mut_ptr(), buf.len() as DWORD, &mut size,
135         )};
136         if res == 0 { return Error::last() }
137         Ok(buf[..(size as usize)].iter().map(|input| {
138             unsafe { match input.EventType {
139                 KEY_EVENT => {
140                     let e = input.Event.KeyEvent();
141                     Input::Key {
142                         key_down: e.bKeyDown != 0,
143                         repeat_count: e.wRepeatCount,
144                         key_code: e.wVirtualKeyCode,
145                         scan_code: e.wVirtualScanCode,
146                         wide_char: *e.uChar.UnicodeChar(),
147                         control_key_state: e.dwControlKeyState,
148                     }
149                 },
150                 MOUSE_EVENT => {
151                     let e = input.Event.MouseEvent();
152                     Input::Mouse {
153                         position: (e.dwMousePosition.X, e.dwMousePosition.Y),
154                         button_state: e.dwButtonState,
155                         control_key_state: e.dwControlKeyState,
156                         event_flags: e.dwEventFlags,
157                     }
158                 },
159                 WINDOW_BUFFER_SIZE_EVENT => {
160                     let s = input.Event.WindowBufferSizeEvent().dwSize;
161                     Input::WindowBufferSize(s.X, s.Y)
162                 },
163                 MENU_EVENT => Input::Menu(input.Event.MenuEvent().dwCommandId),
164                 FOCUS_EVENT => Input::Focus(input.Event.FocusEvent().bSetFocus != 0),
165                 e => unreachable!("invalid event type: {}", e),
166             } }
167         }).collect())
168     }
169     /// Clears all pending input
flush_input(&self) -> Result<()>170     pub fn flush_input(&self) -> Result<()> {
171         let res = unsafe { FlushConsoleInputBuffer(*self.0) };
172         if res == 0 { return Error::last() }
173         Ok(())
174     }
175 }
176 impl FromRawHandle for InputBuffer {
from_raw_handle(handle: HANDLE) -> InputBuffer177     unsafe fn from_raw_handle(handle: HANDLE) -> InputBuffer {
178         InputBuffer(Handle::from_raw_handle(handle))
179     }
180 }
181 #[repr(transparent)] #[derive(Copy, Clone)]
182 pub struct ScreenBufferInfo(CONSOLE_SCREEN_BUFFER_INFO);
183 impl ScreenBufferInfo {
size(&self) -> (i16, i16)184     pub fn size(&self) -> (i16, i16) {
185         (self.0.dwSize.X, self.0.dwSize.Y)
186     }
187 }
188 #[repr(transparent)] #[derive(Copy, Clone)]
189 pub struct ScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX);
190 impl ScreenBufferInfoEx {
raw_mut(&mut self) -> &mut CONSOLE_SCREEN_BUFFER_INFOEX191     pub fn raw_mut(&mut self) -> &mut CONSOLE_SCREEN_BUFFER_INFOEX {
192         &mut self.0
193     }
194 }
195 #[repr(transparent)] #[derive(Copy, Clone)]
196 pub struct FontInfoEx(CONSOLE_FONT_INFOEX);
197 #[derive(Copy, Clone)]
198 pub enum Input {
199     Key {
200         key_down: bool,
201         repeat_count: u16,
202         key_code: u16,
203         scan_code: u16,
204         wide_char: u16,
205         control_key_state: u32,
206     },
207     Mouse {
208         position: (i16, i16),
209         button_state: u32,
210         control_key_state: u32,
211         event_flags: u32,
212     },
213     WindowBufferSize(i16, i16),
214     Menu(u32),
215     Focus(bool),
216 }
217 #[repr(transparent)] #[derive(Copy, Clone)]
218 pub struct CharInfo(CHAR_INFO);
219 impl CharInfo {
new(ch: u16, attr: u16) -> CharInfo220     pub fn new(ch: u16, attr: u16) -> CharInfo {
221         let mut ci: CHAR_INFO = unsafe { zeroed() };
222         unsafe { *ci.Char.UnicodeChar_mut() = ch };
223         ci.Attributes = attr;
224         CharInfo(ci)
225     }
character(&self) -> u16226     pub fn character(&self) -> u16 { unsafe { *self.0.Char.UnicodeChar() } }
attributes(&self) -> u16227     pub fn attributes(&self) -> u16 { self.0.Attributes }
228 }
229 /// Allocates a console if the process does not already have a console.
alloc() -> Result<()>230 pub fn alloc() -> Result<()> {
231     match unsafe { AllocConsole() } {
232         0 => Error::last(),
233         _ => Ok(()),
234     }
235 }
236 /// Detaches the process from its current console.
free() -> Result<()>237 pub fn free() -> Result<()> {
238     match unsafe { FreeConsole() } {
239         0 => Error::last(),
240         _ => Ok(()),
241     }
242 }
243 /// Attaches the process to the console of the specified process.
244 /// Pass None to attach to the console of the parent process.
attach(processid: Option<u32>) -> Result<()>245 pub fn attach(processid: Option<u32>) -> Result<()> {
246     match unsafe { AttachConsole(processid.unwrap_or(-1i32 as u32)) } {
247         0 => Error::last(),
248         _ => Ok(()),
249     }
250 }
251 /// Gets the current input code page
input_code_page() -> u32252 pub fn input_code_page() -> u32 {
253     unsafe { GetConsoleCP() }
254 }
255 /// Gets the current output code page
output_code_page() -> u32256 pub fn output_code_page() -> u32 {
257     unsafe { GetConsoleOutputCP() }
258 }
259 /// Sets the current input code page
set_input_code_page(code: u32) -> Result<()>260 pub fn set_input_code_page(code: u32) -> Result<()> {
261     let res = unsafe { SetConsoleCP(code) };
262     if res == 0 { return Error::last() }
263     Ok(())
264 }
265 /// Sets the current output code page
set_output_code_page(code: u32) -> Result<()>266 pub fn set_output_code_page(code: u32) -> Result<()> {
267     let res = unsafe { SetConsoleOutputCP(code) };
268     if res == 0 { return Error::last() }
269     Ok(())
270 }
271