Skip to content

Commit 5f31150

Browse files
authored
Add support for OSC 104, 110, 111, 112 and 117 (resets) (#18767)
This pull request adds support for resetting the various color table entries and xterm resource values back to their defaults. Building on the default color table James introduced in #17879, it was relatively straightforward to add support for resetting specific entries. This implementation cleaves tightly to observed behavior in xterm(379) rather than observed behavior in libvte(0.70.6). They differ in the following ways: - xterm rejects any OSC [110..119] with any number of parameters; libvte accepts it but only resets the first color. - When passed a list of color indices to reset in 104, xterm resets any colors up until the first one which fails to parse as an integer and does _not_ reset the rest; libvte resets all parseable color indices. I was unable to verify how these reset commands interact with colors set via `DECAC Assign Color` so I went with the implementation that made the most sense: - Resetting the background color with `110` also restores the background color alias entry to its pre-`DECAC` value; this results in the perceived background color returning to e.g. index 0 in conhost and the `background` color in Terminal. - _ibid._ for the foreground color Refs #18695 Refs #17879 Closes #3719
1 parent 22c509f commit 5f31150

File tree

9 files changed

+216
-8
lines changed

9 files changed

+216
-8
lines changed

src/renderer/base/RenderSettings.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ COLORREF RenderSettings::GetColorTableEntry(const size_t tableIndex) const
112112
return _colorTable.at(tableIndex);
113113
}
114114

115+
// Routine Description:
116+
// - Restores all of the xterm-addressable colors to the ones saved in SaveDefaultSettings.
117+
void RenderSettings::RestoreDefaultIndexed256ColorTable()
118+
{
119+
std::copy_n(_defaultColorTable.begin(), 256, _colorTable.begin());
120+
}
121+
122+
// Routine Description:
123+
// - Restores a color table entry to the value saved in SaveDefaultSettings.
124+
void RenderSettings::RestoreDefaultColorTableEntry(const size_t tableIndex)
125+
{
126+
_colorTable.at(tableIndex) = _defaultColorTable.at(tableIndex);
127+
}
128+
115129
// Routine Description:
116130
// - Sets the position in the color table for the given color alias and updates the color.
117131
// Arguments:
@@ -159,6 +173,11 @@ size_t RenderSettings::GetColorAliasIndex(const ColorAlias alias) const noexcept
159173
return gsl::at(_colorAliasIndices, static_cast<size_t>(alias));
160174
}
161175

176+
void RenderSettings::RestoreDefaultColorAliasIndex(const ColorAlias alias) noexcept
177+
{
178+
gsl::at(_colorAliasIndices, static_cast<size_t>(alias)) = gsl::at(_defaultColorAliasIndices, static_cast<size_t>(alias));
179+
}
180+
162181
// Routine Description:
163182
// - Calculates the RGB colors of a given text attribute, using the current
164183
// color table configuration and active render settings.

src/renderer/inc/RenderSettings.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ namespace Microsoft::Console::Render
3737
void ResetColorTable() noexcept;
3838
void SetColorTableEntry(const size_t tableIndex, const COLORREF color);
3939
COLORREF GetColorTableEntry(const size_t tableIndex) const;
40+
void RestoreDefaultIndexed256ColorTable();
41+
void RestoreDefaultColorTableEntry(const size_t tableIndex);
4042
void SetColorAlias(const ColorAlias alias, const size_t tableIndex, const COLORREF color);
4143
COLORREF GetColorAlias(const ColorAlias alias) const;
4244
void SetColorAliasIndex(const ColorAlias alias, const size_t tableIndex) noexcept;
4345
size_t GetColorAliasIndex(const ColorAlias alias) const noexcept;
46+
void RestoreDefaultColorAliasIndex(const ColorAlias alias) noexcept;
4447
std::pair<COLORREF, COLORREF> GetAttributeColors(const TextAttribute& attr) const noexcept;
4548
std::pair<COLORREF, COLORREF> GetAttributeColorsWithAlpha(const TextAttribute& attr) const noexcept;
4649
COLORREF GetAttributeUnderlineColor(const TextAttribute& attr) const noexcept;

src/terminal/adapter/ITermDispatch.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,11 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
7878
virtual void TabSet(const VTParameter setType) = 0; // DECST8C
7979
virtual void SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCSetColorTable
8080
virtual void RequestColorTableEntry(const size_t tableIndex) = 0; // OSCGetColorTable
81-
virtual void SetXtermColorResource(const size_t resource, const DWORD color) = 0; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
81+
virtual void ResetColorTable() = 0; // OSCResetColorTable
82+
virtual void ResetColorTableEntry(const size_t tableIndex) = 0; // OSCResetColorTable
83+
virtual void SetXtermColorResource(const size_t resource, const DWORD color) = 0; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor
8284
virtual void RequestXtermColorResource(const size_t resource) = 0; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
85+
virtual void ResetXtermColorResource(const size_t resource) = 0; // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor
8386
virtual void AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) = 0; // DECAC
8487

