xref: /aosp_15_r20/external/crosvm/base/src/sys/windows/terminal.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::io::Stdin;
6 
7 use winapi::shared::minwindef::DWORD;
8 use winapi::um::consoleapi::GetConsoleMode;
9 use winapi::um::consoleapi::SetConsoleMode;
10 use winapi::um::wincon::ENABLE_ECHO_INPUT;
11 use winapi::um::wincon::ENABLE_LINE_INPUT;
12 use winapi::um::wincon::ENABLE_PROCESSED_INPUT;
13 use winapi::um::wincon::ENABLE_VIRTUAL_TERMINAL_INPUT;
14 
15 use crate::AsRawDescriptor;
16 use crate::Error;
17 use crate::RawDescriptor;
18 use crate::Result;
19 
20 /// Trait for file descriptors that are terminals.
21 ///
22 /// # Safety
23 /// This is marked unsafe because the implementation must promise that the returned RawDescriptor is
24 /// a valid descriptor and that the lifetime of the returned descriptor is at least that of the
25 /// trait object.
26 pub unsafe trait Terminal {
27     /// Gets the file descriptor of the terminal.
terminal_descriptor(&self) -> RawDescriptor28     fn terminal_descriptor(&self) -> RawDescriptor;
29 
30     /// Set this terminal's mode to raw mode.
31     ///
32     /// Returns the original mode, which can be passed to `restore_mode()` to reset the terminal to
33     /// its previous state.
set_raw_mode(&self) -> Result<DWORD>34     fn set_raw_mode(&self) -> Result<DWORD> {
35         let descriptor = self.terminal_descriptor();
36         let mut orig_mode = 0;
37 
38         // SAFETY:
39         // Safe because we provide a valid descriptor and pointer and we check the return result.
40         if unsafe { GetConsoleMode(descriptor, &mut orig_mode) } == 0 {
41             return Err(Error::last());
42         }
43 
44         let new_mode = (orig_mode | ENABLE_VIRTUAL_TERMINAL_INPUT)
45             & !(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
46 
47         // SAFETY:
48         // Safe because the syscall will only read the extent of mode and we check the return
49         // result.
50         if unsafe { SetConsoleMode(descriptor, new_mode) } == 0 {
51             return Err(Error::last());
52         }
53 
54         Ok(orig_mode)
55     }
56 
57     /// Set this terminal's mode to a previous state returned by `set_raw_mode()`.
restore_mode(&self, mode: DWORD) -> Result<()>58     fn restore_mode(&self, mode: DWORD) -> Result<()> {
59         // SAFETY:
60         // Safe because the syscall will only read the extent of mode and we check the return
61         // result.
62         if unsafe { SetConsoleMode(self.terminal_descriptor(), mode) } == 0 {
63             Err(Error::last())
64         } else {
65             Ok(())
66         }
67     }
68 }
69 
70 // SAFETY:
71 // Safe because we return a genuine terminal descriptor that never changes and shares our lifetime.
72 unsafe impl Terminal for Stdin {
terminal_descriptor(&self) -> RawDescriptor73     fn terminal_descriptor(&self) -> RawDescriptor {
74         self.as_raw_descriptor()
75     }
76 }
77