Add support for restoring a DECCTR color table report (#13139)
This PR introduces the framework for the `DECRSTS` sequence which is used to restore terminal state reports. But to start with, I've just implemented the `DECCTR` color table report, which provides a way for applications to alter the terminal's color scheme. ## PR Checklist * [x] Closes #13132 * [x] CLA signed. * [x] 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 I've added the functions for parsing DEC RGB and HLS color formats into the `Utils` class, where we've got all our other color parsing routines, since this functionality will eventually be needed in other VT protocols like Sixel and ReGIS. Since `DECRSTS` is a `DCS` sequence, this only works in conhost for now, or when using the experimental passthrough mode in Windows Terminal. ## Validation Steps Performed I've added a number of unit tests to check that the `DECCTR` report is being interpreted as expected. This includes various edge cases (e.g. omitted and out-of-range parameters), which I have confirmed to match the color parsing on a real VT240 terminal.
This commit is contained in:
parent
9dca6c27ee
commit
c157f6346a
|
@ -520,6 +520,7 @@ DECAWM
|
|||
DECCKM
|
||||
DECCOLM
|
||||
DECCRA
|
||||
DECCTR
|
||||
DECDHL
|
||||
decdld
|
||||
DECDLD
|
||||
|
@ -545,7 +546,9 @@ DECREQTPARM
|
|||
DECRLM
|
||||
DECRQM
|
||||
DECRQSS
|
||||
DECRQTSR
|
||||
DECRST
|
||||
DECRSTS
|
||||
DECSASD
|
||||
DECSC
|
||||
DECSCA
|
||||
|
@ -1006,6 +1009,7 @@ hkey
|
|||
hkl
|
||||
HKLM
|
||||
hlocal
|
||||
HLS
|
||||
hlsl
|
||||
HMENU
|
||||
HMIDIOUT
|
||||
|
|
|
@ -132,6 +132,7 @@ class ScreenBufferTests
|
|||
TEST_METHOD(VtNewlineOutsideMargins);
|
||||
|
||||
TEST_METHOD(VtSetColorTable);
|
||||
TEST_METHOD(VtRestoreColorTableReport);
|
||||
|
||||
TEST_METHOD(ResizeTraditionalDoesNotDoubleFreeAttrRows);
|
||||
|
||||
|
@ -1738,6 +1739,237 @@ void ScreenBufferTests::VtSetColorTable()
|
|||
VERIFY_ARE_EQUAL(RGB(9, 9, 9), gci.GetColorTableEntry(5));
|
||||
}
|
||||
|
||||
void ScreenBufferTests::VtRestoreColorTableReport()
|
||||
{
|
||||
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
||||
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
|
||||
auto& stateMachine = si.GetStateMachine();
|
||||
|
||||
// Set everything to white to start with.
|
||||
for (auto i = 0; i < 16; i++)
|
||||
{
|
||||
gci.SetColorTableEntry(i, RGB(255, 255, 255));
|
||||
}
|
||||
|
||||
// The test cases below are copied from the VT340 default color table, but
|
||||
// note that our HLS conversion algorithm doesn't exactly match the VT340,
|
||||
// so some of the component values may be off by 1%.
|
||||
|
||||
Log::Comment(L"HLS color definitions");
|
||||
|
||||
// HLS(0°,0%,0%) -> RGB(0,0,0)
|
||||
stateMachine.ProcessString(L"\033P2$p0;1;0;0;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 0), gci.GetColorTableEntry(0));
|
||||
|
||||
// HLS(0°,49%,59%) -> RGB(51,51,199)
|
||||
stateMachine.ProcessString(L"\033P2$p1;1;0;49;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 51, 199), gci.GetColorTableEntry(1));
|
||||
|
||||
// HLS(120°,46%,71%) -> RGB(201,34,34)
|
||||
stateMachine.ProcessString(L"\033P2$p2;1;120;46;71\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(201, 34, 34), gci.GetColorTableEntry(2));
|
||||
|
||||
// HLS(240°,49%,59%) -> RGB(51,199,51)
|
||||
stateMachine.ProcessString(L"\033P2$p3;1;240;49;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 199, 51), gci.GetColorTableEntry(3));
|
||||
|
||||
// HLS(60°,49%,59%) -> RGB(199,51,199)
|
||||
stateMachine.ProcessString(L"\033P2$p4;1;60;49;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(199, 51, 199), gci.GetColorTableEntry(4));
|
||||
|
||||
// HLS(300°,49%,59%) -> RGB(51,199,199)
|
||||
stateMachine.ProcessString(L"\033P2$p5;1;300;49;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 199, 199), gci.GetColorTableEntry(5));
|
||||
|
||||
// HLS(180°,49%,59%) -> RGB(199,199,51)
|
||||
stateMachine.ProcessString(L"\033P2$p6;1;180;49;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(199, 199, 51), gci.GetColorTableEntry(6));
|
||||
|
||||
// HLS(0°,46%,0%) -> RGB(117,117,117)
|
||||
stateMachine.ProcessString(L"\033P2$p7;1;0;46;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(117, 117, 117), gci.GetColorTableEntry(7));
|
||||
|
||||
// HLS(0°,26%,0%) -> RGB(66,66,66)
|
||||
stateMachine.ProcessString(L"\033P2$p8;1;0;26;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(66, 66, 66), gci.GetColorTableEntry(8));
|
||||
|
||||
// HLS(0°,46%,28%) -> RGB(84,84,150)
|
||||
stateMachine.ProcessString(L"\033P2$p9;1;0;46;28\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 84, 150), gci.GetColorTableEntry(9));
|
||||
|
||||
// HLS(120°,42%,38%) -> RGB(148,66,66)
|
||||
stateMachine.ProcessString(L"\033P2$p10;1;120;42;38\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(148, 66, 66), gci.GetColorTableEntry(10));
|
||||
|
||||
// HLS(240°,46%,28%) -> RGB(84,150,84)
|
||||
stateMachine.ProcessString(L"\033P2$p11;1;240;46;28\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 150, 84), gci.GetColorTableEntry(11));
|
||||
|
||||
// HLS(60°,46%,28%) -> RGB(150,84,150)
|
||||
stateMachine.ProcessString(L"\033P2$p12;1;60;46;28\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(150, 84, 150), gci.GetColorTableEntry(12));
|
||||
|
||||
// HLS(300°,46%,28%) -> RGB(84,150,150)
|
||||
stateMachine.ProcessString(L"\033P2$p13;1;300;46;28\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 150, 150), gci.GetColorTableEntry(13));
|
||||
|
||||
// HLS(180°,46%,28%) -> RGB(150,150,84)
|
||||
stateMachine.ProcessString(L"\033P2$p14;1;180;46;28\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(150, 150, 84), gci.GetColorTableEntry(14));
|
||||
|
||||
// HLS(0°,79%,0%) -> RGB(201,201,201)
|
||||
stateMachine.ProcessString(L"\033P2$p15;1;0;79;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(201, 201, 201), gci.GetColorTableEntry(15));
|
||||
|
||||
// Reset everything to white again.
|
||||
for (auto i = 0; i < 16; i++)
|
||||
{
|
||||
gci.SetColorTableEntry(i, RGB(255, 255, 255));
|
||||
}
|
||||
|
||||
Log::Comment(L"RGB color definitions");
|
||||
|
||||
// RGB(0%,0%,0%) -> RGB(0,0,0)
|
||||
stateMachine.ProcessString(L"\033P2$p0;2;0;0;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 0), gci.GetColorTableEntry(0));
|
||||
|
||||
// RGB(20%,20%,78%) -> RGB(51,51,199)
|
||||
stateMachine.ProcessString(L"\033P2$p1;2;20;20;78\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 51, 199), gci.GetColorTableEntry(1));
|
||||
|
||||
// RGB(79%,13%,13%) -> RGB(201,33,33)
|
||||
stateMachine.ProcessString(L"\033P2$p2;2;79;13;13\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(201, 33, 33), gci.GetColorTableEntry(2));
|
||||
|
||||
// RGB(20%,78%,20%) -> RGB(51,199,51)
|
||||
stateMachine.ProcessString(L"\033P2$p3;2;20;78;20\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 199, 51), gci.GetColorTableEntry(3));
|
||||
|
||||
// RGB(78%,20%,78%) -> RGB(199,51,199)
|
||||
stateMachine.ProcessString(L"\033P2$p4;2;78;20;78\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(199, 51, 199), gci.GetColorTableEntry(4));
|
||||
|
||||
// RGB(20%,78%,78%) -> RGB(51,199,199)
|
||||
stateMachine.ProcessString(L"\033P2$p5;2;20;78;78\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(51, 199, 199), gci.GetColorTableEntry(5));
|
||||
|
||||
// RGB(78%,78%,20%) -> RGB(199,199,51)
|
||||
stateMachine.ProcessString(L"\033P2$p6;2;78;78;20\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(199, 199, 51), gci.GetColorTableEntry(6));
|
||||
|
||||
// RGB(46%,46%,46%) -> RGB(117,117,117)
|
||||
stateMachine.ProcessString(L"\033P2$p7;2;46;46;46\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(117, 117, 117), gci.GetColorTableEntry(7));
|
||||
|
||||
// RGB(26%,26%,26%) -> RGB(66,66,66)
|
||||
stateMachine.ProcessString(L"\033P2$p8;2;26;26;26\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(66, 66, 66), gci.GetColorTableEntry(8));
|
||||
|
||||
// RGB(33%,33%,59%) -> RGB(84,84,150)
|
||||
stateMachine.ProcessString(L"\033P2$p9;2;33;33;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 84, 150), gci.GetColorTableEntry(9));
|
||||
|
||||
// RGB(58%,26%,26%) -> RGB(148,66,66)
|
||||
stateMachine.ProcessString(L"\033P2$p10;2;58;26;26\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(148, 66, 66), gci.GetColorTableEntry(10));
|
||||
|
||||
// RGB(33%,59%,33%) -> RGB(84,150,84)
|
||||
stateMachine.ProcessString(L"\033P2$p11;2;33;59;33\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 150, 84), gci.GetColorTableEntry(11));
|
||||
|
||||
// RGB(59%,33%,59%) -> RGB(150,84,150)
|
||||
stateMachine.ProcessString(L"\033P2$p12;2;59;33;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(150, 84, 150), gci.GetColorTableEntry(12));
|
||||
|
||||
// RGB(33%,59%,59%) -> RGB(84,150,150)
|
||||
stateMachine.ProcessString(L"\033P2$p13;2;33;59;59\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(84, 150, 150), gci.GetColorTableEntry(13));
|
||||
|
||||
// RGB(59%,59%,33%) -> RGB(150,150,84)
|
||||
stateMachine.ProcessString(L"\033P2$p14;2;59;59;33\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(150, 150, 84), gci.GetColorTableEntry(14));
|
||||
|
||||
// RGB(79%,79%,79%) -> RGB(201,201,201)
|
||||
stateMachine.ProcessString(L"\033P2$p15;2;79;79;79\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(201, 201, 201), gci.GetColorTableEntry(15));
|
||||
|
||||
// Reset everything to white again.
|
||||
for (auto i = 0; i < 16; i++)
|
||||
{
|
||||
gci.SetColorTableEntry(i, RGB(255, 255, 255));
|
||||
}
|
||||
|
||||
Log::Comment(L"Multiple color definitions");
|
||||
|
||||
// Setting colors 0, 2, and 4 to red, green, and blue (HLS).
|
||||
stateMachine.ProcessString(L"\033P2$p0;1;120;50;100/2;1;240;50;100/4;1;360;50;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(255, 0, 0), gci.GetColorTableEntry(0));
|
||||
VERIFY_ARE_EQUAL(RGB(0, 255, 0), gci.GetColorTableEntry(2));
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 255), gci.GetColorTableEntry(4));
|
||||
|
||||
// Setting colors 1, 3, and 5 to red, green, and blue (RGB).
|
||||
stateMachine.ProcessString(L"\033P2$p1;2;100;0;0/3;2;0;100;0/5;2;0;0;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(255, 0, 0), gci.GetColorTableEntry(1));
|
||||
VERIFY_ARE_EQUAL(RGB(0, 255, 0), gci.GetColorTableEntry(3));
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 255), gci.GetColorTableEntry(5));
|
||||
|
||||
// The interpretation of omitted and out of range parameter values is based
|
||||
// on the VT240 and VT340 sixel implementations. It is assumed that color
|
||||
// parsing is handled in the same way for other operations.
|
||||
|
||||
Log::Comment(L"Omitted parameter values");
|
||||
|
||||
// Omitted hue interpreted as 0° (blue)
|
||||
stateMachine.ProcessString(L"\033P2$p6;1;;50;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 255), gci.GetColorTableEntry(6));
|
||||
|
||||
// Omitted luminosity interpreted as 0% (black)
|
||||
stateMachine.ProcessString(L"\033P2$p7;1;120;;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 0), gci.GetColorTableEntry(7));
|
||||
|
||||
// Omitted saturation interpreted as 0% (gray)
|
||||
stateMachine.ProcessString(L"\033P2$p8;1;120;50\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(128, 128, 128), gci.GetColorTableEntry(8));
|
||||
|
||||
// Omitted red component interpreted as 0%
|
||||
stateMachine.ProcessString(L"\033P2$p6;2;;50;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 128, 255), gci.GetColorTableEntry(6));
|
||||
|
||||
// Omitted green component interpreted as 0%
|
||||
stateMachine.ProcessString(L"\033P2$p7;2;50;;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(128, 0, 255), gci.GetColorTableEntry(7));
|
||||
|
||||
// Omitted blue component interpreted as 0%
|
||||
stateMachine.ProcessString(L"\033P2$p8;2;50;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(128, 255, 0), gci.GetColorTableEntry(8));
|
||||
|
||||
Log::Comment(L"Out of range parameter values");
|
||||
|
||||
// Hue wraps at 360°, so 480° interpreted as 120° (red)
|
||||
stateMachine.ProcessString(L"\033P2$p9;1;480;50;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(255, 0, 0), gci.GetColorTableEntry(9));
|
||||
|
||||
// Luminosity is clamped at 100%, so 150% interpreted as 100%
|
||||
stateMachine.ProcessString(L"\033P2$p10;1;240;150;100\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(255, 255, 255), gci.GetColorTableEntry(10));
|
||||
|
||||
// Saturation is clamped at 100%, so 120% interpreted as 100%
|
||||
stateMachine.ProcessString(L"\033P2$p11;1;0;50;120\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 255), gci.GetColorTableEntry(11));
|
||||
|
||||
// Red component is clamped at 100%, so 150% interpreted as 100%
|
||||
stateMachine.ProcessString(L"\033P2$p12;2;150;0;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(255, 0, 0), gci.GetColorTableEntry(12));
|
||||
|
||||
// Green component is clamped at 100%, so 150% interpreted as 100%
|
||||
stateMachine.ProcessString(L"\033P2$p13;2;0;150;0\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 255, 0), gci.GetColorTableEntry(13));
|
||||
|
||||
// Blue component is clamped at 100%, so 150% interpreted as 100%
|
||||
stateMachine.ProcessString(L"\033P2$p14;2;0;0;150\033\\");
|
||||
VERIFY_ARE_EQUAL(RGB(0, 0, 255), gci.GetColorTableEntry(14));
|
||||
}
|
||||
|
||||
void ScreenBufferTests::ResizeTraditionalDoesNotDoubleFreeAttrRows()
|
||||
{
|
||||
// there is not much to verify here, this test passes if the console doesn't crash.
|
||||
|
|
|
@ -245,6 +245,12 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
|||
WindowFrame = 2,
|
||||
};
|
||||
|
||||
enum class ColorModel : VTInt
|
||||
{
|
||||
HLS = 1,
|
||||
RGB = 2,
|
||||
};
|
||||
|
||||
enum class EraseType : VTInt
|
||||
{
|
||||
ToEnd = 0,
|
||||
|
@ -481,6 +487,12 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
|||
Size96 = 1
|
||||
};
|
||||
|
||||
enum class ReportFormat : VTInt
|
||||
{
|
||||
TerminalStateReport = 1,
|
||||
ColorTableReport = 2
|
||||
};
|
||||
|
||||
constexpr VTInt s_sDECCOLMSetColumns = 132;
|
||||
constexpr VTInt s_sDECCOLMResetColumns = 80;
|
||||
|
||||
|
|
|
@ -144,6 +144,8 @@ public:
|
|||
const VTParameter cellHeight,
|
||||
const DispatchTypes::DrcsCharsetSize charsetSize) = 0; // DECDLD
|
||||
|
||||
virtual StringHandler RestoreTerminalState(const DispatchTypes::ReportFormat format) = 0; // DECRSTS
|
||||
|
||||
virtual StringHandler RequestSetting() = 0; // DECRQSS
|
||||
|
||||
virtual bool PlaySounds(const VTParameters parameters) = 0; // DECPS
|
||||
|
|
|
@ -2552,6 +2552,79 @@ ITermDispatch::StringHandler AdaptDispatch::DownloadDRCS(const VTInt fontNumber,
|
|||
};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - DECRSTS - Restores the terminal state from a stream of data previously
|
||||
// saved with a DECRQTSR query.
|
||||
// Arguments:
|
||||
// - format - the format of the state report being restored.
|
||||
// Return Value:
|
||||
// - a function to receive the data or nullptr if the format is unsupported.
|
||||
ITermDispatch::StringHandler AdaptDispatch::RestoreTerminalState(const DispatchTypes::ReportFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case DispatchTypes::ReportFormat::ColorTableReport:
|
||||
return _RestoreColorTable();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - DECCTR - This is a parser for the Color Table Report received via DECRSTS.
|
||||
// The report contains a list of color definitions separated with a slash
|
||||
// character. Each definition consists of 5 parameters: Pc;Pu;Px;Py;Pz
|
||||
// - Pc is the color number.
|
||||
// - Pu is the color model (1 = HLS, 2 = RGB).
|
||||
// - Px, Py, and Pz are component values in the color model.
|
||||
// Arguments:
|
||||
// - <none>
|
||||
// Return Value:
|
||||
// - a function to parse the report data.
|
||||
ITermDispatch::StringHandler AdaptDispatch::_RestoreColorTable()
|
||||
{
|
||||
return [this, parameter = VTInt{}, parameters = std::vector<VTParameter>{}](const auto ch) mutable {
|
||||
if (ch >= L'0' && ch <= L'9')
|
||||
{
|
||||
parameter *= 10;
|
||||
parameter += (ch - L'0');
|
||||
parameter = std::min(parameter, MAX_PARAMETER_VALUE);
|
||||
}
|
||||
else if (ch == L';')
|
||||
{
|
||||
if (parameters.size() < 5)
|
||||
{
|
||||
parameters.push_back(parameter);
|
||||
}
|
||||
parameter = 0;
|
||||
}
|
||||
else if (ch == L'/' || ch == AsciiChars::ESC)
|
||||
{
|
||||
parameters.push_back(parameter);
|
||||
const auto colorParameters = VTParameters{ parameters.data(), parameters.size() };
|
||||
const auto colorNumber = colorParameters.at(0).value_or(0);
|
||||
if (colorNumber < TextColor::TABLE_SIZE)
|
||||
{
|
||||
const auto colorModel = DispatchTypes::ColorModel{ colorParameters.at(1) };
|
||||
const auto x = colorParameters.at(2).value_or(0);
|
||||
const auto y = colorParameters.at(3).value_or(0);
|
||||
const auto z = colorParameters.at(4).value_or(0);
|
||||
if (colorModel == DispatchTypes::ColorModel::HLS)
|
||||
{
|
||||
SetColorTableEntry(colorNumber, Utils::ColorFromHLS(x, y, z));
|
||||
}
|
||||
else if (colorModel == DispatchTypes::ColorModel::RGB)
|
||||
{
|
||||
SetColorTableEntry(colorNumber, Utils::ColorFromRGB100(x, y, z));
|
||||
}
|
||||
}
|
||||
parameters.clear();
|
||||
parameter = 0;
|
||||
}
|
||||
return (ch != AsciiChars::ESC);
|
||||
};
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - DECRQSS - Requests the state of a VT setting. The value being queried is
|
||||
// identified by the intermediate and final characters of its control
|
||||
|
|
|
@ -139,6 +139,8 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
const VTParameter cellHeight,
|
||||
const DispatchTypes::DrcsCharsetSize charsetSize) override; // DECDLD
|
||||
|
||||
StringHandler RestoreTerminalState(const DispatchTypes::ReportFormat format) override; // DECRSTS
|
||||
|
||||
StringHandler RequestSetting() override; // DECRQSS
|
||||
|
||||
bool PlaySounds(const VTParameters parameters) override; // DECPS
|
||||
|
@ -198,6 +200,8 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
void _ResetTabStops() noexcept;
|
||||
void _InitTabStopsForWidth(const VTInt width);
|
||||
|
||||
StringHandler _RestoreColorTable();
|
||||
|
||||
void _ReportSGRSetting() const;
|
||||
void _ReportDECSTBMSetting();
|
||||
|
||||
|
|
|
@ -135,7 +135,9 @@ public:
|
|||
const DispatchTypes::DrcsFontSet /*fontSet*/,
|
||||
const DispatchTypes::DrcsFontUsage /*fontUsage*/,
|
||||
const VTParameter /*cellHeight*/,
|
||||
const DispatchTypes::DrcsCharsetSize /*charsetSize*/) override { return nullptr; }
|
||||
const DispatchTypes::DrcsCharsetSize /*charsetSize*/) override { return nullptr; } // DECDLD
|
||||
|
||||
StringHandler RestoreTerminalState(const DispatchTypes::ReportFormat /*format*/) override { return nullptr; }; // DECRSTS
|
||||
|
||||
StringHandler RequestSetting() override { return nullptr; }; // DECRQSS
|
||||
|
||||
|
|
|
@ -667,6 +667,9 @@ IStateMachineEngine::StringHandler OutputStateMachineEngine::ActionDcsDispatch(c
|
|||
parameters.at(6),
|
||||
parameters.at(7));
|
||||
break;
|
||||
case DcsActionCodes::DECRSTS_RestoreTerminalState:
|
||||
handler = _dispatch->RestoreTerminalState(parameters.at(0));
|
||||
break;
|
||||
case DcsActionCodes::DECRQSS_RequestSetting:
|
||||
handler = _dispatch->RequestSetting();
|
||||
break;
|
||||
|
|
|
@ -149,6 +149,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
enum DcsActionCodes : uint64_t
|
||||
{
|
||||
DECDLD_DownloadDRCS = VTID("{"),
|
||||
DECRSTS_RestoreTerminalState = VTID("$p"),
|
||||
DECRQSS_RequestSetting = VTID("$q")
|
||||
};
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ namespace Microsoft::Console::Utils
|
|||
til::color ColorFromHexString(const std::string_view wstr);
|
||||
std::optional<til::color> ColorFromXTermColor(const std::wstring_view wstr) noexcept;
|
||||
std::optional<til::color> ColorFromXParseColorSpec(const std::wstring_view wstr) noexcept;
|
||||
til::color ColorFromHLS(const int h, const int l, const int s) noexcept;
|
||||
til::color ColorFromRGB100(const int r, const int g, const int b) noexcept;
|
||||
|
||||
bool HexToUint(const wchar_t wch, unsigned int& value) noexcept;
|
||||
bool StringToUint(const std::wstring_view wstr, unsigned int& value);
|
||||
|
|
|
@ -318,6 +318,84 @@ catch (...)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Constructs a til::color value from RGB percentage components.
|
||||
// Arguments:
|
||||
// - r - The red component of the color (0-100%).
|
||||
// - g - The green component of the color (0-100%).
|
||||
// - b - The blue component of the color (0-100%).
|
||||
// Return Value:
|
||||
// - The color defined by the given components.
|
||||
til::color Utils::ColorFromRGB100(const int r, const int g, const int b) noexcept
|
||||
{
|
||||
// The color class is expecting components in the range 0 to 255,
|
||||
// so we need to scale our percentage values by 255/100. We can
|
||||
// optimise this conversion with a pre-created lookup table.
|
||||
static constexpr auto scale100To255 = [] {
|
||||
std::array<uint8_t, 101> lut{};
|
||||
for (size_t i = 0; i < std::size(lut); i++)
|
||||
{
|
||||
lut.at(i) = gsl::narrow_cast<uint8_t>((i * 255 + 50) / 100);
|
||||
}
|
||||
return lut;
|
||||
}();
|
||||
|
||||
const auto red = til::at(scale100To255, std::min<unsigned>(r, 100u));
|
||||
const auto green = til::at(scale100To255, std::min<unsigned>(g, 100u));
|
||||
const auto blue = til::at(scale100To255, std::min<unsigned>(b, 100u));
|
||||
return { red, green, blue };
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Constructs a til::color value from HLS components.
|
||||
// Arguments:
|
||||
// - h - The hue component of the color (0-360°).
|
||||
// - l - The luminosity component of the color (0-100%).
|
||||
// - s - The saturation component of the color (0-100%).
|
||||
// Return Value:
|
||||
// - The color defined by the given components.
|
||||
til::color Utils::ColorFromHLS(const int h, const int l, const int s) noexcept
|
||||
{
|
||||
const auto hue = h % 360;
|
||||
const auto lum = gsl::narrow_cast<float>(std::min(l, 100));
|
||||
const auto sat = gsl::narrow_cast<float>(std::min(s, 100));
|
||||
|
||||
// This calculation is based on the HSL to RGB algorithm described in
|
||||
// Wikipedia: https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB
|
||||
// We start by calculating the chroma value, and the point along the bottom
|
||||
// faces of the RGB cube with the same hue and chroma as our color (x).
|
||||
const auto chroma = (50.f - abs(lum - 50.f)) * sat / 50.f;
|
||||
const auto x = chroma * (60 - abs(hue % 120 - 60)) / 60.f;
|
||||
|
||||
// We'll also need an offset added to each component to match lightness.
|
||||
const auto lightness = lum - chroma / 2.0f;
|
||||
|
||||
// We use the chroma value for the brightest component, x for the second
|
||||
// brightest, and 0 for the last. The values are scaled by 255/100 to get
|
||||
// them in the range 0 to 255, as required by the color class.
|
||||
constexpr auto scale = 255.f / 100.f;
|
||||
const auto comp1 = gsl::narrow_cast<uint8_t>((chroma + lightness) * scale + 0.5f);
|
||||
const auto comp2 = gsl::narrow_cast<uint8_t>((x + lightness) * scale + 0.5f);
|
||||
const auto comp3 = gsl::narrow_cast<uint8_t>((0 + lightness) * scale + 0.5f);
|
||||
|
||||
// Finally we order the components based on the given hue. But note that the
|
||||
// DEC terminals used a different mapping for hue than is typical for modern
|
||||
// color models. Blue is at 0°, red is at 120°, and green is at 240°.
|
||||
// See DEC STD 070, ReGIS Graphics Extension, § 8.6.2.2.2, Color by Value.
|
||||
if (hue < 60)
|
||||
return { comp2, comp3, comp1 }; // blue to magenta
|
||||
else if (hue < 120)
|
||||
return { comp1, comp3, comp2 }; // magenta to red
|
||||
else if (hue < 180)
|
||||
return { comp1, comp2, comp3 }; // red to yellow
|
||||
else if (hue < 240)
|
||||
return { comp2, comp1, comp3 }; // yellow to green
|
||||
else if (hue < 300)
|
||||
return { comp3, comp1, comp2 }; // green to cyan
|
||||
else
|
||||
return { comp3, comp2, comp1 }; // cyan to blue
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Converts a hex character to its equivalent integer value.
|
||||
// Arguments:
|
||||
|
|
Loading…
Reference in New Issue