1use windows::core::Result as WinResult;
4use windows::Win32::Foundation::WAIT_OBJECT_0;
5use windows::Win32::{
6 Foundation::HANDLE,
7 System::{
8 Console::{
9 GetConsoleMode, GetStdHandle, SetConsoleMode, CONSOLE_MODE,
10 DISABLE_NEWLINE_AUTO_RETURN, ENABLE_ECHO_INPUT, ENABLE_EXTENDED_FLAGS,
11 ENABLE_INSERT_MODE, ENABLE_LINE_INPUT, ENABLE_MOUSE_INPUT, ENABLE_PROCESSED_INPUT,
12 ENABLE_QUICK_EDIT_MODE, ENABLE_VIRTUAL_TERMINAL_INPUT, STD_ERROR_HANDLE,
13 STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
14 },
15 Threading::WaitForSingleObject,
16 },
17};
18
19use crate::error::Error;
20
21#[derive(Debug, Clone)]
23pub struct Console {
24 stdin: HANDLE,
25 stdout: HANDLE,
26 stderr: HANDLE,
27 stdin_mode: CONSOLE_MODE,
28 stdout_mode: CONSOLE_MODE,
29 stderr_mode: CONSOLE_MODE,
30}
31
32impl Console {
33 pub fn current() -> Result<Self, Error> {
35 let stdin = unsafe { GetStdHandle(STD_INPUT_HANDLE)? };
39 let stdout = unsafe { GetStdHandle(STD_OUTPUT_HANDLE)? };
40 let stderr = unsafe { GetStdHandle(STD_ERROR_HANDLE)? };
41
42 let stdin_mode = get_console_mode(stdin)?;
43 let stdout_mode = get_console_mode(stdout)?;
44 let stderr_mode = get_console_mode(stderr)?;
45
46 Ok(Self {
47 stderr,
48 stderr_mode,
49 stdin,
50 stdin_mode,
51 stdout,
52 stdout_mode,
53 })
54 }
55
56 pub fn set_raw(&self) -> Result<(), Error> {
59 set_raw_stdin(self.stdin, self.stdin_mode)?;
60
61 unsafe {
62 SetConsoleMode(self.stdout, self.stdout_mode | DISABLE_NEWLINE_AUTO_RETURN)?;
63 }
64 unsafe {
65 SetConsoleMode(self.stderr, self.stderr_mode | DISABLE_NEWLINE_AUTO_RETURN)?;
66 }
67
68 Ok(())
69 }
70
71 pub fn reset(&self) -> Result<(), Error> {
73 for (handle, mode) in self.streams() {
74 unsafe { SetConsoleMode(handle, mode)? };
75 }
76
77 Ok(())
78 }
79
80 pub fn is_stdin_empty(&self) -> Result<bool, Error> {
84 let empty = unsafe { WaitForSingleObject(self.stdin, 0) == WAIT_OBJECT_0 };
86 Ok(empty)
87 }
88
89 fn streams(&self) -> [(HANDLE, CONSOLE_MODE); 3] {
90 [
91 (self.stdin, self.stdin_mode),
92 (self.stdout, self.stdout_mode),
93 (self.stderr, self.stderr_mode),
94 ]
95 }
96}
97
98fn get_console_mode(h: HANDLE) -> WinResult<CONSOLE_MODE> {
99 let mut mode = CONSOLE_MODE::default();
100 unsafe {
101 GetConsoleMode(h, &mut mode)?;
102 }
103 Ok(mode)
104}
105
106fn set_raw_stdin(stdin: HANDLE, mut mode: CONSOLE_MODE) -> WinResult<()> {
107 mode &= !ENABLE_ECHO_INPUT;
108 mode &= !ENABLE_LINE_INPUT;
109 mode &= !ENABLE_MOUSE_INPUT;
110 mode &= !ENABLE_LINE_INPUT;
111 mode &= !ENABLE_PROCESSED_INPUT;
112
113 mode |= ENABLE_EXTENDED_FLAGS;
114 mode |= ENABLE_INSERT_MODE;
115 mode |= ENABLE_QUICK_EDIT_MODE;
116
117 let vt_input_supported = true;
118 if vt_input_supported {
119 mode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
120 }
121
122 unsafe {
123 SetConsoleMode(stdin, mode)?;
124 }
125
126 Ok(())
127}