Scrolling not handled correctly #19163

Closed
opened 2026-01-31 06:35:35 +00:00 by claunia · 5 comments
Owner

Originally created by @cnayoung on GitHub (Jan 1, 2023).

When a VT100 ‘r’ code is used to create a scroll region in the console window, as lines scroll off the top of the window, their content is lost and the displayed lines may be repeated and printed in an incorrect order. The same code works as expected in old command console, but the issue occurs in Windows Terminal from both PowerShell and Command Prompts.

To reproduce, I’ve create a basic C# console application. The code ensures that VT100 support is switched on (it is off by default when using the old command console). It sets the buffer height to an appropriate value. It then creates a status line in the last line of the console window and sets the rest of the window area as a scrollable region (ESC[0;28r). Then, having set the cursor to the top left of the window, it prints out 50 lines.

If the code is run under the old command console, all is well. When you scroll up, the lines that have scrolled off the screen are visible. However, the same code (compiled binary) does not work correctly under Windows Terminal. The output is garbled with some lines repeated, and displayed out of order. The behaviour of the code can be improved by adding a delay (100ms sleep) after each line is output. This slows the code down significantly, but the lines are at least output in the correct order. However, when you scroll up, the lines that have scrolled off the screen are empty.

NB., if you build a debug build under Visual Studio, it will probably run a bit better (less garbled) than a release build.

I’m using version 1.15.3466.0 of Windows Terminal on version 21H2 of Windows 11 Pro (22000.1335). I have not upgraded yet to 22H2 due to an unresolved boot issue under that version on my laptop. I have compiled the code under both .NET 6 and .NET 7.

Here is the source code:

using System.Runtime.InteropServices;
using static System.Console;

DoTest();

void DoTest()
{
    // Ensure that VT100 code support is enabled - Windows only.
    WindowsVt.Enable();

    BufferHeight = 60;
    SetCursorPosition(0,WindowHeight - 1);
    Write("This is a status line at the bottom of the console.");
    WriteLine($"\u001b[0;{Console.WindowHeight - 2}r");
    SetCursorPosition(0, 0);

    for (var i = 0; i < 50; i++)
    {
        WriteLine(i.ToString());
        //Thread.Sleep(100);  // Work around for Windows Terminal
    }

    ReadKey();
}

public static class WindowsVt {

    private const int STD_OUTPUT_HANDLE = -11;

    private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;

    private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;

