bashtput

Line created with tput gets removed on scroll


I have the following BASH function that takes arguments and displays them at the bottom of the terminal in a new line that's excluded from the scroll region:

bottomLine() {
    clear
    # Get all arguments and assign them to a var
    CONTENT=$@
    # Save cursor position
    tput sc
    # Add a new line
    tput il 1
    # Change scroll region to exclude the last lines
    tput csr 0 $(($(tput lines) - 3))
    # Move cursor to bottom line
    tput cup $(tput lines) 0
    # Clear to the end of the line
    tput el
    # Echo the content on that row
    echo -ne "${CONTENT}"
    # Restore cursor position
    tput rc
}

It's fairly straightforward and works. Thing is, after some commands (sometimes after just a few, sometimes after 15 minutes of work) the line would get scrolled up even though it should be excluded from the scrolling region.

This happens to me in both Tilda and Terminator.

Any help would be appreciated, cheers.

EDIT: The best way to reproduce the issue is if you do several "ls -a, ls -a, ls -a, ls -a" until you reach the bottom of the page, then open a random file with Vi and then do another "ls -a". When you do this, the unscrollable bottom row goes above even though it shouldn't.


Solution

  • My first impulse was to answer that there is no way to freeze the scrollable region once and forever, since any program manipulating the terminal (like vim does) can override your settings. However then I figured out that you can restore the settings through the shell prompting functionality. To this end you must add your terminal control sequence to the PS1 environment variable.

    I've modified your function so that it automatically updates the prompt the first time it is called. To this end I had to split it into two functions.

    bottomLineTermCtlSeq() {
        #clear
        # Save cursor position
        tput sc
        # Add a new line
        tput il 1
        # Change scroll region to exclude the last lines
        tput csr 0 $(($(tput lines) - 3))
        # Move cursor to bottom line
        tput cup $(tput lines) 0
        # Clear to the end of the line
        tput el
        # Echo the content on that row
        cat "${BOTTOM_LINE_CONTENT_FILE}"
        # Restore cursor position
        tput rc
    }
    
    bottomLine() {
        local bottomLinePromptSeq='\[$(bottomLineTermCtlSeq)\]'
        if [[ "$PS1" != *$bottomLinePromptSeq* ]]
        then
            PS1="$bottomLinePromptSeq$PS1"
        fi
        if [ -z "$BOTTOM_LINE_CONTENT_FILE" ]
        then
            export BOTTOM_LINE_CONTENT_FILE="$(mktemp --tmpdir bottom_line.$$.XXX)"
        fi
        echo -ne "$@" > "$BOTTOM_LINE_CONTENT_FILE"
        bottomLineTermCtlSeq
    }
    

    I store the current content of the bottom line in a file rather than in an environment variable so that subprocesses of the top level shell can also manipulate the bottom line.

    Note that I removed the clear command from the terminal manipulation sequence, which means that you may need to call it yourself before calling bottomLine for the first time (when you call it while having reached to the bottom of your screen).

    Also note that the bottom line can be messed up when the terminal window is resized.