Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion crates/vp_shared/src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ impl Progress {

/// Show a new row, blocking keyboard input and hiding the cursor until it is cleared.
fn start(bar: ProgressBar, style: ProgressStyle) -> Self {
// Write cursor controls before the redraw thread can emit a progress frame.
let input = input::block();
let mut progress = Self::new(bar, style, None);
progress._input = input::block();
progress._input = input;
progress
}

Expand Down
45 changes: 42 additions & 3 deletions crates/vp_shared/src/progress/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,9 +366,7 @@ mod tests {
sys::save(fd).is_some_and(|raw| sys::apply(fd, &raw))
}

/// The cursor changes written to the pty since the last call, `l` for each
/// hide and `h` for each show.
fn cursor_changes(master: &OwnedFd) -> String {
fn terminal_output(master: &OwnedFd) -> Vec<u8> {
let mut output = Vec::new();
let mut buf = [0; 1024];
loop {
Expand All @@ -382,6 +380,12 @@ mod tests {
}
}
output
}

/// The cursor changes written to the pty since the last call, `l` for each
/// hide and `h` for each show.
fn cursor_changes(master: &OwnedFd) -> String {
terminal_output(master)
.windows(6)
.filter_map(|sequence| match sequence {
b"\x1b[?25l" => Some('l'),
Expand All @@ -391,6 +395,41 @@ mod tests {
.collect()
}

const PROGRESS_CHILD: &str = "VP_SHARED_INPUT_PROGRESS_CHILD";

#[test]
fn progress_hides_cursor_before_first_frame() {
use std::process::Command;

if std::env::var_os(PROGRESS_CHILD).is_some() {
crate::EnvConfig::with_vars([("CI", None::<&str>)], |_| {
let _progress = crate::progress::Progress::counter("Preparing fixture", 1)
.expect("stderr is a terminal");
});
return;
}

let size = nix::pty::Winsize { ws_row: 24, ws_col: 80, ws_xpixel: 0, ws_ypixel: 0 };
let pty = openpty(Some(&size), None).unwrap();
let status = Command::new(std::env::current_exe().unwrap())
.args(["--exact", "progress::input::tests::progress_hides_cursor_before_first_frame"])
.env("TERM", "xterm-256color")
.env(PROGRESS_CHILD, "1")
.stdin(pty.slave.try_clone().unwrap())
.stdout(std::process::Stdio::null())
.stderr(pty.slave.try_clone().unwrap())
.status()
.unwrap();

assert!(status.success(), "{status}");
let output = terminal_output(&pty.master);
assert!(output.starts_with(b"\x1b[?25l"), "{output:?}");
assert!(
output.windows(b"Preparing fixture".len()).any(|part| part == b"Preparing fixture"),
"{output:?}"
);
}

#[test]
fn raw_input_keeps_signals_and_restores_the_terminal() {
let pty = openpty(None, None).unwrap();
Expand Down
Loading