crossterm-rs / crossterm

Cross platform terminal library rust
MIT License
3.17k stars 274 forks source link

cursor::SavePosition / cursor::RestorePosition break with newlines #673

Open wmmc88 opened 2 years ago

wmmc88 commented 2 years ago

Describe the bug cursor::SavePosition seems to only save the position on a line and doesnt work with newline.

To Reproduce Working Code to overwrite printed value:

use crossterm::{cursor, terminal, ExecutableCommand, QueueableCommand};

fn main() {
    let mut stdout = stdout();
    stdout.execute(cursor::Hide).unwrap();

    for year in 1950..2030 {
        stdout.queue(cursor::SavePosition).unwrap();
        stdout
            .write_all(format!("{year}").as_bytes()
            )
            .unwrap();
        stdout.flush().unwrap();

        thread::sleep(time::Duration::from_millis(200));
        stdout.queue(cursor::RestorePosition).unwrap();
        stdout
            .queue(terminal::Clear(terminal::ClearType::FromCursorDown))
            .unwrap();
    }

    stdout.execute(cursor::Show).unwrap();
    println!("Done!");
}

Adding a single newline breaks the code:

use crossterm::{cursor, terminal, ExecutableCommand, QueueableCommand};

fn main() {
    let mut stdout = stdout();
    stdout.execute(cursor::Hide).unwrap();

    for year in 1950..2030 {
        stdout.queue(cursor::SavePosition).unwrap();
        stdout
            .write_all(format!("{year}\n").as_bytes()
            )
            .unwrap();
        stdout.flush().unwrap();

        thread::sleep(time::Duration::from_millis(200));
        stdout.queue(cursor::RestorePosition).unwrap();
        stdout
            .queue(terminal::Clear(terminal::ClearType::FromCursorDown))
            .unwrap();
    }

    stdout.execute(cursor::Show).unwrap();
    println!("Done!");
}

Expected behavior Restore the position of the cursor.

OS Ubuntu 22.04

Terminal/Console gnome-terminal

sigmaSd commented 2 years ago

I have a guess that save restore cursor position requires raw mode to be enabled to work 100% of the time. Otherwise sometimes they fail.

wmmc88 commented 2 years ago

Tried out raw mode and it doesn't seem to fix my issue. Just seems like SavePosition and RestorePosition don't work when there are newlines. I've resorted to just counting the newlines and using MoveUp + ClearType::FromCursorDown(My intention is to print some lines, then overwrite them in a loop).

sigmaSd commented 2 years ago

It might also be due to scrolling effect, if the terminal size is small it can mess the math see https://unix.stackexchange.com/questions/278884/save-cursor-position-and-restore-it-in-terminal/278888#278888 for why scrolling breaks this.

sigmaSd commented 2 years ago

Either way crossterm just wraps the ansi sequences https://github.com/crossterm-rs/crossterm/blob/9a50fd2ce2701bb15f58722495024937143ad34d/src/cursor.rs#L272 so this is can't be considered a crossterm bug.

TimonPost commented 2 years ago

Possibly work around this issue by fetching the terminal position at the loop start, and doing a goto after the print. Like @sigmaSd says crossterm doesn't do much else than just writing an ANSI code.

bartoszpiech commented 1 year ago

I had the same bug, found out that my terminal emulator (st) did not implement all of the VT100 escape sequences, after testing. The code in other terminals (xterm) works just fine.

wiktor-k commented 11 months ago

I've resorted to just counting the newlines and using MoveUp + ClearType::FromCursorDown(My intention is to print some lines, then overwrite them in a loop).

I've just encountered the same issue and used this workaround (as well as printing \r to clear the first line) and it works well. :clap: thanks @wmmc88 !