Shell – Save cursor position and restore it in terminal

asyncpromptshellterminalterminfo

I am playing with some terminal capabilities to create async prompt output on bash. I want to get something like this: while outputting string in PROMPT_COMMAND, save terminal cursor position, print put ... and go do other stuff (printing other info for example); then some time later go back to memorized position and print something new. PS1 is designed that way that command itself is put on newline, so that async output is just text in terminal.

I found out cup terminfo entry to move cursor to given position, an there are escape codes to get current position (for examble, see this question). But for me, seems like all of them work with cursor position on screen, rather than in terminal buffer. That means that after putting new line my remembered position will be wrong.

Are there any way to work with terminal cursor position to store and restore cursor position based on buffer position? You can assume that the remembered position is still in visible part of the buffer (so you actually can put cursor there), but it might go up several lines up. For now, let's not consider race conditions. If no, I will try to find other tricks.

There is zsh shell which actually has async prompt support, but I don't actually know will it work if before output user will press Enter to go to new line.

Best Answer

Most of the terminals you are likely to use implement escape sequences for saving and restoring the cursor position. Those are the sc (save cursor) and rc (restore cursor) capabilities in the terminal description, which you can use via tput (just like cup):

tput sc
tput cup 50 10
echo some stuff
echo more stuff
tput rc

However, if your program writes enough text to force scrolling, the restored position will still be on the same place on the terminal's screen. It does not move along with the scrolled text.

POSIX, by the way, documents only part of tput because the tool is actually part of another standard, X/Open Curses. That is summarized in the portability section of the manual page.

Back to the question: there are no escape sequences which track the buffer position (accounting for scrolling). To get that, your application would have to keep track of what was sent to the screen. Solving that sort of problem is what the curses library is for. In a plain command-line application, there is not much that you can do.