Skip to content

Commit 246e1d5

Browse files
committed
Add support for OSC 104, 110, 111, 112 and 117 (resets) (microsoft#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 microsoft#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 microsoft#18695 Refs microsoft#17879 Closes microsoft#3719 (cherry picked from commit 5f31150) Service-Card-Id: PVTI_lADOAF3p4s4AmhmQzgZOWsU Service-Version: 1.22
1 parent 180a52e commit 246e1d5

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
@@ -3288,6 +3288,40 @@ void AdaptDispatch::RequestColorTableEntry(const size_t tableIndex)
32883288
}
32893289
}
32903290

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

3371+
// Method Description:
3372+
// - Restores to the original user-provided value one Xterm Color Resource such as Default Foreground, Background, Cursor
3373+
void AdaptDispatch::ResetXtermColorResource(const size_t resource)
3374+
{
3375+
assert(resource >= 10);
3376+
const auto mappingIndex = resource - 10;
3377+
const auto& oscMapping = XtermResourceColorTableMappings.at(mappingIndex);
3378+
if (oscMapping.ColorTableIndex > 0)
3379+
{
3380+
if (oscMapping.AliasIndex >= 0)
3381+
{
3382+
// If this color reset applies to an aliased color, point the alias back at the original color
3383+
_renderSettings.RestoreDefaultColorAliasIndex(static_cast<ColorAlias>(oscMapping.AliasIndex));
3384+
}
3385+
3386+
ResetColorTableEntry(oscMapping.ColorTableIndex);
3387+
}
3388+
}
3389+
33373390
// Method Description:
33383391
// DECAC - Assigns the foreground and background color indexes that should be
33393392
// 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
@@ -132,8 +132,11 @@ namespace Microsoft::Console::VirtualTerminal
132132
void SetColorTableEntry(const size_t tableIndex,
133133
const DWORD color) override; // OSCSetColorTable
134134
void RequestColorTableEntry(const size_t tableIndex) override; // OSCGetColorTable
135-
void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor, OSCResetCursorColor
135+
void ResetColorTable() override; // OSCResetColorTable
136+
void ResetColorTableEntry(const size_t tableIndex) override; // OSCResetColorTable
137+
void SetXtermColorResource(const size_t resource, const DWORD color) override; // OSCSetDefaultForeground, OSCSetDefaultBackground, OSCSetCursorColor
136138
void RequestXtermColorResource(const size_t resource) override; // OSCGetDefaultForeground, OSCGetDefaultBackground, OSCGetCursorColor
139+
void ResetXtermColorResource(const size_t resource) override; // OSCResetForegroundColor, OSCResetBackgroundColor, OSCResetCursorColor, OSCResetHighlightColor
137140
void AssignColor(const DispatchTypes::ColorItem item, const VTInt fgIndex, const VTInt bgIndex) override; // DECAC
138141

139142
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
@@ -804,10 +804,40 @@ bool OutputStateMachineEngine::ActionOscDispatch(const size_t parameter, const s
804804
}
805805
break;
806806
}
807+
case OscActionCodes::ResetColor:
808+
{
809+
if (string.empty())
810+
{
811+
_dispatch->ResetColorTable();
812+
}
813+
else
814+
{
815+
for (auto&& c : til::split_iterator{ string, L';' })
816+
{
817+
if (const auto index{ til::parse_unsigned<size_t>(c, 10) }; index)
818+
{
819+
_dispatch->ResetColorTableEntry(*index);
820+
}
821+
else
822+
{
823+
// NOTE: xterm stops at the first unparseable index whereas VTE keeps going.
824+
break;
825+
}
826+
}
827+
}
828+
break;
829+
}
830+
case OscActionCodes::ResetForegroundColor:
831+
case OscActionCodes::ResetBackgroundColor:
807832
case OscActionCodes::ResetCursorColor:
833+
case OscActionCodes::ResetHighlightColor:
808834
{
809-
// The reset codes for xterm dynamic resources are the set codes + 100
810-
_dispatch->SetXtermColorResource(parameter - 100u, INVALID_COLOR);
835+
// NOTE: xterm ignores the request if there's any parameters whereas VTE resets the provided index and ignores the rest
836+
if (string.empty())
837+
{
838+
// The reset codes for xterm dynamic resources are the set codes + 100
839+
_dispatch->ResetXtermColorResource(parameter - 100u);
840+
}
811841
break;
812842
}
813843
case OscActionCodes::Hyperlink:

src/terminal/parser/OutputStateMachineEngine.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,11 @@ namespace Microsoft::Console::VirtualTerminal
212212
SetHighlightColor = 17,
213213
DECSWT_SetWindowTitle = 21,
214214
SetClipboard = 52,
215-
ResetForegroundColor = 110, // Not implemented
216-
ResetBackgroundColor = 111, // Not implemented
215+
ResetColor = 104,
216+
ResetForegroundColor = 110,
217+
ResetBackgroundColor = 111,
217218
ResetCursorColor = 112,
219+
ResetHighlightColor = 117,
218220
FinalTermAction = 133,
219221
VsCodeAction = 633,
220222
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)