Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

conhost: SetConsoleWindowInfo broken when used in alternate screen buffer #13741

Open
Gyross opened this issue Aug 15, 2022 · 6 comments
Open
Labels
Area-Server Down in the muck of API call servicing, interprocess communication, eventing, etc. Issue-Bug It either shouldn't be doing this or needs an investigation. Product-Conhost For issues in the Console codebase
Milestone

Comments

@Gyross
Copy link

Gyross commented Aug 15, 2022

Windows Terminal version

No response

Windows build number

10.0.19041.1806

Other Software

conhost.exe 10.0.19041.1566
conhostV1.dll 10.0.19041.21
Visual Studio 2019 version 16.11.18

Steps to reproduce

Use this test program:

#include <windows.h>
#include <iostream>

bool get_info_and_print(HANDLE h_stdout, CONSOLE_SCREEN_BUFFER_INFO& buf_info)
{
	if (!GetConsoleScreenBufferInfo(h_stdout, &buf_info)) {
		std::cerr << "Failed to get screen buffer info.\n";
		return false;
	}

	std::cout << "Screen buffer size: "
		<< buf_info.dwSize.X << "x" << buf_info.dwSize.Y << "\n";
	std::cout << "Window size: "
		<< buf_info.srWindow.Right + 1 - buf_info.srWindow.Left << "x"
		<< buf_info.srWindow.Bottom + 1 - buf_info.srWindow.Top << "\n\n";
	std::cin.get();
	return true;
}

// Set screen buffer size, then set the window size to maximum.
// Revert back to original window size and buffer size afterward.
// Assume initial console size is smaller than 150x45, larger than 10x10
bool do_test()
{
	constexpr COORD target_buffer_size{ 150, 45 };

	HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
	if (h_stdout == INVALID_HANDLE_VALUE) return 1;

	std::cout << "Orignial buffer & window\n";

	// Get original screen buffer info
	CONSOLE_SCREEN_BUFFER_INFO original_buf_info;
	if (!get_info_and_print(h_stdout, original_buf_info)) return false;

	// Set the screen buffer to target size
	if (!SetConsoleScreenBufferSize(h_stdout, target_buffer_size)) {
		std::cerr << "Failed to set buffer size.\n";
	}

	std::cout << "Made buffer larger\n";

	// Get buffer info after adjustment
	CONSOLE_SCREEN_BUFFER_INFO larger_buf_info;
	if (!get_info_and_print(h_stdout, larger_buf_info)) return false;

	std::cout << "GetConsoleScreenBufferInfo gives dwMaximumWindowSize as "
		<< larger_buf_info.dwMaximumWindowSize.X << "x"
		<< larger_buf_info.dwMaximumWindowSize.Y << "!\n";

	// Attempt to set window size to maximum allowed.
	SMALL_RECT larger_window_size{
		0, 0,
		larger_buf_info.dwMaximumWindowSize.X - 1,
		larger_buf_info.dwMaximumWindowSize.Y - 1
	};

	// ################################################################
	// THIS FAILS IN ALT BUFFER WITH ERROR_INVALID_PARAMETER DESPITE
	// ADHERING TO THE PARAMETER LIMITS GIVEN BY ConsoleScreenBufferInfo
	if (!SetConsoleWindowInfo(h_stdout, TRUE, &larger_window_size)) {
		DWORD err_code = GetLastError();
		std::cerr << "Failed to set window size. Err code: " << err_code << "\n";
	}

	std::cout << "Also made window larger\n";
	// Get buffer info after adjustment
	CONSOLE_SCREEN_BUFFER_INFO larger_buf_info_temp;
	if (!get_info_and_print(h_stdout, larger_buf_info_temp)) return false;

	// Revert window
	if (!SetConsoleWindowInfo(h_stdout, TRUE, &original_buf_info.srWindow)) {
		std::cerr << "Failed to revert window size\n";
	}

	if (!SetConsoleScreenBufferSize(h_stdout, original_buf_info.dwSize)) {
		std::cerr << "Failed to revert screen buffer size\n";
	}

	// Attempt to make the window size smaller than the original size
	SMALL_RECT smaller_window_size {
		original_buf_info.srWindow.Left,
		original_buf_info.srWindow.Top,
		original_buf_info.srWindow.Right - 10,
		original_buf_info.srWindow.Bottom - 10
	};

	// ################################################################
	// THIS FAILS IN ALT BUFFER BUT RETURNS NONZERO (SUCCESS)
	if (!SetConsoleWindowInfo(h_stdout, TRUE, &smaller_window_size)) {
		DWORD err_code = GetLastError();
		std::cerr << "Failed to set window size. Err code: " << err_code << "\n";
	}

	std::cout << "Made window smaller\n";
	// Get buffer info after adjustment
	CONSOLE_SCREEN_BUFFER_INFO smaller_buf_info_temp;
	if (!get_info_and_print(h_stdout, smaller_buf_info_temp)) return false;

	// Revert window
	if (!SetConsoleWindowInfo(h_stdout, TRUE, &original_buf_info.srWindow)) {
		std::cerr << "Failed to revert window size\n";
	}

	return true;
}

