crossterm icon indicating copy to clipboard operation
crossterm copied to clipboard

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

Open wmmc88 opened this issue 2 years ago • 7 comments

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

wmmc88 avatar May 29 '22 16:05 wmmc88

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.

sigmaSd avatar May 31 '22 20:05 sigmaSd

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).

wmmc88 avatar Jun 04 '22 23:06 wmmc88

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 avatar Jun 05 '22 07:06 sigmaSd

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.

sigmaSd avatar Jun 05 '22 08:06 sigmaSd

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.

TimonPost avatar Jun 06 '22 15:06 TimonPost

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.

bartoszpiech avatar Apr 07 '23 20:04 bartoszpiech

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 !

wiktor-k avatar Oct 12 '23 09:10 wiktor-k