From 00ee88400aab0cd94409204ce0c6d20854ba7eff Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 19 Jun 2025 02:13:00 +0100 Subject: [PATCH] Indicate support for OSC 52 in the DA1 report (#19034) ## Summary of the Pull Request Some applications that make use of the `OSC 52` clipboard sequence will only do so if they can be certain that the terminal actually has that functionality. Indicating our support for `OSC 52` in the `DA1` report will give them an easy way to detect that. ## References and Relevant Issues `OSC 52` support was added to Windows Terminal in issue #5823, and to ConHost in issue #18949. ## Detailed Description of the Pull Request / Additional comments Support for writing to the clipboard is indicated in the primary device attributes report by the extension parameter `52`. This is obviously not a standard DEC extension, but it's one that's been agreed upon by a number of modern terminals. The extension is only reported when writing to the clipboard is actually permitted (Windows Terminal has an option to disable that). ## Validation Steps Performed I've updated the Device Attributes unit test to check that we're reporting extension `52` when clipboard access is enabled, and not reporting it when disabled. ## PR Checklist - [x] Closes #19017 - [x] Tests added/passed --- src/cascadia/TerminalCore/Terminal.cpp | 9 +++++--- src/cascadia/TerminalCore/Terminal.hpp | 2 +- src/terminal/adapter/ITermDispatch.hpp | 9 +++++++- src/terminal/adapter/adaptDispatch.cpp | 22 +++++++++++++------ src/terminal/adapter/adaptDispatch.hpp | 4 ++-- src/terminal/adapter/termDispatch.hpp | 2 +- .../adapter/ut_adapter/adapterTest.cpp | 13 +++++++---- 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 039b251144..005916822d 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -94,7 +94,7 @@ void Terminal::UpdateSettings(ICoreSettings settings) if (_stateMachine) { - SetVtChecksumReportSupport(settings.AllowVtChecksumReport()); + SetOptionalFeatures(settings); } _getTerminalInput().ForceDisableWin32InputMode(settings.ForceVTInput()); @@ -232,10 +232,13 @@ void Terminal::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) engine.Dispatch().SetCursorStyle(cursorStyle); } -void Terminal::SetVtChecksumReportSupport(const bool enabled) +void Terminal::SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings) { auto& engine = reinterpret_cast(_stateMachine->Engine()); - engine.Dispatch().SetVtChecksumReportSupport(enabled); + auto features = til::enumset{}; + features.set(ITermDispatch::OptionalFeature::ChecksumReport, settings.AllowVtChecksumReport()); + features.set(ITermDispatch::OptionalFeature::ClipboardWrite, settings.AllowVtClipboardWrite()); + engine.Dispatch().SetOptionalFeatures(features); } bool Terminal::IsXtermBracketedPasteModeEnabled() const noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index afbf873da9..e38841e3bc 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -95,7 +95,7 @@ public: void SetHighContrastMode(bool hc) noexcept; void SetFontInfo(const FontInfo& fontInfo); void SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle); - void SetVtChecksumReportSupport(const bool enabled); + void SetOptionalFeatures(winrt::Microsoft::Terminal::Core::ICoreSettings settings); bool IsXtermBracketedPasteModeEnabled() const noexcept; std::wstring_view GetWorkingDirectory() noexcept; diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index da24f71b6f..5ebb2ff31c 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -25,6 +25,12 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch public: using StringHandler = std::function; + enum class OptionalFeature + { + ChecksumReport, + ClipboardWrite, + }; + #pragma warning(push) #pragma warning(disable : 26432) // suppress rule of 5 violation on interface because tampering with this is fraught with peril virtual ~ITermDispatch() = 0; @@ -133,7 +139,6 @@ public: virtual void ScreenAlignmentPattern() = 0; // DECALN virtual void SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR - virtual void SetVtChecksumReportSupport(const bool enabled) = 0; virtual void SetClipboard(wil::zwstring_view content) = 0; // OSCSetClipboard @@ -185,6 +190,8 @@ public: virtual StringHandler RestorePresentationState(const DispatchTypes::PresentationReportFormat format) = 0; // DECRSPS virtual void PlaySounds(const VTParameters parameters) = 0; // DECPS + + virtual void SetOptionalFeatures(const til::enumset features) = 0; }; inline Microsoft::Console::VirtualTerminal::ITermDispatch::~ITermDispatch() = default; #pragma warning(pop) diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index bc03ae8331..cb8e4feb5a 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1297,11 +1297,6 @@ void AdaptDispatch::SelectAttributeChangeExtent(const DispatchTypes::ChangeExten } } -void AdaptDispatch::SetVtChecksumReportSupport(const bool enabled) noexcept -{ - _vtChecksumReportEnabled = enabled; -} - // Routine Description: // - DECRQCRA - Computes and reports a checksum of the specified area of // the buffer memory. @@ -1318,7 +1313,7 @@ void AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p // If this feature is not enabled, we'll just report a zero checksum. if constexpr (Feature_VtChecksumReport::IsEnabled()) { - if (_vtChecksumReportEnabled) + if (_optionalFeatures.test(OptionalFeature::ChecksumReport)) { // If the page number is 0, then we're meant to return a checksum of all // of the pages, but we have no need for that, so we'll just return 0. @@ -1483,8 +1478,16 @@ void AdaptDispatch::DeviceAttributes() // 28 = Rectangular area operations // 32 = Text macros // 42 = ISO Latin-2 character set + // 52 = Clipboard access - _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42c"); + if (_optionalFeatures.test(OptionalFeature::ClipboardWrite)) + { + _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42;52c"); + } + else + { + _ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42c"); + } } // Routine Description: @@ -4790,3 +4793,8 @@ void AdaptDispatch::PlaySounds(const VTParameters parameters) _api.PlayMidiNote(noteNumber, noteNumber == 71 ? 0 : velocity, duration); }); } + +void AdaptDispatch::SetOptionalFeatures(const til::enumset features) noexcept +{ + _optionalFeatures = features; +} diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 313611a732..af726c0e01 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -188,7 +188,7 @@ namespace Microsoft::Console::VirtualTerminal void PlaySounds(const VTParameters parameters) override; // DECPS - void SetVtChecksumReportSupport(const bool enabled) noexcept override; + void SetOptionalFeatures(const til::enumset features) noexcept override; private: enum class Mode @@ -313,7 +313,7 @@ namespace Microsoft::Console::VirtualTerminal std::unique_ptr _fontBuffer; std::shared_ptr _macroBuffer; std::optional _initialCodePage; - bool _vtChecksumReportEnabled = false; + til::enumset _optionalFeatures = { OptionalFeature::ClipboardWrite }; // We have two instances of the saved cursor state, because we need // one for the main buffer (at index 0), and another for the alt buffer diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index 0f3908331b..99c9033fee 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -178,7 +178,7 @@ public: void PlaySounds(const VTParameters /*parameters*/) override{}; // DECPS - void SetVtChecksumReportSupport(const bool /*enabled*/) override{}; + void SetOptionalFeatures(const til::enumset /*features*/) override{}; }; #pragma warning(default : 26440) // Restore "can be declared noexcept" warning diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index b288561359..a6eecb6b37 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -415,7 +415,6 @@ public: auto& renderer = _testGetSet->_renderer; auto& renderSettings = renderer._renderSettings; auto adapter = std::make_unique(*_testGetSet, &renderer, renderSettings, _terminalInput); - adapter->SetVtChecksumReportSupport(true); fSuccess = adapter.get() != nullptr; if (fSuccess) @@ -1737,11 +1736,15 @@ public: Log::Comment(L"Test 1: Verify normal response."); _testGetSet->PrepData(); _pDispatch->DeviceAttributes(); + _testGetSet->ValidateInputEvent(L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42;52c"); - auto pwszExpectedResponse = L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42c"; - _testGetSet->ValidateInputEvent(pwszExpectedResponse); + Log::Comment(L"Test 2: Verify response with clipboard disabled."); + _testGetSet->PrepData(); + _pDispatch->SetOptionalFeatures({}); + _pDispatch->DeviceAttributes(); + _testGetSet->ValidateInputEvent(L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42c"); - Log::Comment(L"Test 2: Verify failure when ReturnResponse doesn't work."); + Log::Comment(L"Test 3: Verify failure when ReturnResponse doesn't work."); _testGetSet->PrepData(); _testGetSet->_returnResponseResult = FALSE; @@ -2187,6 +2190,8 @@ public: using namespace std::string_view_literals; + _pDispatch->SetOptionalFeatures(ITermDispatch::OptionalFeature::ChecksumReport); + Log::Comment(L"Test 1: ASCII characters"); outputText(L"A"sv); verifyChecksumReport(L"FF4F");