int main()
{
	// Attempt to enable virtual terminal prcessing
	HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
	if (h_stdout == INVALID_HANDLE_VALUE) return 1;

	DWORD stdout_mode;
	if (!GetConsoleMode(h_stdout, &stdout_mode)) return 1;

	if (!SetConsoleMode(h_stdout, stdout_mode |
		ENABLE_VIRTUAL_TERMINAL_PROCESSING |
		ENABLE_PROCESSED_OUTPUT))
	{
		return 1;
	}

	std::cout << "Testing on main screen buffer.\n";
	do_test();

	// Switch to alternate screen buffer
	std::cout << "\x1b[?1049h" << std::flush;

	std::cout << "Testing on alternate screen buffer.\n";
	do_test();

	// Switch back to main screen buffer
	std::cout << "\x1b[?1049l" << std::flush;

	// Restore console mode
	SetConsoleMode(h_stdout, stdout_mode);

	return 0;
}

Expected Behavior

No response

Actual Behavior

Recording compiled with Visual Studio 2019 version 16.11.18, default console project with Debug/x86 target.

rqUpmv2sfr.mp4
  1. When trying to increase the window size to the maximum size allowed according to GetConsoleScreenBufferInfo, SetConsoleWindowInfo returns 0 with the invalid parameters error code.
  2. When trying to decrease the window size, SetConsoleWindowInfo returns non-zero indicating success, but the actual window size remains unchanged.

I don't know if resizing is ever supposed to work in the alternate screen buffer, but even if it's supposed to fail, the way it fails seems pretty nonsensical to me.

@Gyross Gyross added the Issue-Bug It either shouldn't be doing this or needs an investigation. label Aug 15, 2022
@ghost ghost added Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Tag-Fix Doesn't match tag requirements labels Aug 15, 2022
@Gyross
Copy link
Author

Gyross commented Aug 15, 2022

As an aside, can you explain the behaviour of the scrollbars?

  • When in main buffer, there are no scrollbars when window size == buffer size, and there are both horizontal and vertical scrollbars when window size X < buffer size X && window size Y < buffer size Y.
  • When in alt buffer, there is a disabled vertical scrollbar when window size == buffer size, and there is an enabled vertical scrollbar, but no horizontal scrollbar when window size X < buffer size X && window size Y < buffer size Y.

Is all this just conhost only being able to halfway enforce the buffer size == window size rule for alt buffer?

@carlos-zamora carlos-zamora added this to the Backlog milestone Apr 26, 2023
@carlos-zamora carlos-zamora added Product-Conhost For issues in the Console codebase Area-Server Down in the muck of API call servicing, interprocess communication, eventing, etc. and removed Needs-Triage It's a new issue that the core contributor team needs to triage at the next triage meeting Needs-Tag-Fix Doesn't match tag requirements labels Apr 26, 2023
@BDisp
Copy link

