Automatic scrolling of window when setting cursor position #19303

Open
opened 2026-01-31 06:39:29 +00:00 by claunia · 4 comments
Owner

Originally created by @ssbssa on GitHub (Feb 2, 2023).

Windows Terminal version

1.16.230126001

Windows build number

10.0.19044.2486

Other Software

No response

Steps to reproduce

I mentioned this first in #14759.

You can reproduce the problem with this test program:

#include <windows.h>

int main()
{
  HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  GetConsoleScreenBufferInfo(con, &csbi);

  // use scrollback of 100 lines
  COORD con_size = {
    csbi.srWindow.Right - csbi.srWindow.Left + 1,
    csbi.srWindow.Bottom - csbi.srWindow.Top + 1 + 100
  };
  SetConsoleScreenBufferSize(con, con_size);

  // write 50 lines so the position is not at the top of the scrollback
  for (int i = 0; i < 50; i++)
    WriteConsoleA(con, "line\n", 5, NULL, NULL);

  // stop at escape key
  HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
  INPUT_RECORD ir;
  DWORD didread;
  while (ReadConsoleInput(in, &ir, 1, &didread))
  {
    if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown)
    {
      if (ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)
        break;

      WriteConsoleA(con, &ir.Event.KeyEvent.uChar.AsciiChar, 1, NULL, NULL);
      GetConsoleScreenBufferInfo(con, &csbi);
      // re-set the cursor position where it already is, this is done
      // so the cursor is visible at the new location after the character
      // was printed
      SetConsoleCursorPosition(con, csbi.dwCursorPosition);
      // now the console window is scrolled so that the cursor is at the bottom
      // of the window.
    }
  }

  return 0;
}

There is a difference of behavior when tried with either the conhost.exe of the current Win10, or the OpenConsole.exe of Microsoft.WindowsTerminal_Win10_1.16.10261.0_8wekyb3d8bbwe.msixbundle.

Between entering some keys, scroll the window up or down with the mouse.

Expected Behavior

Once you enter a key, and the cursor position is re-set, it will scroll the window to the cursor, but only if the cursor was not already in the visible part of the window.
Which is exactly how it works in conhost.exe.

Actual Behavior

But with OpenConsole.exe 1.16.230126001, the window is always scrolled so that the cursor is at the bottom of the screen, even if it was already visible before.

Originally created by @ssbssa on GitHub (Feb 2, 2023). ### Windows Terminal version 1.16.230126001 ### Windows build number 10.0.19044.2486 ### Other Software _No response_ ### Steps to reproduce I mentioned this first in #14759. You can reproduce the problem with this test program: ```c #include <windows.h> int main() { HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(con, &csbi); // use scrollback of 100 lines COORD con_size = { csbi.srWindow.Right - csbi.srWindow.Left + 1, csbi.srWindow.Bottom - csbi.srWindow.Top + 1 + 100 }; SetConsoleScreenBufferSize(con, con_size); // write 50 lines so the position is not at the top of the scrollback for (int i = 0; i < 50; i++) WriteConsoleA(con, "line\n", 5, NULL, NULL); // stop at escape key HANDLE in = GetStdHandle(STD_INPUT_HANDLE); INPUT_RECORD ir; DWORD didread; while (ReadConsoleInput(in, &ir, 1, &didread)) { if (ir.EventType == KEY_EVENT && ir.Event.KeyEvent.bKeyDown) { if (ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE) break; WriteConsoleA(con, &ir.Event.KeyEvent.uChar.AsciiChar, 1, NULL, NULL); GetConsoleScreenBufferInfo(con, &csbi); // re-set the cursor position where it already is, this is done // so the cursor is visible at the new location after the character // was printed SetConsoleCursorPosition(con, csbi.dwCursorPosition); // now the console window is scrolled so that the cursor is at the bottom // of the window. } } return 0; } ``` There is a difference of behavior when tried with either the conhost.exe of the current Win10, or the OpenConsole.exe of Microsoft.WindowsTerminal_Win10_1.16.10261.0_8wekyb3d8bbwe.msixbundle. Between entering some keys, scroll the window up or down with the mouse. ### Expected Behavior Once you enter a key, and the cursor position is re-set, it will scroll the window to the cursor, but only if the cursor was not already in the visible part of the window. Which is exactly how it works in conhost.exe. ### Actual Behavior But with OpenConsole.exe 1.16.230126001, the window is always scrolled so that the cursor is at the bottom of the screen, even if it was already visible before.
Author
Owner

