Skip to content

Commit

Permalink
Add support for querying the DECAC settings (#14990)
Browse files Browse the repository at this point in the history
This PR adds support for querying the color indices set by the `DECAC`
control, using the existing `DECRQSS` implementation.

## References and Relevant Issues

The initial `DECRQSS` support was added in PR #11152.
The `DECAC` functionality was added in PR #13058, but at the time we
didn't know how to format the associated `DECRQSS` query.

## Detailed Description of the Pull Request / Additional comments

For most `DECRQSS` queries, the setting being requested is identified by
the final characters of its escape sequence. However, for the `DECAC`
settings, you also need to include a parameter value, to indicate which
color item you're querying.

This meant we needed to extend the `DECRQSS` parser, so I also took this
opportunity to ensure we correctly parsed any parameter prefix chars. We
don't yet support any setting requiring a prefix, but this makes sure we
don't respond incorrectly if an app does query such a setting.

## Validation Steps Performed

Thanks to @al20878, we've been able to test how these queries are parsed
on a real VT525 terminal, and I've manually verified our implementation
matches that behavior.

I've also extended the existing `DECRQSS` unit test to confirm that we
are responding to the `DECAC` queries as expected.

Closes #13091
  • Loading branch information
j4james authored Mar 17, 2023
1 parent 5c9f756 commit 2810155
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 6 deletions.
66 changes: 60 additions & 6 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3487,11 +3487,11 @@ ITermDispatch::StringHandler AdaptDispatch::RequestSetting()
// this is the opposite of what is documented in most DEC manuals, which
// say that 0 is for a valid response, and 1 is for an error. The correct
// interpretation is documented in the DEC STD 070 reference.
const auto idBuilder = std::make_shared<VTIDBuilder>();
return [=](const auto ch) {
if (ch >= '\x40' && ch <= '\x7e')
return [this, parameter = VTInt{}, idBuilder = VTIDBuilder{}](const auto ch) mutable {
const auto isFinal = ch >= L'\x40' && ch <= L'\x7e';
if (isFinal)
{
const auto id = idBuilder->Finalize(ch);
const auto id = idBuilder.Finalize(ch);
switch (id)
{
case VTID("m"):
Expand All @@ -3506,6 +3506,9 @@ ITermDispatch::StringHandler AdaptDispatch::RequestSetting()
case VTID("*x"):
_ReportDECSACESetting();
break;
case VTID(",|"):
_ReportDECACSetting(VTParameter{ parameter });
break;
default:
_api.ReturnResponse(L"\033P0$r\033\\");
break;
Expand All @@ -3514,9 +3517,22 @@ ITermDispatch::StringHandler AdaptDispatch::RequestSetting()
}
else
{
if (ch >= '\x20' && ch <= '\x2f')
// Although we don't yet support any operations with parameter
// prefixes, it's important that we still parse the prefix and
// include it in the ID. Otherwise we'll mistakenly respond to
// prefixed queries that we don't actually recognise.
const auto isParameterPrefix = ch >= L'<' && ch <= L'?';
const auto isParameter = ch >= L'0' && ch < L'9';
const auto isIntermediate = ch >= L'\x20' && ch <= L'\x2f';
if (isParameterPrefix || isIntermediate)
{
idBuilder.AddIntermediate(ch);
}
else if (isParameter)
{
idBuilder->AddIntermediate(ch);
parameter *= 10;
parameter += (ch - L'0');
parameter = std::min(parameter, MAX_PARAMETER_VALUE);
}
return true;
}
Expand Down Expand Up @@ -3656,6 +3672,44 @@ void AdaptDispatch::_ReportDECSACESetting() const
_api.ReturnResponse({ response.data(), response.size() });
}

// Method Description:
// - Reports the DECAC color assignments in response to a DECRQSS query.
// Arguments:
// - None
// Return Value:
// - None
void AdaptDispatch::_ReportDECACSetting(const VTInt itemNumber) const
{
using namespace std::string_view_literals;

size_t fgIndex = 0;
size_t bgIndex = 0;
switch (static_cast<DispatchTypes::ColorItem>(itemNumber))
{
case DispatchTypes::ColorItem::NormalText:
fgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultForeground);
bgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::DefaultBackground);
break;
case DispatchTypes::ColorItem::WindowFrame:
fgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::FrameForeground);
bgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::FrameBackground);
break;
default:
_api.ReturnResponse(L"\033P0$r\033\\");
return;
}

// A valid response always starts with DCS 1 $ r.
fmt::basic_memory_buffer<wchar_t, 64> response;
response.append(L"\033P1$r"sv);

fmt::format_to(std::back_inserter(response), FMT_COMPILE(L"{};{};{}"), itemNumber, fgIndex, bgIndex);

// The ',|' indicates this is a DECAC response, and ST ends the sequence.
response.append(L",|\033\\"sv);
_api.ReturnResponse({ response.data(), response.size() });
}

// Routine Description:
// - DECPS - Plays a sequence of musical notes.
// Arguments:
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ namespace Microsoft::Console::VirtualTerminal
void _ReportDECSTBMSetting();
void _ReportDECSCASetting() const;
void _ReportDECSACESetting() const;
void _ReportDECACSetting(const VTInt itemNumber) const;

StringHandler _CreateDrcsPassthroughHandler(const DispatchTypes::DrcsCharsetSize charsetSize);
StringHandler _CreatePassthroughHandler();
Expand Down
24 changes: 24 additions & 0 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1753,6 +1753,30 @@ class AdapterTest
requestSetting(L"\"q");
_testGetSet->ValidateInputEvent(L"\033P1$r1\"q\033\\");

// Initialize the color alias indices for the DECAC tests below.
_testGetSet->PrepData();
auto& renderSettings = _testGetSet->_renderer._renderSettings;
renderSettings.SetColorAliasIndex(ColorAlias::DefaultForeground, 3);
renderSettings.SetColorAliasIndex(ColorAlias::DefaultBackground, 5);
renderSettings.SetColorAliasIndex(ColorAlias::FrameForeground, 4);
renderSettings.SetColorAliasIndex(ColorAlias::FrameBackground, 6);

Log::Comment(L"Requesting DECAC colors (default).");
requestSetting(L",|");
_testGetSet->ValidateInputEvent(L"\033P1$r1;3;5,|\033\\");

Log::Comment(L"Requesting DECAC colors (normal text).");
requestSetting(L"1,|");
_testGetSet->ValidateInputEvent(L"\033P1$r1;3;5,|\033\\");

Log::Comment(L"Requesting DECAC colors (window frame).");
requestSetting(L"2,|");
_testGetSet->ValidateInputEvent(L"\033P1$r2;4;6,|\033\\");

Log::Comment(L"Requesting DECAC colors (invalid item).");
requestSetting(L"3,|");
_testGetSet->ValidateInputEvent(L"\033P0$r\033\\");

Log::Comment(L"Requesting an unsupported setting.");
_testGetSet->PrepData();
requestSetting(L"x");
Expand Down

0 comments on commit 2810155

Please sign in to comment.