Add support for chaining OSC 10-12 (#8999)
This adds the support for chaining OSC 10-12, allowing users to set all of them at once. **BREAKING CHANGE** Before this PR, the OSC 10/11/12 command will only be dispatched iff the first color is valid. This is no longer true. The new implementation strictly follows xterm's behavior. Each color is treated independently. For example, `\e]10;invalid;white\e\\` is effectively `\e]11;white\e\\`. ## Validation Steps Performed Tests added. Manually tested. Main OSC color tracking issue: #942 OSC 4 & Initial OSC 10-12 PR: #7578 Closes one item in #942
This commit is contained in:
parent
45bee078f0
commit
779354d368
|
@ -13,6 +13,9 @@
|
|||
using namespace Microsoft::Console;
|
||||
using namespace Microsoft::Console::VirtualTerminal;
|
||||
|
||||
// the console uses 0xffffffff as an "invalid color" value
|
||||
constexpr COLORREF INVALID_COLOR = 0xffffffff;
|
||||
|
||||
// takes ownership of pDispatch
|
||||
OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr<ITermDispatch> pDispatch) :
|
||||
_dispatch(std::move(pDispatch)),
|
||||
|
@ -672,36 +675,52 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
|
|||
break;
|
||||
}
|
||||
case OscActionCodes::SetForegroundColor:
|
||||
{
|
||||
std::vector<DWORD> colors;
|
||||
success = _GetOscSetColor(string, colors);
|
||||
if (success && colors.size() > 0)
|
||||
{
|
||||
success = _dispatch->SetDefaultForeground(til::at(colors, 0));
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCFG);
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::SetBackgroundColor:
|
||||
{
|
||||
std::vector<DWORD> colors;
|
||||
success = _GetOscSetColor(string, colors);
|
||||
if (success && colors.size() > 0)
|
||||
{
|
||||
success = _dispatch->SetDefaultBackground(til::at(colors, 0));
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCBG);
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::SetCursorColor:
|
||||
{
|
||||
std::vector<DWORD> colors;
|
||||
success = _GetOscSetColor(string, colors);
|
||||
if (success && colors.size() > 0)
|
||||
if (success)
|
||||
{
|
||||
success = _dispatch->SetCursorColor(til::at(colors, 0));
|
||||
size_t commandIndex = parameter;
|
||||
size_t colorIndex = 0;
|
||||
|
||||
if (commandIndex == OscActionCodes::SetForegroundColor && colors.size() > colorIndex)
|
||||
{
|
||||
const auto color = til::at(colors, colorIndex);
|
||||
if (color != INVALID_COLOR)
|
||||
{
|
||||
success = success && _dispatch->SetDefaultForeground(color);
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCFG);
|
||||
commandIndex++;
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
if (commandIndex == OscActionCodes::SetBackgroundColor && colors.size() > colorIndex)
|
||||
{
|
||||
const auto color = til::at(colors, colorIndex);
|
||||
if (color != INVALID_COLOR)
|
||||
{
|
||||
success = success && _dispatch->SetDefaultBackground(color);
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCBG);
|
||||
commandIndex++;
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
if (commandIndex == OscActionCodes::SetCursorColor && colors.size() > colorIndex)
|
||||
{
|
||||
const auto color = til::at(colors, colorIndex);
|
||||
if (color != INVALID_COLOR)
|
||||
{
|
||||
success = success && _dispatch->SetCursorColor(color);
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC);
|
||||
commandIndex++;
|
||||
colorIndex++;
|
||||
}
|
||||
}
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC);
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::SetClipboard:
|
||||
|
@ -718,7 +737,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
|
|||
}
|
||||
case OscActionCodes::ResetCursorColor:
|
||||
{
|
||||
success = _dispatch->SetCursorColor(0xffffffff);
|
||||
success = _dispatch->SetCursorColor(INVALID_COLOR);
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCRCC);
|
||||
break;
|
||||
}
|
||||
|
@ -947,19 +966,15 @@ bool OutputStateMachineEngine::_ParseHyperlink(const std::wstring_view string,
|
|||
// with accumulated Ps. For example "OSC 10;color1;color2" is effectively an "OSC 10;color1"
|
||||
// and an "OSC 11;color2".
|
||||
//
|
||||
// However, we do not support the chaining of OSC 10-17 yet. Right now only the first parameter
|
||||
// will take effect.
|
||||
// Arguments:
|
||||
// - string - the Osc String to parse
|
||||
// - rgbs - receives the colors that we parsed in the format: 0x00BBGGRR
|
||||
// Return Value:
|
||||
// - True if the first table index and color was parsed successfully. False otherwise.
|
||||
// - True if at least one color was parsed successfully. False otherwise.
|
||||
bool OutputStateMachineEngine::_GetOscSetColor(const std::wstring_view string,
|
||||
std::vector<DWORD>& rgbs) const noexcept
|
||||
try
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
const auto parts = Utils::SplitString(string, L';');
|
||||
if (parts.size() < 1)
|
||||
{
|
||||
|
@ -973,17 +988,16 @@ try
|
|||
if (colorOptional.has_value())
|
||||
{
|
||||
newRgbs.push_back(colorOptional.value());
|
||||
// Only mark the parsing as a success iff the first color is a success.
|
||||
if (i == 0)
|
||||
{
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newRgbs.push_back(INVALID_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
rgbs.swap(newRgbs);
|
||||
|
||||
return success;
|
||||
return rgbs.size() > 0;
|
||||
}
|
||||
CATCH_LOG_RETURN_FALSE()
|
||||
|
||||
|
|
|
@ -1052,6 +1052,8 @@ public:
|
|||
_defaultForegroundColor{ RGB(0, 0, 0) },
|
||||
_setDefaultBackground(false),
|
||||
_defaultBackgroundColor{ RGB(0, 0, 0) },
|
||||
_setDefaultCursorColor(false),
|
||||
_defaultCursorColor{ RGB(0, 0, 0) },
|
||||
_hyperlinkMode{ false },
|
||||
_options{ s_cMaxOptions, static_cast<DispatchTypes::GraphicsOptions>(s_uiGraphicsCleared) }, // fill with cleared option
|
||||
_colorTable{},
|
||||
|
@ -1441,6 +1443,13 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SetCursorColor(const DWORD color) noexcept override
|
||||
{
|
||||
_setDefaultCursorColor = true;
|
||||
_defaultCursorColor = color;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetClipboard(std::wstring_view content) noexcept override
|
||||
{
|
||||
_copyContent = { content.begin(), content.end() };
|
||||
|
@ -1527,6 +1536,8 @@ public:
|
|||
DWORD _defaultForegroundColor;
|
||||
bool _setDefaultBackground;
|
||||
DWORD _defaultBackgroundColor;
|
||||
bool _setDefaultCursorColor;
|
||||
DWORD _defaultCursorColor;
|
||||
bool _setColorTableEntry;
|
||||
bool _hyperlinkMode;
|
||||
std::wstring _copyContent;
|
||||
|
@ -2846,6 +2857,7 @@ class StateMachineExternalTest final
|
|||
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||
StateMachine mach(std::move(engine));
|
||||
|
||||
// Single param
|
||||
mach.ProcessString(L"\033]10;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultForegroundColor);
|
||||
|
@ -2876,20 +2888,34 @@ class StateMachineExternalTest final
|
|||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Multiple params.
|
||||
// Multiple params
|
||||
mach.ProcessString(L"\033]10;#111;rgb:2/2/2\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultBackgroundColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]10;#111;DarkOrange\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultBackgroundColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Partially valid sequences. Only the first color works.
|
||||
mach.ProcessString(L"\033]10;#111;DarkOrange;rgb:2/2/2\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultBackgroundColor);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
|
||||
VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultCursorColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Partially valid multi-param sequences.
|
||||
mach.ProcessString(L"\033]10;#111;\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
|
@ -2899,12 +2925,28 @@ class StateMachineExternalTest final
|
|||
mach.ProcessString(L"\033]10;#111;rgb:\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]10;#111;#2\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultForegroundColor);
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]10;;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultBackgroundColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]10;#1;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultBackgroundColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
|
@ -2918,17 +2960,6 @@ class StateMachineExternalTest final
|
|||
VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Invalid multi-param sequences.
|
||||
mach.ProcessString(L"\033]10;;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]10;#1;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultForeground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
}
|
||||
|
||||
TEST_METHOD(TestOscSetDefaultBackground)
|
||||
|
@ -2944,6 +2975,7 @@ class StateMachineExternalTest final
|
|||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Single param
|
||||
mach.ProcessString(L"\033]11;rgb:12/34/56\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), pDispatch->_defaultBackgroundColor);
|
||||
|
@ -2968,7 +3000,30 @@ class StateMachineExternalTest final
|
|||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Partially valid sequences. Only the first color works.
|
||||
// Multiple params
|
||||
mach.ProcessString(L"\033]11;#111;rgb:2/2/2\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
|
||||
VERIFY_ARE_EQUAL(RGB(0x22, 0x22, 0x22), pDispatch->_defaultCursorColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]11;#111;DarkOrange\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
|
||||
VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultCursorColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]11;#111;DarkOrange;rgb:2/2/2\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
|
||||
VERIFY_ARE_EQUAL(RGB(255, 140, 0), pDispatch->_defaultCursorColor);
|
||||
// The third param is out of range.
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Partially valid multi-param sequences.
|
||||
mach.ProcessString(L"\033]11;#111;\033\\");
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_ARE_EQUAL(RGB(0x10, 0x10, 0x10), pDispatch->_defaultBackgroundColor);
|
||||
|
@ -2987,6 +3042,20 @@ class StateMachineExternalTest final
|
|||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]11;;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
|
||||
VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultCursorColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]11;#1;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
VERIFY_IS_TRUE(pDispatch->_setDefaultCursorColor);
|
||||
VERIFY_ARE_EQUAL(RGB(0x11, 0x11, 0x11), pDispatch->_defaultCursorColor);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Invalid sequences.
|
||||
mach.ProcessString(L"\033]11;rgb:1/1/\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
@ -2997,17 +3066,6 @@ class StateMachineExternalTest final
|
|||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
// Invalid multi-param sequences.
|
||||
mach.ProcessString(L"\033]11;;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
mach.ProcessString(L"\033]11;#1;rgb:1/1/1\033\\");
|
||||
VERIFY_IS_FALSE(pDispatch->_setDefaultBackground);
|
||||
|
||||
pDispatch->ClearState();
|
||||
}
|
||||
|
||||
TEST_METHOD(TestOscSetColorTableEntry)
|
||||
|
|
Loading…
Reference in a new issue