@zadjii-msft commented on GitHub (Feb 2, 2023):

Thanks for the repro!

@zadjii-msft commented on GitHub (Feb 2, 2023): Thanks for the repro!
Author
Owner

@j4james commented on GitHub (Feb 2, 2023):

This change in behavior is almost certainly a result of PR #12972, and I suspect it could be difficult to resolve without reintroducing the virtual bottom bugs that PR was attempting to fix.

@j4james commented on GitHub (Feb 2, 2023): This change in behavior is almost certainly a result of PR #12972, and I suspect it could be difficult to resolve without reintroducing the virtual bottom bugs that PR was attempting to fix.
Author
Owner

@rforzachamp821 commented on GitHub (Jun 22, 2023):

Thanks for putting this issue down. I have the same issue and it really sucks, as there's no other API that can perform the functions with GetConsoleScreenBufferInfo() in the meantime.
CONSOLE_SCREEN_BUFFER_INFO::dwCursorPosition.Y is also affected by this, and it maxes out at the vertical console window size when you hit the bottom of the console window to the point where the scrollbar shows up.

@rforzachamp821 commented on GitHub (Jun 22, 2023): Thanks for putting this issue down. I have the same issue and it really sucks, as there's no other API that can perform the functions with GetConsoleScreenBufferInfo() in the meantime. CONSOLE_SCREEN_BUFFER_INFO::dwCursorPosition.Y is also affected by this, and it maxes out at the vertical console window size when you hit the bottom of the console window to the point where the scrollbar shows up.
Author
Owner

@rforzachamp821 commented on GitHub (Oct 29, 2023):

Any updates on the bug? I was able to create a custom detection algorithm for the bug, maybe that helps:

// A function to check for a specific bug affecting CONSOLE_SCREEN_BUFFER_INFO::dwCursorPosition
// positioning reports, in the new Windows Terminal and OpenConsole.exe.
bool ConsoleWTBugCheck() {
	// Get console size
	HANDLE hTest = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_SCREEN_BUFFER_INFO csbiTest;
	GetConsoleScreenBufferInfo(hTest, &csbiTest);
	int nConsoleHeight = csbiTest.srWindow.Bottom - csbiTest.srWindow.Top;

	// Spam enters until a few more than console bottom
	for (int i = 0; i <= (nConsoleHeight + 4); i++) {
		std::cout << '\n';
	}

	// Get the console cursor position after that
	GetConsoleScreenBufferInfo(hTest, &csbiTest);
	int nVerticalCPosition = csbiTest.dwCursorPosition.Y;
	cls();

	if (nVerticalCPosition <= nConsoleHeight) {
		return true;
	}
	else {
		return false;
	}
}

This code gets the height of the terminal and spams newlines after at least 1 above the terminal height (i used 4 for some reason). The number of newlines entered is recorded. It then checks the new reported height by the GetConsoleScreenBufferInfo() function. If that function reports anything smaller than the number of newlines entered, then the bug is there, else it isn't. A cls() function is there to clear the screen after the newlines so the user doesn't see them.

It is required that the test is done when the cursor position is at the highest row (0).

@rforzachamp821 commented on GitHub (Oct 29, 2023): Any updates on the bug? I was able to create a custom detection algorithm for the bug, maybe that helps: ```C++ // A function to check for a specific bug affecting CONSOLE_SCREEN_BUFFER_INFO::dwCursorPosition // positioning reports, in the new Windows Terminal and OpenConsole.exe. bool ConsoleWTBugCheck() { // Get console size HANDLE hTest = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbiTest; GetConsoleScreenBufferInfo(hTest, &csbiTest); int nConsoleHeight = csbiTest.srWindow.Bottom - csbiTest.srWindow.Top; // Spam enters until a few more than console bottom for (int i = 0; i <= (nConsoleHeight + 4); i++) { std::cout << '\n'; } // Get the console cursor position after that GetConsoleScreenBufferInfo(hTest, &csbiTest); int nVerticalCPosition = csbiTest.dwCursorPosition.Y; cls(); if (nVerticalCPosition <= nConsoleHeight) { return true; } else { return false; } } ``` This code gets the height of the terminal and spams newlines after at least 1 above the terminal height (i used 4 for some reason). The number of newlines entered is recorded. It then checks the new reported height by the GetConsoleScreenBufferInfo() function. If that function reports anything smaller than the number of newlines entered, then the bug is there, else it isn't. A cls() function is there to clear the screen after the newlines so the user doesn't see them. It is required that the test is done when the cursor position is at the highest row (0).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#19303