Search code examples
windowscmdterminalansi-escapevt100

Windows console: virtual terminal sequences are limited to viewport


I'm trying to implement a clear_terminal() function that only clears a given number of rows, not the entire screen.

In unix terminals this is solved by the following ansi escape sequence:

\033[{rows}A \033[0J

Exactly the same result can be reproduced using the Windows API functions:

hConsoleOutput = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
lpConsoleScreenBufferInfo = CONSOLE_SCREEN_BUFFER_INFO()

ctypes.windll.kernel32.GetConsoleScreenBufferInfo(
    hConsoleOutput,
    ctypes.byref(lpConsoleScreenBufferInfo)
)

lpConsoleScreenBufferInfo.dwCursorPosition.Y -= rows
ctypes.windll.kernel32.SetConsoleCursorPosition(
    hConsoleOutput,
    lpConsoleScreenBufferInfo.dwCursorPosition
)

ctypes.windll.kernel32.FillConsoleOutputCharacterA(
    hConsoleOutput,
    ctypes.c_char(b' '),
    lpConsoleScreenBufferInfo.dwSize.X * rows,
    lpConsoleScreenBufferInfo.dwCursorPosition,
    ctypes.byref(wintypes.DWORD(0))
)

ctypes.windll.kernel32.FillConsoleOutputAttribute(
    hConsoleOutput,
    lpConsoleScreenBufferInfo.wAttributes,
    lpConsoleScreenBufferInfo.dwSize.X * rows,
    lpConsoleScreenBufferInfo.dwCursorPosition,
    ctypes.byref(wintypes.DWORD(0))
)

But this is as Microsoft says legacy. Because starting from Windows 10 TH2 (v1511), cmd.exe support ANSI Escape Sequences. They must be enabled with SetConsoleMode ENABLE_VIRTUAL_TERMINAL_PROCESSING flag.

Enabling this one reveals that escape codes don't work as expected: only a fixed number of lines are cleared. The reason for this is described in the documentation:

Cursor movement will be bounded by the current viewport into the buffer. Scrolling (if available) will not occur.

So using virtual sequences it is not possible to set the cursor position higher than the upper bound of the visible area. Are there any workarounds to remove this unnecessary viewport limitation?

I use the same escape sequence as above. The Windows API solution always cleans up the right number of rows and doesn't run into these limitations.


Solution

  • Even though code \e[3J is not documented by Microsoft, it works like some others. The sequence to clear the screen completely is \e[2J\e[3J, where:

    \e[2J - erase entire screen

    \e[3J - erase saved lines (scrollback)

    Or you can use the code to reset all terminal settings to default: \ec.

    As for the scroll area, according to the POSIX standard, you can only interact with lines within the same visible screen.

    There are no escape sequences which track the buffer position. 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.