8588
virtual void EraseInDisplay(const DispatchTypes::EraseType eraseType) = 0; // ED

src/terminal/adapter/adaptDispatch.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3292,6 +3292,40 @@ void AdaptDispatch::RequestColorTableEntry(const size_t tableIndex)
32923292
}
32933293
}
32943294

3295+
void AdaptDispatch::ResetColorTable()
3296+
{
3297+
_renderSettings.RestoreDefaultIndexed256ColorTable();
3298+
if (_renderer)
3299+
{
3300+
// This is pessimistic because it's unlikely that the frame or background changed,
3301+
// but let's tell the renderer that both changed anyway.
3302+
_renderer->TriggerRedrawAll(true, true);
3303+
}
3304+
}
3305+
3306+
// Method Description:
3307+
// - Restores a single color table entry to its default user-specified value
3308+
// Arguments:
3309+
// - tableIndex: The VT color table index
3310+
void AdaptDispatch::ResetColorTableEntry(const size_t tableIndex)
3311+
{
3312+
_renderSettings.RestoreDefaultColorTableEntry(tableIndex);
3313+
3314+
if (_renderer)
3315+
{
3316+
// If we're updating the background color, we need to let the renderer
3317+
// know, since it may want to repaint the window background to match.
3318+
const auto backgroundIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultBackground);
3319+
const auto backgroundChanged = (tableIndex == backgroundIndex);
3320+
3321+
// Similarly for the frame color, the tab may need to be repainted.
3322+
const auto frameIndex = _renderSettings.GetColorAliasIndex(ColorAlias::FrameBackground);
3323+
const auto frameChanged = (tableIndex == frameIndex);
3324+
3325+
_renderer->TriggerRedrawAll(backgroundChanged, frameChanged);
3326+
}
3327+
}
3328+
32953329
// Method Description:
32963330
// - Sets one Xterm Color Resource such as Default Foreground, Background, Cursor
32973331
void AdaptDispatch::SetXtermColorResource(const size_t resource, const DWORD color)
@@ -3338,6 +3372,25 @@ void AdaptDispatch::RequestXtermColorResource(const size_t resource)
33383372
}
33393373
}
33403374

3375+
// Method Description:
3376+
// - Restores to the original user-provided value one Xterm Color Resource such as Default Foreground, Background, Cursor
3377+
void AdaptDispatch::ResetXtermColorResource(const size_t resource)
3378+
{
3379+
assert(resource >= 10);
3380+
const auto mappingIndex = resource - 10;
3381+
const auto& oscMapping = XtermResourceColorTableMappings.at(mappingIndex);
3382+
if (oscMapping.ColorTableIndex > 0)
3383+
{
3384+
if (oscMapping.AliasIndex >= 0)
3385+
{
3386+
// If this color reset applies to an aliased color, point the alias back at the original color
3387+
_renderSettings.RestoreDefaultColorAliasIndex(static_cast<ColorAlias>(oscMapping.AliasIndex));
3388+
}
3389+
3390+
ResetColorTableEntry(oscMapping.ColorTableIndex);
3391+
}
3392+
}
3393+
33413394
// Method Description:
33423395
// DECAC - Assigns the foreground and background color indexes that should be
33433396
// used for a given aspect of the user interface.

src/terminal/adapter/adaptDispatch.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,11 @@ namespace Microsoft::Console::VirtualTerminal
133133
void SetColorTableEntry(const size_t tableIndex,
134134
const DWORD color) override; // OSCSetColorTable
135135
void RequestColorTableEntry(const size_t tableIndex) override; // OSCGetColorTable
136-
void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
136+
void ResetColorTable() override; // OSCResetColorTable
137+
void ResetColorTableEntry(const size_t tableIndex) override; // OSCResetColorTable
138+
void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor
137139
void RequestXtermColorResource(const size_t resource) override; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
140+
void ResetXtermColorResource(const size_t resource) override; // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor
138141
void AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) override; // DECAC
139142

