1 //! This contains the logic for working with the console buffer.
2 
3 use std::io::Result;
4 use std::mem::size_of;
5 
6 use winapi::{
7     shared::minwindef::TRUE,
8     shared::ntdef::NULL,
9     um::{
10         minwinbase::SECURITY_ATTRIBUTES,
11         wincon::{
12             CreateConsoleScreenBuffer, GetConsoleScreenBufferInfo, SetConsoleActiveScreenBuffer,
13             SetConsoleScreenBufferSize, CONSOLE_TEXTMODE_BUFFER, COORD,
14         },
15         winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE},
16     },
17 };
18 
19 use super::{handle_result, result, Handle, HandleType, ScreenBufferInfo};
20 
21 /// A wrapper around a screen buffer.
22 #[derive(Clone, Debug)]
23 pub struct ScreenBuffer {
24     handle: Handle,
25 }
26 
27 impl ScreenBuffer {
28     /// Create a wrapper around a screen buffer from its handle.
new(handle: Handle) -> Self29     pub fn new(handle: Handle) -> Self {
30         Self { handle }
31     }
32 
33     /// Get the current console screen buffer
current() -> Result<ScreenBuffer>34     pub fn current() -> Result<ScreenBuffer> {
35         Ok(ScreenBuffer {
36             handle: Handle::new(HandleType::CurrentOutputHandle)?,
37         })
38     }
39 
40     /// Create new console screen buffer.
41     ///
42     /// This wraps
43     /// [`CreateConsoleScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/createconsolescreenbuffer)
create() -> Result<ScreenBuffer>44     pub fn create() -> Result<ScreenBuffer> {
45         let security_attr: SECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES {
46             nLength: size_of::<SECURITY_ATTRIBUTES>() as u32,
47             lpSecurityDescriptor: NULL,
48             bInheritHandle: TRUE,
49         };
50 
51         let new_screen_buffer = handle_result(unsafe {
52             CreateConsoleScreenBuffer(
53                 GENERIC_READ |           // read/write access
54                     GENERIC_WRITE,
55                 FILE_SHARE_READ | FILE_SHARE_WRITE, // shared
56                 &security_attr,                     // default security attributes
57                 CONSOLE_TEXTMODE_BUFFER,            // must be TEXTMODE
58                 NULL,
59             )
60         })?;
61         Ok(ScreenBuffer {
62             handle: unsafe { Handle::from_raw(new_screen_buffer) },
63         })
64     }
65 
66     /// Set this screen buffer to the current one.
67     ///
68     /// This wraps
69     /// [`SetConsoleActiveScreenBuffer`](https://docs.microsoft.com/en-us/windows/console/setconsoleactivescreenbuffer).
show(&self) -> Result<()>70     pub fn show(&self) -> Result<()> {
71         result(unsafe { SetConsoleActiveScreenBuffer(*self.handle) })
72     }
73 
74     /// Get the screen buffer information like terminal size, cursor position, buffer size.
75     ///
76     /// This wraps
77     /// [`GetConsoleScreenBufferInfo`](https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo).
info(&self) -> Result<ScreenBufferInfo>78     pub fn info(&self) -> Result<ScreenBufferInfo> {
79         let mut csbi = ScreenBufferInfo::new();
80         result(unsafe { GetConsoleScreenBufferInfo(*self.handle, &mut csbi.0) })?;
81         Ok(csbi)
82     }
83 
84     /// Set the console screen buffer size to the given size.
85     ///
86     /// This wraps
87     /// [`SetConsoleScreenBufferSize`](https://docs.microsoft.com/en-us/windows/console/setconsolescreenbuffersize).
set_size(&self, x: i16, y: i16) -> Result<()>88     pub fn set_size(&self, x: i16, y: i16) -> Result<()> {
89         result(unsafe { SetConsoleScreenBufferSize(*self.handle, COORD { X: x, Y: y }) })
90     }
91 
92     /// Get the underlying raw `HANDLE` used by this type to execute with.
handle(&self) -> &Handle93     pub fn handle(&self) -> &Handle {
94         &self.handle
95     }
96 }
97 
98 impl From<Handle> for ScreenBuffer {
from(handle: Handle) -> Self99     fn from(handle: Handle) -> Self {
100         ScreenBuffer { handle }
101     }
102 }
103 
104 #[cfg(test)]
105 mod tests {
106     use super::ScreenBuffer;
107 
108     #[test]
test_screen_buffer_info()109     fn test_screen_buffer_info() {
110         let buffer = ScreenBuffer::current().unwrap();
111         let info = buffer.info().unwrap();
112         info.terminal_size();
113         info.terminal_window();
114         info.attributes();
115         info.cursor_pos();
116     }
117 }
118