How to get the screen state as set by tput smcup/rmcup?

3.9k Views Asked by At

Whilst I know that tput rmcup returns from the "alternate screen" (referred to as "cup mode" in man 5 terminfo) and restores the saved screen, it does have the side-effect of repositioning the cursor.

So if tput smcup was called, tput rmcup restores the screen and repositions the cursor, but if you then type a few more commands or press enter a few times and then use tput rmcup again, the cursor returns to the original saved position.

One use case is in a bash script that replays a terminal recording [using scriptreplay]: if the script ends prematurely without [the equivalent of] calling tput rmcup then I'd like to be able to detect that in my bash script and call tput rmcup automatically.

So in short, I'd like to be able to determine what the current screen state is; i.e., is it the "alternate screen" or is it the "normal screen"?

1

There are 1 best solutions below

6
On

If you're using xterm, then you can ask it what mode it's using. Although the alternate-screen feature is an xterm feature rather than "DEC", the setting is grouped with the DECSET modes described in XTerm Control Sequences:

CSI ? Pm h
          DEC Private Mode Set (DECSET).
...
            Ps = 1 0 4 7  -> Use Alternate Screen Buffer, xterm.  This
          may be disabled by the titeInhibit resource.
            Ps = 1 0 4 8  -> Save cursor as in DECSC, xterm.  This may
          be disabled by the titeInhibit resource.
            Ps = 1 0 4 9  -> Save cursor as in DECSC, xterm.  After sav-
          ing the cursor, switch to the Alternate Screen Buffer, clear-
          ing it first.  This may be disabled by the titeInhibit

The DECRQM control can be used to query the terminal:

CSI ? Ps$ p
          Request DEC private mode (DECRQM).  For VT300 and up, reply
          DECRPM is
            CSI ? Ps; Pm$ y
          where Ps is the mode number as in DECSET/DECSET, Pm is the
          mode value as in the ANSI DECRQM.

That is, your script could

printf '\033[?1049$p'

and read the result back, expecting something like \033[?1049;1$y

Here is a quick demo:

#!/bin/sh                                                                      

unexpected() {
        result=$(echo "$check"|sed -e 's/^@/\\033/')
        printf '? unexpected reply: %s\n' "$result"
        exit 1
}

exec </dev/tty
old=`stty -g`
stty raw -echo min 0  time 5
printf '\033[?1049$p'
read status
stty $old

if [ -n "$status" ]
then
        check=$(echo "$status" |tr '\033' '@')
        if [ "$check" != "$status" ]
        then
                case "$check" in
                '@[?1049;1$y')
                        echo "alternate screen"
                        ;;
                '@[?1049;2$y')
                        echo "normal screen"
                        ;;
                *)
                        unexpected
                        ;;
                esac
        else
                unexpected
        fi
else
        echo "? no reply from terminal"
fi

Of course, if you're not using xterm, your mileage may vary...