Skip to content

Commit

Permalink
fix(runtime): (WinOS) console raw mode off repair (fixes denoland#17866)
Browse files Browse the repository at this point in the history
  • Loading branch information
rivy committed Mar 1, 2023
1 parent 92ba46c commit d53f806
Showing 1 changed file with 52 additions and 5 deletions.
57 changes: 52 additions & 5 deletions runtime/ops/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ pub fn init() -> Extension {
.build()
}

#[cfg(windows)]
const COOKED_MODE: DWORD = // ref: <https://learn.microsoft.com/en-us/windows/console/setconsolemode> @@ <https://archive.is/HWHwG>
wincon::ENABLE_LINE_INPUT // enable line-by-line input (returns input only after CR is read)
| wincon::ENABLE_ECHO_INPUT // enables real-time character echo to console display (requires ENABLE_LINE_INPUT)
| wincon::ENABLE_PROCESSED_INPUT // system handles CTRL-C (with ENABLE_LINE_INPUT, also handles BS, CR, and LF) and other control keys (when using `ReadFile` or `ReadConsole`)
;

#[cfg(windows)]
fn mode_raw_input_on(original_mode: DWORD) -> DWORD {
original_mode // disable, then enable (`&` has higher precedence than `|`)
& ! COOKED_MODE // disable line-by-line input, character echo, and system control key handling
| wincon::ENABLE_VIRTUAL_TERMINAL_INPUT // enable console/system handling of virtual terminal sequences
}

#[cfg(windows)]
fn mode_raw_input_off(original_mode: DWORD) -> DWORD {
original_mode // disable, then enable (`&` has higher precedence than `|`)
& ! wincon::ENABLE_VIRTUAL_TERMINAL_INPUT // disable console/system handling of virtual terminal sequences
| COOKED_MODE // enable line-by-line input, character echo, and system control key handling
}

#[op(fast)]
fn op_stdin_set_raw(
state: &mut OpState,
Expand Down Expand Up @@ -83,13 +104,10 @@ fn op_stdin_set_raw(
return Err(Error::last_os_error().into());
}

const RAW_MODE_MASK: DWORD = wincon::ENABLE_LINE_INPUT
| wincon::ENABLE_ECHO_INPUT
| wincon::ENABLE_PROCESSED_INPUT;
let new_mode = if is_raw {
original_mode & !RAW_MODE_MASK | wincon::ENABLE_VIRTUAL_TERMINAL_INPUT
mode_raw_input_on(original_mode)
} else {
original_mode | RAW_MODE_MASK & !wincon::ENABLE_VIRTUAL_TERMINAL_INPUT
mode_raw_input_off(original_mode)
};

// SAFETY: winapi call
Expand Down Expand Up @@ -271,3 +289,32 @@ pub fn console_size(
}
}
}

#[cfg(all(test, windows))]
mod tests {
#[cfg(windows)]
#[test]
fn test_winos_raw_mode_transitions() {
use crate::ops::tty::mode_raw_input_off;
use crate::ops::tty::mode_raw_input_on;

let known_off_modes =
[0xf7 /* Win10/CMD */, 0x1f7 /* Win10/WinTerm */];
let known_on_modes =
[0x2f0 /* Win10/CMD */, 0x3f0 /* Win10/WinTerm */];

// assert known transitions
assert_eq!(known_on_modes[0], mode_raw_input_on(known_off_modes[0]));
assert_eq!(known_on_modes[1], mode_raw_input_on(known_off_modes[1]));

// assert ON-OFF round-trip is neutral
assert_eq!(
known_off_modes[0],
mode_raw_input_off(mode_raw_input_on(known_off_modes[0]))
);
assert_eq!(
known_off_modes[1],
mode_raw_input_off(mode_raw_input_on(known_off_modes[1]))
);
}
}

0 comments on commit d53f806

Please sign in to comment.