    public static bool Enable() {
        var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

        if (!GetConsoleMode(iStdOut, out var outConsoleMode)) {
            return false;
        }

        outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;

        return SetConsoleMode(iStdOut, outConsoleMode);
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetConsoleMode(nint hConsoleHandle, out uint lpMode);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetConsoleMode(nint hConsoleHandle, uint dwMode);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern nint GetStdHandle(int nStdHandle);
}

ConsoleScrollingExample.zip

Originally created by @cnayoung on GitHub (Jan 1, 2023). When a VT100 ‘r’ code is used to create a scroll region in the console window, as lines scroll off the top of the window, their content is lost and the displayed lines may be repeated and printed in an incorrect order. The same code works as expected in old command console, but the issue occurs in Windows Terminal from both PowerShell and Command Prompts. To reproduce, I’ve create a basic C# console application. The code ensures that VT100 support is switched on (it is off by default when using the old command console). It sets the buffer height to an appropriate value. It then creates a status line in the last line of the console window and sets the rest of the window area as a scrollable region (ESC[0;28r). Then, having set the cursor to the top left of the window, it prints out 50 lines. If the code is run under the old command console, all is well. When you scroll up, the lines that have scrolled off the screen are visible. However, the same code (compiled binary) does not work correctly under Windows Terminal. The output is garbled with some lines repeated, and displayed out of order. The behaviour of the code can be improved by adding a delay (100ms sleep) after each line is output. This slows the code down significantly, but the lines are at least output in the correct order. However, when you scroll up, the lines that have scrolled off the screen are empty. NB., if you build a debug build under Visual Studio, it will probably run a bit better (less garbled) than a release build. I’m using version 1.15.3466.0 of Windows Terminal on version 21H2 of Windows 11 Pro (22000.1335). I have not upgraded yet to 22H2 due to an unresolved boot issue under that version on my laptop. I have compiled the code under both .NET 6 and .NET 7. Here is the source code: ---------------------------------------------------------------------------------------------- ```csharp using System.Runtime.InteropServices; using static System.Console; DoTest(); void DoTest() { // Ensure that VT100 code support is enabled - Windows only. WindowsVt.Enable(); BufferHeight = 60; SetCursorPosition(0,WindowHeight - 1); Write("This is a status line at the bottom of the console."); WriteLine($"\u001b[0;{Console.WindowHeight - 2}r"); SetCursorPosition(0, 0); for (var i = 0; i < 50; i++) { WriteLine(i.ToString()); //Thread.Sleep(100); // Work around for Windows Terminal } ReadKey(); } public static class WindowsVt { private const int STD_OUTPUT_HANDLE = -11; private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008; public static bool Enable() { var iStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if (!GetConsoleMode(iStdOut, out var outConsoleMode)) { return false; } outConsoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; return SetConsoleMode(iStdOut, outConsoleMode); } [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GetConsoleMode(nint hConsoleHandle, out uint lpMode); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool SetConsoleMode(nint hConsoleHandle, uint dwMode); [DllImport("kernel32.dll", SetLastError = true)] private static extern nint GetStdHandle(int nStdHandle); } ``` ---------------------------------------------------------------------------------------------- [ConsoleScrollingExample.zip](https://github.com/microsoft/terminal/files/10329398/ConsoleScrollingExample.zip)
claunia added the Resolution-Duplicate label 2026-01-31 06:35:35 +00:00
Author
Owner

@j4james commented on GitHub (Jan 1, 2023):

Thanks for the detailed report. The scroll margin thing is a known problem (see issue #3673). We just don't have a way to propagate that state over the conpty connection at this point in time. In the long term it could be solved by the passthrough mode (#1173).

The output is garbled with some lines repeated, and displayed out of order

This bit I'm not so sure about. But if the BufferHeight = 60; statement is intended to resize the window, that could be the reason. We don't yet support resizing in Windows Terminal (that's tracked in issue #5094), so any console resize operation can leave the conpty buffer in a confused state, which might explain the corruption you're seeing.

@j4james commented on GitHub (Jan 1, 2023): Thanks for the detailed report. The scroll margin thing is a known problem (see issue #3673). We just don't have a way to propagate that state over the conpty connection at this point in time. In the long term it could be solved by the passthrough mode (#1173). > The output is garbled with some lines repeated, and displayed out of order This bit I'm not so sure about. But if the `BufferHeight = 60;` statement is intended to resize the window, that could be the reason. We don't yet support resizing in Windows Terminal (that's tracked in issue #5094), so any console resize operation can leave the conpty buffer in a confused state, which might explain the corruption you're seeing.
Author
Owner

@cnayoung commented on GitHub (Jan 2, 2023):

Thanks. That's clear.

The BufferHeight property in the .NET Console class does not resize the window. It simply sets the buffer height, presumably using SetConsoleScreenBufferInfoEx() and the dwSize field in the CONSOLE_SCREEN_BUFFER_INFO_EX struct (on Windows).

There are separate properties for getting or setting the console window dimensions, and also a SetWindowSize() method for resizing the window, presumably using SetConsoleWindowInfo().

These properties and methods are very sensitive, of course, to the OS platform, and are only fully supported on Windows. Setting the BufferHeight property, for example (as used in the example code) is only supported on Windows, while getting the BufferHeight value is supported on Linux and FreeBSD, but not on iOS or Android.

@cnayoung commented on GitHub (Jan 2, 2023): Thanks. That's clear. The BufferHeight property in the .NET Console class **does not** resize the window. It simply sets the buffer height, presumably using SetConsoleScreenBufferInfoEx() and the dwSize field in the CONSOLE_SCREEN_BUFFER_INFO_EX struct (on Windows). There are separate properties for getting or setting the console window dimensions, and also a SetWindowSize() method for resizing the window, presumably using SetConsoleWindowInfo(). These properties and methods are very sensitive, of course, to the OS platform, and are only fully supported on Windows. Setting the BufferHeight property, for example (as used in the example code) is only supported on Windows, while getting the BufferHeight value is supported on Linux and FreeBSD, but not on iOS or Android.
Author
Owner

@cnayoung commented on GitHub (Jan 2, 2023):

...and just in case it's any use in the future, here are screen shots of the output from the code I attached. In both cases, I ran the code in a smaller window and then maximised it to show all the scrolled content. As you can see, the output in Windows Terminal is seriously corrupted, but improves with the Thread.Sleep() workaround.

Classic Command Console
ClassicCommandConsole

Windows Terminal (Release build without Thread.Sleep() workaround)
WindowsTerminal

Windows Terminal (Release build with Thread.Sleep() workaround)
WindowsTerminalWithWorkAround

With the workaround, the output is not corrupted, but also, you cannot scroll back up to see the empty lines.

@cnayoung commented on GitHub (Jan 2, 2023): ...and just in case it's any use in the future, here are screen shots of the output from the code I attached. In both cases, I ran the code in a smaller window and then maximised it to show all the scrolled content. As you can see, the output in Windows Terminal is seriously corrupted, but improves with the Thread.Sleep() workaround. Classic Command Console ![ClassicCommandConsole](https://user-images.githubusercontent.com/3735271/210228774-b61c6e1a-de1c-4a56-8a95-dd6da3338774.png) Windows Terminal (Release build without Thread.Sleep() workaround) ![WindowsTerminal](https://user-images.githubusercontent.com/3735271/210228779-c9f2281b-7e34-40df-b098-9288247f6b4e.png) Windows Terminal (Release build with Thread.Sleep() workaround) ![WindowsTerminalWithWorkAround](https://user-images.githubusercontent.com/3735271/210236364-7d7f7c79-6e08-471a-ab0d-1ae15fd4fcb5.png) With the workaround, the output is not corrupted, but also, you cannot scroll back up to see the empty lines.
Author
Owner

@zadjii-msft commented on GitHub (Jan 3, 2023):

Thanks for the detailed writeup! I'd agree with the assesment that this seems to be a /dupe of #3673. Let's call it that until proven otherwise. Thanks!

@zadjii-msft commented on GitHub (Jan 3, 2023): Thanks for the detailed writeup! I'd agree with the assesment that this seems to be a /dupe of #3673. Let's call it that until proven otherwise. Thanks!
Author
Owner

@ghost commented on GitHub (Jan 3, 2023):

Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!

@ghost commented on GitHub (Jan 3, 2023): Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#19163