-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
Consolidate the color palette APIs #11784
Conversation
VERIFY_IS_FALSE(term.SetColorTableEntry(256, 100)); | ||
VERIFY_IS_FALSE(term.SetColorTableEntry(512, 100)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 256 test is no longer applicable because the color table is now larger than 256, and I didn't want to leave another test case right on the edge when it's very likely to grow again. The 512 test case still covers the concept of an out-of-range value.
Long term I'd really like to get rid of these boolean error returns anyway, but that's a separate issue.
// If we're setting the default foreground or background colors | ||
// we need to make sure the index is correctly set as well. | ||
if (tableIndex == TextColor::DEFAULT_FOREGROUND) | ||
{ | ||
gci.SetDefaultForegroundIndex(TextColor::DEFAULT_FOREGROUND); | ||
} | ||
if (tableIndex == TextColor::DEFAULT_BACKGROUND) | ||
{ | ||
gci.SetDefaultBackgroundIndex(TextColor::DEFAULT_BACKGROUND); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At some point I'm hoping this code might be moved up to the AdaptDispatch
/TerminalDispatch
level, since there are separate code paths for the default colors, which should be able to set these index values unconditionally. I just didn't want to overcomplicate the PR with the introduction of index APIs until we really need them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I feel you there. There's going to be a lot more we want to share at that level going forward.
_defaultForeground = gci.GetDefaultForeground(); | ||
_defaultBackground = gci.GetDefaultBackground(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values were just being "cached" here to optimise the LookupAttributeColors
call below, but that's no longer needed now that the default index positions are essentially precalculated at startup.
altCursor.SetStyle(myCursor.GetSize(), myCursor.GetColor(), myCursor.GetType()); | ||
altCursor.SetStyle(myCursor.GetSize(), myCursor.GetType()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that the cursor color is stored in the color table, it doesn't need to be copied backwards and forwards between the alt buffer cursor and the main buffer - it's always the same everywhere.
@@ -182,7 +181,6 @@ void Selection::_RestoreDataToCursor(Cursor& cursor) noexcept | |||
{ | |||
cursor.SetSize(_ulSavedCursorSize); | |||
cursor.SetIsVisible(_fSavedCursorVisible); | |||
cursor.SetColor(_savedCursorColor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with the alt buffer, there's no longer a need to save the cursor color here, because it's not going to be affected by the switch to a selection cursor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what the purpose of this code was; it suggests that there's a different cursor during selection?
const auto defaultForeground = gci.GetDefaultForeground(); | ||
const auto defaultBackground = gci.GetDefaultBackground(); | ||
const auto GetAttributeColors = [=, &gci](const auto& attr) { | ||
return gci.LookupAttributeColors(attr, defaultForeground, defaultBackground); | ||
return gci.LookupAttributeColors(attr); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is another case where we were attempting to optimise the LookupAttributeColors
call, which is no longer necessary.
#define SET_FIELD_AND_SIZE(x) FIELD_OFFSET(Settings, x), RTL_FIELD_SIZE(Settings, x) | ||
#define SET_FIELD_AND_SIZE(x) UFIELD_OFFSET(Settings, x), RTL_FIELD_SIZE(Settings, x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why this wasn't a problem before, but FIELD_OFFSET
returns a LONG
, which then has to be downcast to a DWORD
, which the audit doesn't like. UFIELD_OFFSET
returns a DWORD
.
virtual bool SetDefaultForeground(const DWORD color) noexcept = 0; | ||
virtual bool SetDefaultBackground(const DWORD color) noexcept = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I briefly thought "why not leave these in the header and just have them like instantly forward to the other one as a sort of windowsx.h
shorthand so folks won't get too confused by having to pull a table index to adjust the defaults." but then I thought against it as it's really not that complicated to assume all colors are in the table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I'm glad for the net negative in code as well. I think it makes sense to just have colors be all the problem of the table. Thanks as always!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is excellent, as always. Not a qualm in the world. 😄
Thanks for doing this!
As an aside, I was unable to figure out what the VT525 indexed color operations are, and I'm very curious 😄
static constexpr size_t DEFAULT_FOREGROUND = 256; | ||
static constexpr size_t DEFAULT_BACKGROUND = 257; | ||
static constexpr size_t CURSOR_COLOR = 258; | ||
static constexpr size_t TABLE_SIZE = 259; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's by design that TextColor
can still only refer to colors 0-255 as indexed colors, right? We're not going to expand the definition of a TextColor
to store default-ness as indices 256, 257?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, for now at least. I did think their might be some advantage to storing the default colors with their index already calculated (in which case we would need to increase the index range), but I'm not sure that would work correctly when the indexes are changed. I might reconsider this in the future though.
@@ -182,7 +181,6 @@ void Selection::_RestoreDataToCursor(Cursor& cursor) noexcept | |||
{ | |||
cursor.SetSize(_ulSavedCursorSize); | |||
cursor.SetIsVisible(_fSavedCursorVisible); | |||
cursor.SetColor(_savedCursorColor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what the purpose of this code was; it suggests that there's a different cursor during selection?
Hello @zadjii-msft! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (
|
The VT525 only has 16 palette entries, and the default colors are just indexed values within that set (much like the old Windows conhost). So to change one of the default colors, you'd lookup their index and change the palette entry at that position in the color table. Or if necessary, set the index to somewhere else in the table, and then update the palette entry for that new index. The sequence to set those index values is Beyond that, though, they also had an "alternate color" mode, where each of the SGR renditions (bold, reverse, underline, etc) could be mapped to individual colors. So you could make bold displayed in green, and underline in red, etc. This was for compatibility with color terminals like the VT340 and VT240 which didn't support the SGR color sequences, but instead had hardcoded colors for the different SGR style renditions. On the VT525, those alternate color indexes could be set with the And while the VT340 didn't allow you to set the index values directly, you could choose between a couple of different fixed mappings with the There are a few other sequences that also affect how this stuff works (e.g. |
Yeah, when you select the "Mark" edit menu (or Ctrl+M), it changes to a block cursor, and you can then set the selected area with the keyboard. |
as a self-professed sponge, I'm more than happy to know all of this and more. Thanks! |
## Summary of the Pull Request This PR moves the color table and related render settings, which are common to both conhost and Windows Terminal, into a shared class that can be accessed directly from the renderer. This avoids the overhead of having to look up these properties via the `IRenderData` interface, which relies on inefficient virtual function calls. This also introduces the concept of color aliases, which determine the position in the color table that colors like the default foreground and background are stored. This allows the option of mapping them to one of the standard 16 colors, or to have their own separate table entries. ## References This is a continuation of the color table refactoring started in #11602 and #11784. The color alias functionality is a prerequisite for supporting a default bold color as proposed in #11939. The color aliases could also be a way for us to replace the PowerShell color quirk for #6807. ## PR Checklist * [x] Closes #12002 * [x] CLA signed. * [ ] Tests added/passed * [ ] Documentation updated. * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments In addition to the color table, this new `RenderSettings` class manages the blinking state, the code for adjusting indistinguishable colors, and various boolean properties used in the color calculations. These boolean properties are now stored in a `til::enumset` so they can all be managed through a single `SetRenderMode` API, and easily extended with additional modes that we're likely to need in the future. In Windows Terminal we have an instance of `RenderSettings` stored in the `Terminal` class, and in conhost it's stored in the `Settings` class. In both cases, a reference to this class is passed to the `Renderer` constructor, so it now has direct access to that data. The renderer can then pass that reference to the render engines where it's needed in the `UpdateDrawingBrushes` method. This means the renderer no longer needs the `IRenderData` interface to access the `GetAttributeColors`, `GetCursorColor`, or `IsScreenReversed` methods, so those have now been removed. We still need access to `GetAttributeColors` in certain accessibility code, though, so I've kept that method in the `IUIAData` interface, but the implementation just forwards to the `RenderSettings` class. The implementation of the `RenderSettings::GetAttributeColors` method is loosely based on the original `Terminal` code, only the `CalculateRgbColors` call has now been incorporated directly into the code. This let us deduplicate some bits that were previously repeated in the section for adjusting indistinguishable colors. The last steps, where we calculate the alpha components, have now been split in to a separate `GetAttributeColorsWithAlpha` method, since that's typically not needed. ## Validation Steps Performed There were quite a lot changes needed in the unit tests, but they're mostly straightforward replacements of one method call with another. In the `TextAttributeTests`, where we were previously testing the `CalculateRgbColors` method, we're now running those tests though `RenderSettings::GetAttributeColors`, which incorporates the same functionality. The only complication is when testing the `IntenseIsBright` option, that needs to be set with an additional `SetRenderMode` call where previously it was just a parameter on `CalculateRgbColors`. In the `ScreenBufferTests` and `TextBufferTests`, calls to `LookupAttributeColors` have again been replaced by the `RenderSettings::GetAttributeColors` method, which serves the same purpose, and calls to `IsScreenReversed` have been replaced with an appropriate `GetRenderMode` call. In the `VtRendererTests`, all the calls to `UpdateDrawingBrushes` now just need to be passed a reference to a `RenderSettings` instance.
🎉 Handy links: |
This PR merges the default colors and cursor color into the main color
table, enabling us to simplify the
ConGetSet
andITerminalApi
interfaces, with just two methods required for getting and setting any
form of color palette entry.
The is a follow-up to the color table standardization in #11602, and a
another small step towards de-duplicating
AdaptDispatch
andTerminalDispatch
for issue #3849. It should also make it easier tosupport color queries (#3718) and a configurable bold color (#5682) in
the future.
On the conhost side, default colors could originally be either indexed
positions in the 16-color table, or separate standalone RGB values. With
the new system, the default colors will always be in the color table, so
we just need to track their index positions.
To make this work, those positions need to be calculated at startup
based on the loaded registry/shortcut settings, and updated when
settings are changed (this is handled in
CalculateDefaultColorIndices
). But the plus side is that it's now mucheasier to lookup the default color values for rendering.
For now the default colors in Windows Terminal use hardcoded positions,
because it doesn't need indexed default colors like conhost. But in the
future I'd like to extend the index handling to both terminals, so we
can eventually support the VT525 indexed color operations.
As for the cursor color, that was previously stored in the
Cursor
class, which meant that it needed to be copied around in various places
where cursors were being instantiated. Now that it's managed separately
in the color table, a lot of that code is no longer required.
Validation
Some of the unit test initialization code needed to be updated to setup
the color table and default index values as required for the new system.
There were also some adjustments needed to account for API changes, in
particular for methods that now take index values for the default colors
in place of COLORREFs. But for the most part, the essential behavior of
the tests remains unchanged.
I've also run a variety of manual tests looking at the legacy console
APIs as well as the various VT color sequences, and checking that
everything works as expected when color schemes are changed, both in
Windows Terminal and conhost, and in the latter case with both indexed
colors and RGB values.
Closes #11768