From: Alexander McLin Date: Mon, 30 Mar 2026 01:29:48 +0000 (-0400) Subject: Fixes issue 3264 by adding a fallback read mode when reading from non-tty stdin on... X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=533594b6b64f56b90abd4f9b65899f5994c71d7f;p=scryer-prolog.git Fixes issue 3264 by adding a fallback read mode when reading from non-tty stdin on Windows --- diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 2ac93cb7..df15aeb5 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -68,9 +68,7 @@ use cpu_time::ProcessTime; use std::time::{Duration, SystemTime}; #[cfg(feature = "repl")] -use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; -#[cfg(feature = "repl")] -use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use crate::read::user_interaction::{get_key, KeyCode, KeyModifiers}; use blake2::{Blake2b512, Blake2s256}; @@ -147,28 +145,6 @@ impl ModuleQuantification { } } -#[cfg(feature = "repl")] -pub(crate) fn get_key() -> KeyEvent { - let key; - enable_raw_mode().expect("failed to enable raw mode"); - loop { - let key_ = read(); - if let Ok(Event::Key(key_)) = key_ { - if key_.kind != KeyEventKind::Release { - match key_.code { - KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => { - key = key_; - break; - } - _ => (), - } - } - } - } - disable_raw_mode().expect("failed to disable raw mode"); - key -} - fn pstr_segment_char_count_and_tail(heap: &Heap, pstr_loc: usize) -> (usize, usize) { let char_iter = heap.char_iter(pstr_loc); diff --git a/src/read.rs b/src/read.rs index a07f4d09..f0346ad2 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,3 +1,8 @@ +#[cfg(feature = "repl")] +pub mod fallback_mode; +#[cfg(feature = "repl")] +pub mod user_interaction; + use crate::parser::ast::*; use crate::parser::lexer::Lexer; use crate::parser::parser::*; diff --git a/src/read/fallback_mode.rs b/src/read/fallback_mode.rs new file mode 100644 index 00000000..b7a5ba92 --- /dev/null +++ b/src/read/fallback_mode.rs @@ -0,0 +1,37 @@ +use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}; +use std::io::{Read, Error, ErrorKind}; + +// Provide graceful degradation limited support mode if reading from stdin that does not +// support terminal features. +// +// Retrieve a single byte from stdin, does not support full Unicode decoding; only convert +// byte to KeyEvent char if it falls within the ASCII subset of UTF-8. +// +// Does not support passing through Ctrl-C as a KeyEvent. +pub(crate) fn limited_support_read() -> std::io::Result { + let byte_or_none = std::io::stdin().bytes().next(); + + match byte_or_none { + Some(byte) => match byte { + Ok(b) => { + if b.is_ascii() { + Ok(map_ascii_to_keyevent(b as char)) + } + else { + Err(Error::new(ErrorKind::Unsupported, "not supported input")) + } + } + Err(e) => Err(e), + }, + None => Err(Error::new(ErrorKind::UnexpectedEof, "EOF")), + } +} + +fn map_ascii_to_keyevent(c: char) -> KeyEvent { + KeyEvent { + code: KeyCode::Char(c), + modifiers: KeyModifiers::NONE, + kind: KeyEventKind::Press, + state: KeyEventState::empty(), + } +} \ No newline at end of file diff --git a/src/read/user_interaction.rs b/src/read/user_interaction.rs new file mode 100644 index 00000000..fb82259f --- /dev/null +++ b/src/read/user_interaction.rs @@ -0,0 +1,49 @@ +use crossterm::event::{read, Event, KeyEventKind}; +pub(crate) use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use crossterm::tty::IsTty; + +use crate::read::fallback_mode; + +pub(crate) fn get_key() -> KeyEvent { + let key; + if supported_terminal() { + enable_raw_mode().expect("failed to enable raw mode"); + loop { + let key_ = read(); + if let Ok(Event::Key(key_)) = key_ { + if key_.kind != KeyEventKind::Release { + match key_.code { + KeyCode::Char(_) | KeyCode::Enter | KeyCode::Tab => { + key = key_; + break; + } + _ => (), + } + } + } + } + disable_raw_mode().expect("failed to disable raw mode"); + } else { + // stdin is not supported terminal type, fallback to limited support mode. + loop { + let key_ = fallback_mode::limited_support_read(); + if let Ok(key_) = key_ { + key = key_; + break; + } + } + } + key +} + +fn supported_terminal() -> bool { + #[cfg(windows)] + let windows_os = true; + + #[cfg(not(windows))] + let windows_os = false; + + // If OS is Windows and stdin is not a tty, it's either a pipe or an unsupported terminal configuration. + !windows_os || std::io::stdin().is_tty() +} \ No newline at end of file