From 281015504629739995ddb63dd542615570919970 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Fri, 17 Mar 2023 19:16:47 +0000 Subject: [PATCH] Add support for querying the DECAC settings (#14990) 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 --- src/terminal/adapter/adaptDispatch.cpp | 66 +++++++++++++++++-- src/terminal/adapter/adaptDispatch.hpp | 1 + .../adapter/ut_adapter/adapterTest.cpp | 24 +++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index d43fea1bb83..b3937d9d433 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -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(); - 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"): @@ -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; @@ -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; } @@ -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(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 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: diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index aab3402416f..b2a0fe1a353 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -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(); diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 4b49d63a25a..b080dbfc084 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -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");