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