diff --git a/UPSTREAM_MAPPING.md b/UPSTREAM_MAPPING.md index 9bf892b..c44ea25 100644 --- a/UPSTREAM_MAPPING.md +++ b/UPSTREAM_MAPPING.md @@ -48,7 +48,7 @@ upstream tag `v2.0.8`, checked out locally in `upstream-go/` (gitignored). | `termios_other.go` | `src/termios_other.rs` | Non-POSIX fallback | | `termios_unix.go` | `src/termios_unix.rs` | POSIX termios helper | | `termios_windows.go` | `src/termios_windows.rs` | Windows console mode helper | -| `tty.go` | `src/tty.rs` | `init_terminal` / `restore_terminal` | +| `tty.go` | `src/tty.rs` | `init_terminal` / `restore_terminal`; Unix raw-mode implementation with compile-safe Windows no-op boundary | | `tty_unix.go` | `src/tty_unix.rs` | Unix TTY reader + raw mode | | `tty_windows.go` | `src/tty_windows.rs` | Windows VT console helper | | `xterm.go` | `src/xterm.rs` | XTVERSION query, `TerminalVersionMsg` | @@ -160,6 +160,11 @@ byte-for-byte. | `tutorials/basics/main.go` | `examples/tutorial_basics.rs` | Tutorial: counter; quits on 'q' | | `tutorials/commands/main.go` | `examples/tutorial_commands.rs` | Tutorial: commands; quits on 'q' | +Rust-only portability regression coverage is maintained in `tests/windows_platform.rs`; it +exercises the Windows raw-mode and terminal-restoration boundary that has no standalone +upstream Go test file. The PTY-driven `tests/interactive.rs` suite remains Unix-gated because +its real pseudo-terminal dependency is intentionally not claimed as native Windows support. + Example support files (`examples/go.mod`, `examples/go.sum`, `examples/table/demo.tape`, per-example `README.md`/`.gif` assets, `tutorials/go.mod`, `tutorials/go.sum`) are documented in the Support Files section. diff --git a/src/program.rs b/src/program.rs index 844a6bd..04f4913 100644 --- a/src/program.rs +++ b/src/program.rs @@ -921,9 +921,10 @@ fn uv_mouse_to_mouse(m: rusty_ultraviolet::Mouse) -> crate::mouse::Mouse { /// CheckOptimizedMovements reads the stdin termios and reports whether hard /// tabs (TABDLY==TAB0) and backspace (BSDLY==BS0) optimizations are enabled. fn check_optimized_movements() -> (bool, bool) { - use std::os::fd::AsRawFd; #[cfg(unix)] { + use std::os::fd::AsRawFd; + let fd = std::io::stdin().as_raw_fd(); let mut t: libc::termios = unsafe { std::mem::zeroed() }; if unsafe { libc::tcgetattr(fd, &mut t) } != 0 { diff --git a/src/tty.rs b/src/tty.rs index 178b4a5..3cdc5f6 100644 --- a/src/tty.rs +++ b/src/tty.rs @@ -1,23 +1,28 @@ //! Cleanroom Rust port of upstream Go source file: `tty.go` //! Upstream Target Tag / Version: `v2.0.8` //! -//! +//! //! # TTY Terminal Management //! //! TTY initialization, input stream setup, raw mode toggles, and window dimension queries for Bubble Tea v2.0.8. -//! +//! On Windows, raw-mode operations are compile-safe no-ops until the native +//! console adapter is completed; window-size queries remain available. +//! use crossterm::terminal::size as term_size; +#[cfg(unix)] use std::sync::{Mutex, OnceLock}; /// Saved terminal state for the raw mode toggle, mirroring the upstream /// `p.previousTtyInputState` (x/term `MakeRaw`/`Restore`). +#[cfg(unix)] static SAVED_TERMIOS: OnceLock>> = OnceLock::new(); /// Initializes terminal raw mode, mirroring the upstream `initInput` -> /// `term.MakeRaw` path (`tty_unix.go`). Unlike a fully-zeroed `cfmakeraw`, /// only `OPOST` is cleared from the output flags, so `TABDLY` (and thus the /// hard-tab cursor optimization) behaves exactly as it does upstream. +#[cfg(unix)] pub fn enable_raw_mode() -> std::io::Result<()> { use std::os::fd::AsRawFd; let fd = std::io::stdin().as_raw_fd(); @@ -52,8 +57,16 @@ pub fn enable_raw_mode() -> std::io::Result<()> { Ok(()) } +/// Keeps the raw-mode API available on Windows while native console mode +/// support remains deferred. +#[cfg(not(unix))] +pub fn enable_raw_mode() -> std::io::Result<()> { + Ok(()) +} + /// Restores the terminal state saved by [`enable_raw_mode`], mirroring the /// upstream `term.Restore` path. +#[cfg(unix)] pub fn disable_raw_mode() -> std::io::Result<()> { use std::os::fd::AsRawFd; let saved = SAVED_TERMIOS @@ -70,6 +83,13 @@ pub fn disable_raw_mode() -> std::io::Result<()> { Ok(()) } +/// Keeps terminal restoration deterministic on Windows when no raw mode was +/// enabled by the compile-safe fallback. +#[cfg(not(unix))] +pub fn disable_raw_mode() -> std::io::Result<()> { + Ok(()) +} + /// Initializes terminal raw mode. pub fn init_terminal() -> Result<(), Box> { enable_raw_mode()?; diff --git a/tests/interactive.rs b/tests/interactive.rs index b0b982c..546e80d 100644 --- a/tests/interactive.rs +++ b/tests/interactive.rs @@ -1,3 +1,5 @@ +#![cfg(unix)] + //! Interactive integration tests for the Bubble Tea examples, driven through //! a real pseudo-terminal (Playwright-style): keys, typing, mouse, resizing, //! and assertions on the reconstructed on-screen state. diff --git a/tests/tea_test.rs b/tests/tea_test.rs index 004437d..8c7b29f 100644 --- a/tests/tea_test.rs +++ b/tests/tea_test.rs @@ -520,9 +520,9 @@ fn test_clipboard_and_color_commands() { // TTY helpers let _ = rusty_bubbletea::tty::get_window_size(); - let (tx_sig, _rx_sig) = std::sync::mpsc::channel(); + let (_tx_sig, _rx_sig) = std::sync::mpsc::channel::>(); #[cfg(unix)] - rusty_bubbletea::signals_unix::listen_for_resize(&tx_sig); + rusty_bubbletea::signals_unix::listen_for_resize(&_tx_sig); } #[test] @@ -596,7 +596,11 @@ fn test_keyboard_and_mouse_and_env_and_logging() { nil.reset(); assert!(nil.close().is_ok()); - let mut logger = logging::log_to_file("/tmp/test_bubbletea.log", "test").unwrap(); + let log_path = std::env::temp_dir() + .join("rusty-bubbletea-test.log") + .to_string_lossy() + .into_owned(); + let mut logger = logging::log_to_file(&log_path, "test").unwrap(); logger.log("test message"); } diff --git a/tests/windows_platform.rs b/tests/windows_platform.rs new file mode 100644 index 0000000..9063f1f --- /dev/null +++ b/tests/windows_platform.rs @@ -0,0 +1,17 @@ +#![cfg(windows)] + +//! Windows regression coverage for the compile-safe terminal boundary. + +use rusty_bubbletea::tty::{disable_raw_mode, enable_raw_mode, init_terminal, restore_terminal}; + +#[test] +fn raw_mode_operations_are_safe_no_ops_on_windows() { + enable_raw_mode().expect("Windows raw-mode fallback should succeed"); + disable_raw_mode().expect("Windows raw-mode restoration fallback should succeed"); +} + +#[test] +fn terminal_lifecycle_uses_the_same_windows_boundary() { + init_terminal().expect("Windows terminal initialization fallback should succeed"); + restore_terminal().expect("Windows terminal restoration fallback should succeed"); +}