Skip to content

Commit

Permalink
implement interactive terminal for windows
Browse files Browse the repository at this point in the history
Summary:
Implementation of interactive terminal, that never was implemented for windows.
First we remember console mode and disable `ENABLE_ECHO_INPUT` and `ENABLE_LINE_INPUT` to get input as soon as one or more characters are available. (see [documentation](https://learn.microsoft.com/en-us/windows/console/setconsolemode))
When disabling interactive terminal we simply restore original console mode

Reviewed By: ezgicicek

Differential Revision: D69985905

fbshipit-source-id: fb381766c1d2961f7e132ccf9cce87510d0acc3f
  • Loading branch information
Yury Samkevich authored and facebook-github-bot committed Feb 21, 2025
1 parent 460a3e6 commit e0021de
Showing 1 changed file with 46 additions and 2 deletions.
48 changes: 46 additions & 2 deletions app/buck2_client_ctx/src/console_interaction_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,58 @@ mod interactive_terminal {

#[cfg(windows)]
mod interactive_terminal {
pub struct InteractiveTerminal;
use std::io::IsTerminal;
use std::os::windows::io::AsRawHandle;

use buck2_error::BuckErrorContext;
use winapi::shared::minwindef::DWORD;
use winapi::um::consoleapi::GetConsoleMode;
use winapi::um::consoleapi::SetConsoleMode;
use winapi::um::wincon::ENABLE_ECHO_INPUT;
use winapi::um::wincon::ENABLE_LINE_INPUT;
use winapi::um::winnt::HANDLE;

fn get_console_mode(handle: HANDLE) -> buck2_error::Result<DWORD> {
let mut mode: DWORD = 0;
if unsafe { GetConsoleMode(handle, &mut mode) } != 0 {
Ok(mode)
} else {
Err(std::io::Error::last_os_error()).buck_error_context("Failed to get console mode")
}
}

fn set_console_mode(handle: HANDLE, mode: DWORD) -> buck2_error::Result<()> {
if unsafe { SetConsoleMode(handle, mode) != 0 } {
Ok(())
} else {
Err(std::io::Error::last_os_error()).buck_error_context("Failed to set console mode")
}
}

pub struct InteractiveTerminal {
mode: DWORD,
}

impl InteractiveTerminal {
pub fn enable() -> buck2_error::Result<Option<Self>> {
Ok(None)
let handle = std::io::stdin().as_raw_handle();

if !std::io::stdin().is_terminal()
|| !std::io::stdout().is_terminal()
|| !std::io::stderr().is_terminal()
{
return Ok(None);
}

let mode = get_console_mode(handle)?;
// Switch to non-canonical mode to get input immediately, and disable echo.
set_console_mode(handle, mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))?;
Ok(Some(Self { mode }))
}

pub fn disable(&mut self) -> buck2_error::Result<()> {
let handle = std::io::stdin().as_raw_handle();
set_console_mode(handle, self.mode)?;
Ok(())
}
}
Expand Down

0 comments on commit e0021de

Please sign in to comment.