140143
void WindowManipulation(const DispatchTypes::WindowManipulationType function,

src/terminal/adapter/termDispatch.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
7171
void TabSet(const VTParameter /*setType*/) override {} // DECST8C
7272
void SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override {} // OSCSetColorTable
7373
void RequestColorTableEntry(const size_t /*tableIndex*/) override {} // OSCGetColorTable
74-
void SetXtermColorResource(const size_t /*resource*/, const DWORD /*color*/) override {} // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
74+
void ResetColorTable() override {} // OSCResetColorTable
75+
void ResetColorTableEntry(const size_t /*tableIndex*/) override {} // OSCResetColorTable
76+
void SetXtermColorResource(const size_t /*resource*/, const DWORD /*color*/) override {} // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor
7577
void RequestXtermColorResource(const size_t /*resource*/) override {} // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
78+
void ResetXtermColorResource(const size_t /*resource*/) override {} // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor
7679
void AssignColor(const DispatchTypes::ColorItem /*item*/, const VTInt /*fgIndex*/, const VTInt /*bgIndex*/) override {} // DECAC
7780

7881
void EraseInDisplay(const DispatchTypes::EraseType /* eraseType*/) override {} // ED

src/terminal/parser/OutputStateMachineEngine.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,10 +810,40 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
810810
}
811811
break;
812812
}
813+
case OscActionCodes::ResetColor:
814+
{
815+
if (string.empty())
816+
{
817+
_dispatch->ResetColorTable();
818+
}
819+
else
820+
{
821+
for (auto&& c : til::split_iterator{ string, L';' })
822+
{
823+
if (const auto index{ til::parse_unsigned<size_t>(c, 10) }; index)
824+
{
825+
_dispatch->ResetColorTableEntry(*index);
826+
}
827+
else
828+
{
829+
// NOTE: xterm stops at the first unparseable index whereas VTE keeps going.
830+
break;
831+
}
832+
}
833+
}
834+
break;
835+
}
836+
case OscActionCodes::ResetForegroundColor:
837+
case OscActionCodes::ResetBackgroundColor:
813838
case OscActionCodes::ResetCursorColor:
839+
case OscActionCodes::ResetHighlightColor:
814840
{
815-
// The reset codes for xterm dynamic resources are the set codes + 100
816-
_dispatch->SetXtermColorResource(parameter - 100u, INVALID_COLOR);
841+
// NOTE: xterm ignores the request if there's any parameters whereas VTE resets the provided index and ignores the rest
842+
if (string.empty())
843+
{
844+
// The reset codes for xterm dynamic resources are the set codes + 100
845+
_dispatch->ResetXtermColorResource(parameter - 100u);
846+
}
817847
break;
818848
}
819849
case OscActionCodes::Hyperlink:

src/terminal/parser/OutputStateMachineEngine.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,11 @@ namespace Microsoft::Console::VirtualTerminal
214214
SetHighlightColor = 17,
215215
DECSWT_SetWindowTitle = 21,
216216
SetClipboard = 52,
217-
ResetForegroundColor = 110, // Not implemented
218-
ResetBackgroundColor = 111, // Not implemented
217+
ResetColor = 104,
218+
ResetForegroundColor = 110,
219+
ResetBackgroundColor = 111,
219220
ResetCursorColor = 112,
221+
ResetHighlightColor = 117,
220222
FinalTermAction = 133,
221223
VsCodeAction = 633,
222224
ITerm2Action = 1337,

src/terminal/parser/ut_parser/OutputEngineTest.cpp

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,8 @@ class StatefulDispatch final : public TermDispatch
11631163
_hyperlinkMode{ false },
11641164
_options{ s_cMaxOptions, static_cast<DispatchTypes::GraphicsOptions>(s_uiGraphicsCleared) }, // fill with cleared option
11651165
_colorTable{},
1166-
_setColorTableEntry{ false }
1166+
_setColorTableEntry{ false },
1167+
_resetAllColors{ false }
11671168
{
11681169
}
11691170

@@ -1390,6 +1391,16 @@ class StatefulDispatch final : public TermDispatch
13901391
_colorTableEntriesRequested.push_back(tableIndex);
13911392
}
13921393

1394+
void ResetColorTable() noexcept override
1395+
{
1396+
_resetAllColors = true;
1397+
}
1398+
1399+
void ResetColorTableEntry(const size_t tableIndex) noexcept override
1400+
{
1401+
_colorTableEntriesReset.push_back(tableIndex);
1402+
}
1403+
13931404
void SetXtermColorResource(const size_t resource, const DWORD color) override
13941405
{
13951406
_xtermResourcesChanged.push_back(resource);
@@ -1401,6 +1412,11 @@ class StatefulDispatch final : public TermDispatch
14011412
_xtermResourcesRequested.push_back(resource);
14021413
}
14031414