BDisp commented Aug 8, 2023

The limitation of Windows Terminal being ignoring the SetConsoleScreenBufferSize to resize the buffer to the windows size is killing me. Forces the WT showing the vertical scroll bar when we don't want it. Please can someone explains why we can't resize the buffer to the same size of the visible window, when our app handles the available space? Due the others tabs? Each tab must have is own buffer size and not depends of the WT window size.

@zadjii-msft
Copy link
Member

What you're describing sounds more like #5094. This is specifically a bug in resizing the alt buffer with SetConsoleWindowInfo in conhost.

er, wait though, is it? It sounds like you're trying to use SetConsoleScreenBufferSize to get rid of the scrollback, in terminal? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread)

@BDisp
Copy link

BDisp commented Aug 9, 2023

What you're describing sounds more like #5094. This is specifically a bug in resizing the alt buffer with SetConsoleWindowInfo in conhost.

@zadjii-msft thanks for your feedback. Yes you are right it's more like #5094 and you'll see some comments I posted there. I don't have any issue by resizing the windows and the buffer with the same size with conhost. The trick is first resizing the buffer with the SetConsoleScreenBufferInfoEx(ScreenBuffer, ref csbi) method and then resizing the window with the SetConsoleWindowInfo(OutputHandle, true, ref winRect) method. Finally I call the SetConsoleScreenBufferInfoEx (ScreenBuffer, ref csbi) again. This only work because I use two separate handles, one for the window size (OutputHandle) and other for the buffer size (ScreenBuffer). If we use the same handle for both it will throw an exception, of course, because conhost uses the same handle.

er, wait though, is it? It sounds like you're trying to use SetConsoleScreenBufferSize to get rid of the scrollback, in terminal? Why wouldn't you just use the alt buffer then? (this may warrant a new discussion thread)

I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back. What do you mean by using the alt buffer?
Resizing the buffer with Win API console app using conhost, cmd, powershell, conemu works well, but not on Windows Terminal. The only workaround that works on WT is using "\x1b[3J" escape sequence, but it shouldn't being necessary.

@zadjii-msft
Copy link
Member

I only want to get rid the scrollback while my app is running but when I exit I restore the saved original back

Oh that's like, literally the whole idea of the "alternate screen buffer"! That's the thing that apps like vim, less, emacs, tmux, etc use to let them draw a full-window application, without a scrollback, and then dismiss it simply. Our docs are here. Basically, just SetConsoleMode with ENABLE_VT_PROCESSING, then print a "\x1b[?1049h" to enter the alt buffer, and exit it with "\x1b[?1049l". Pretty sure this sample (extremely dated) has an example of using the alt buffer from a c++ app.

@BDisp
Copy link

BDisp commented Aug 9, 2023

Thanks for your attention. That was what I did for the NetDriver that essential use .NET System.Console for cross platform where for Windows I use Win API with ENABLE_VT_PROCESSING only for accepting escape sequences for keystrokes/mouse handle and for Linux is more straight way. On Windows it's very slower than on Linux. Only for Windows we have a WindowsDriver which basically use native Win API for keystroke/mouse handle, which is much more faster. Since it doesn't uses ENABLE_VT_PROCESSING only Windows Terminal recognizes escape sequences but not other terminal, which only recognize Win API. If Windows Terminal allows Win API functions on a Windows environment would be great. Believe me, conhost, cmd, powershell take a long time to interpret escape sequence, mainly with the mouse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Server Down in the muck of API call servicing, interprocess communication, eventing, etc. Issue-Bug It either shouldn't be doing this or needs an investigation. Product-Conhost For issues in the Console codebase
Projects
None yet
Development

No branches or pull requests

4 participants