Home > Net >  Why do newlines break ANSI cursors?
Why do newlines break ANSI cursors?

Time:09-17

In a contrived example, if I have the following:

sup="$(printf "\e[s" > /dev/tty; printf "one" > /dev/tty; printf "\e[u\e[Jtwo" > /dev/tty)"

The output will successfully erase one leaving only:

two

However, if I use echo "one" to print a newline with it:

sup="$(printf "\e[s" > /dev/tty; echo "one" > /dev/tty; printf "\e[u\e[Jtwo" > /dev/tty)"

Then the output is:

one
two

Why would the newline break the cursor handling? And how could I work around it?

A more comprehensive example would be:

sup="$(printf "\e[s" > /dev/tty; for (( i=0; i<5; i  )); do echo -e "a random number is:\n$RANDOM" > /dev/tty; sleep 1; printf "\e[u\e[J" > /dev/tty; done; echo 'result')"
echo "sup=$sup" # sup=result

CodePudding user response:

I suspect that you're writing to the last line of the window. Writing a newline will cause the window contents to scroll. When you restore the cursor position with ESC [ u, it returns to the physical position in the window that was saved with ESC [ s, not the position in the scroll buffer. But the word one will have scrolled up one line, so two will be written to the line after it rather than overwriting it.

CodePudding user response:

As @Barmer's answer alluded to, the reason why restore cursor does not work, is that saving the cursor only saves the left/right margins, not the row:

Saves the cursor position/state in SCO console mode.[22] In vertical split screen mode, instead used to set (as CSI n ; n s) or reset left and right margins.[23] https://en.wikipedia.org/wiki/ANSI_escape_code#CUP

I was able to come up with a portable solution that does not need me to count line numbers.

tty.bash

#!/usr/bin/env bash

# sourced from:
# https://stackoverflow.com/a/69138082/130638
# inspired by:
# https://unix.stackexchange.com/a/88304/50703
# https://stackoverflow.com/a/2575525/130638
# https://askubuntu.com/a/366158/22776
# https://stackoverflow.com/a/5810220/130638
get_tty_snapshot () {
    local pos oldstty y x y_offset="${1:-0}" x_offset="${2:-0}"
    exec < /dev/tty
    oldstty=$(stty -g)
    stty raw -echo min 0
    echo -en "\e[6n" > /dev/tty
    IFS=';' read -r -d R -a pos
    stty "$oldstty"
    y="$((${pos[0]:2} - 2   y_offset))"
    x="$((pos[1] - 1   x_offset))"
    echo -en "\e[${y};${x}H\e[J"
}
use_tty_snapshot () {
    echo -en "$1" > /dev/tty
}

embed.bash

#!/usr/bin/env bash
source "./tty.bash"

tty_snapshot="$(get_tty_snapshot)"

for (( i=0; i<5; i  )); do
    echo -e "one random number $RANDOM\nanother random number: $RANDOM" > /dev/tty
    sleep 1
    use_tty_snapshot "$tty_snapshot"
done

echo 'result'

example.bash

#!/usr/bin/env bash

echo "ask result"
result="$(./test-cursor-embed)"
echo "got result=[$result]"
  • Related