1415+
void ResetXtermColorResource(const size_t resource) override
1416+
{
1417+
_xtermResourcesReset.push_back(resource);
1418+
}
1419+
14041420
void SetClipboard(wil::zwstring_view content) noexcept override
14051421
{
14061422
_copyContent = content;
@@ -1476,8 +1492,11 @@ class StatefulDispatch final : public TermDispatch
14761492
std::vector<size_t> _xtermResourcesChanged;
14771493
std::vector<DWORD> _xtermResourceValues;
14781494
std::vector<size_t> _xtermResourcesRequested;
1495+
std::vector<size_t> _xtermResourcesReset;
14791496
bool _setColorTableEntry;
14801497
std::vector<size_t> _colorTableEntriesRequested;
1498+
bool _resetAllColors;
1499+
std::vector<size_t> _colorTableEntriesReset;
14811500
bool _hyperlinkMode;
14821501
std::wstring _copyContent;
14831502
std::wstring _uri;
@@ -3221,6 +3240,79 @@ class StateMachineExternalTest final
32213240
pDispatch->ClearState();
32223241
}
32233242

3243+
TEST_METHOD(TestOscXtermResourceReset)
3244+
{
3245+
auto dispatch = std::make_unique<StatefulDispatch>();
3246+
auto pDispatch = dispatch.get();
3247+
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
3248+
StateMachine mach(std::move(engine));
3249+
3250+
mach.ProcessString(L"\033]110\033\\");
3251+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
3252+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size());
3253+
VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesReset.size());
3254+
VERIFY_ARE_EQUAL(10u, pDispatch->_xtermResourcesReset[0]);
3255+
pDispatch->ClearState();
3256+
3257+
mach.ProcessString(L"\033]111;\033\\"); // dangling ;
3258+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
3259+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size());
3260+
VERIFY_ARE_EQUAL(1u, pDispatch->_xtermResourcesReset.size());
3261+
VERIFY_ARE_EQUAL(11u, pDispatch->_xtermResourcesReset[0]);
3262+
pDispatch->ClearState();
3263+
3264+
mach.ProcessString(L"\033]111;110\033\\");
3265+
// NOTE: this is xterm behavior - ignore the entire sequence if any params exist
3266+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesChanged.size());
3267+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesRequested.size());
3268+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size());
3269+
pDispatch->ClearState();
3270+
}
3271+
3272+
TEST_METHOD(TestOscColorTableReset)
3273+
{
3274+
auto dispatch = std::make_unique<StatefulDispatch>();
3275+
auto pDispatch = dispatch.get();
3276+
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
3277+
StateMachine mach(std::move(engine));
3278+
3279+
mach.ProcessString(L"\033]104\033\\");
3280+
VERIFY_IS_TRUE(pDispatch->_resetAllColors);
3281+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesReset.size());
3282+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size());
3283+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size());
3284+
pDispatch->ClearState();
3285+
3286+
mach.ProcessString(L"\033]104;1;3;5;7;9\033\\");
3287+
VERIFY_IS_FALSE(pDispatch->_resetAllColors);
3288+
VERIFY_ARE_EQUAL(5u, pDispatch->_colorTableEntriesReset.size());
3289+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size());
3290+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size());
3291+
VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset[0]);
3292+
VERIFY_ARE_EQUAL(3u, pDispatch->_colorTableEntriesReset[1]);
3293+
VERIFY_ARE_EQUAL(5u, pDispatch->_colorTableEntriesReset[2]);
3294+
VERIFY_ARE_EQUAL(7u, pDispatch->_colorTableEntriesReset[3]);
3295+
VERIFY_ARE_EQUAL(9u, pDispatch->_colorTableEntriesReset[4]);
3296+
pDispatch->ClearState();
3297+
3298+
// NOTE: xterm behavior - stop after first failed parse
3299+
mach.ProcessString(L"\033]104;1;a;3\033\\");
3300+
VERIFY_IS_FALSE(pDispatch->_resetAllColors);
3301+
VERIFY_IS_FALSE(pDispatch->_setColorTableEntry);
3302+
VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset.size());
3303+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size());
3304+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size());
3305+
VERIFY_ARE_EQUAL(1u, pDispatch->_colorTableEntriesReset[0]);
3306+
pDispatch->ClearState();
3307+
3308+
mach.ProcessString(L"\033]104;;;\033\\");
3309+
VERIFY_IS_FALSE(pDispatch->_setColorTableEntry);
3310+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesReset.size());
3311+
VERIFY_ARE_EQUAL(0u, pDispatch->_colorTableEntriesRequested.size());
3312+
VERIFY_ARE_EQUAL(0u, pDispatch->_xtermResourcesReset.size());
3313+
pDispatch->ClearState();
3314+
}
3315+
32243316
TEST_METHOD(TestOscSetWindowTitle)
32253317
{
32263318
BEGIN_TEST_METHOD_PROPERTIES()

0 commit comments

Comments
 (0)