Certain SGR attributes are not preserved after a resize sequence #3565

Closed
opened 2026-01-30 23:24:32 +00:00 by claunia · 3 comments
Owner

Originally created by @j4james on GitHub (Aug 26, 2019).

Environment

Windows build number: Version 10.0.18362.239

Steps to reproduce

Open a WSL conhost shell, and execute the following two commands:

printf "\e[30;48;5;214m ORANGE \n\e[8;24;80t ORANGE \e[m\n"
printf "\e[7;38;5;15;48;5;4m REVERSE \n\e[8;24;80t REVERSE \e[m\n"

The first command uses an SGR escape sequence to set the background color to index 214 (a shade of orange), writes out the text "ORANGE", then uses the XTerm window manipulation sequence to resize the screen, and writes out "ORANGE" again.

The second command does the same sort of thing but with different attributes. It sets the colors to bright white (index 15) on blue (index 4), and also enable the reverse video attribute (making it blue on white).

Expected behavior

The screen resize should have no effect on the active SGR attributes, so in each case I'd expect to see two matching lines of text with identical colors.

Actual behavior

In the first test, the second "ORANGE" line is a shade of yellow rather than orange (the exact color will likely depend on your palette).

In the second test, the second "REVERSE" line is not actually reversed - it's white on blue instead of blue on white.

image

Cause

The resize escape sequence is handled by the DispatchCommon::s_ResizeWindow method, which does so by reading the screen buffer info with GetConsoleScreenBufferInfoEx, modifiying just the dwSize and srWindow fields (i.e the buffer and viewport sizes), and then writing the updated info back with SetConsoleScreenBufferInfoEx. The problem is that the CONSOLE_SCREEN_BUFFER_INFOEX structure can't reliable represent the text attributes in the wAttributes field, so they can become corrupted in the process.

The DECCOLM escape sequence, which is handled by the AdaptDispatch::SetColumns method, has essentially the same problem.

The reverse video case could probably be fixed with improvements to the GenerateLegacyAttributes method, but it's never going to be possible to accurately handle every attribute value with a legacy equivalent. I think the real solution is to avoid using the SetConsoleScreenBufferInfoEx method, and try and update the window and buffer size more directly, via SetConsoleWindowInfo and SetConsoleScreenBufferSize. I haven't actually tried that, though, so maybe it's not as simple as I think.

Originally created by @j4james on GitHub (Aug 26, 2019). # Environment ```none Windows build number: Version 10.0.18362.239 ``` # Steps to reproduce Open a WSL conhost shell, and execute the following two commands: printf "\e[30;48;5;214m ORANGE \n\e[8;24;80t ORANGE \e[m\n" printf "\e[7;38;5;15;48;5;4m REVERSE \n\e[8;24;80t REVERSE \e[m\n" The first command uses an SGR escape sequence to set the background color to index 214 (a shade of orange), writes out the text "ORANGE", then uses the XTerm window manipulation sequence to resize the screen, and writes out "ORANGE" again. The second command does the same sort of thing but with different attributes. It sets the colors to bright white (index 15) on blue (index 4), and also enable the reverse video attribute (making it blue on white). # Expected behavior The screen resize should have no effect on the active SGR attributes, so in each case I'd expect to see two matching lines of text with identical colors. # Actual behavior In the first test, the second "ORANGE" line is a shade of yellow rather than orange (the exact color will likely depend on your palette). In the second test, the second "REVERSE" line is not actually reversed - it's white on blue instead of blue on white. ![image](https://user-images.githubusercontent.com/4181424/63697684-4c363480-c815-11e9-87c8-d652a0ca6b89.png) # Cause The resize escape sequence is handled by the [`DispatchCommon::s_ResizeWindow`](https://github.com/microsoft/terminal/blob/9b92986b49bed8cc41fde4d6ef080921c41e6d9e/src/terminal/adapter/DispatchCommon.cpp#L20-L68) method, which does so by reading the screen buffer info with `GetConsoleScreenBufferInfoEx`, modifiying just the `dwSize` and `srWindow` fields (i.e the buffer and viewport sizes), and then writing the updated info back with `SetConsoleScreenBufferInfoEx`. The problem is that the `CONSOLE_SCREEN_BUFFER_INFOEX` structure can't reliable represent the text attributes in the `wAttributes` field, so they can become corrupted in the process. The DECCOLM escape sequence, which is handled by the [`AdaptDispatch::SetColumns`](https://github.com/microsoft/terminal/blob/594a7e4501d1108267388197ec187ca77f82756b/src/terminal/adapter/adaptDispatch.cpp#L1078-L1095) method, has essentially the same problem. The reverse video case could probably be fixed with improvements to the `GenerateLegacyAttributes` method, but it's never going to be possible to accurately handle every attribute value with a legacy equivalent. I think the real solution is to avoid using the `SetConsoleScreenBufferInfoEx` method, and try and update the window and buffer size more directly, via `SetConsoleWindowInfo` and `SetConsoleScreenBufferSize`. I haven't actually tried that, though, so maybe it's not as simple as I think.
Author
Owner

@DHowett-MSFT commented on GitHub (Aug 26, 2019):

Great analysis. This stems from the fact that we tried, in VT dispatch, to use the "public" console API (or whatever local simulacrum we could get our hands on)... we probably need a private resize for VT to hit.

@DHowett-MSFT commented on GitHub (Aug 26, 2019): Great analysis. This stems from the fact that we tried, in VT dispatch, to use the "public" console API (or whatever local simulacrum we could get our hands on)... we probably need a private resize for VT to hit.
Author
Owner

@zadjii-msft commented on GitHub (Aug 26, 2019):

This is also kinda related to #1765/#1795, The DispatchCommon::s_ResizeWindow is a house of horrors. I've wanted to replace it's implementation with a new ConGetSet::PrivateResizeTerminalWindow API for a while now, which would remove the need for us to double resize with SetConsoleScreenBufferInfoEx/SetConsoleWindowSize. It's a mess.

@zadjii-msft commented on GitHub (Aug 26, 2019): This is also kinda related to #1765/#1795, The `DispatchCommon::s_ResizeWindow` is a house of horrors. I've wanted to replace it's implementation with a new `ConGetSet::PrivateResizeTerminalWindow` API for a while now, which would remove the need for us to double resize with `SetConsoleScreenBufferInfoEx`/`SetConsoleWindowSize`. It's a mess.
Author
Owner

@zadjii-msft commented on GitHub (Dec 13, 2021):

Just as a note to ourselves, this is better, but not fixed:

image

(this is 22519)


On 22590 and main (after the fix for #32), April 2022

The reverse one is fixed, it's the orange one that's broken still.

printf "\e[30;48;5;214m ORANGE \n\e[8;24;80t ORANGE \e[m\n"

Guess: On the resize, we're resetting the ACTIVE attributes to the closest one on the 16 color table. That's resulting in the 256-color orange being emitted with a 16-color color instead.

@zadjii-msft commented on GitHub (Dec 13, 2021): Just as a note to ourselves, this is better, but not fixed: ![image](https://user-images.githubusercontent.com/18356694/145857118-ec4127f4-daca-49fa-9aca-f59bb2b79612.png) (this is 22519) <hr> _On `22590` and `main` (after the fix for #32), April 2022_ The reverse one is fixed, it's the orange one that's broken still. ```sh printf "\e[30;48;5;214m ORANGE \n\e[8;24;80t ORANGE \e[m\n" ``` Guess: On the resize, we're resetting the ACTIVE attributes to the closest one on the 16 color table. That's resulting in the 256-color orange being emitted with a 16-color color instead.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/terminal#3565