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