From e4fe3fc619f181672455ce00f6a51b26b9f0369a Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Sat, 9 May 2020 12:54:32 +0800 Subject: [PATCH 01/13] Add support for VT OSC copy-to-clipboard Signed-off-by: Mingxiang Xue --- src/cascadia/TerminalControl/TermControl.cpp | 11 +++ src/cascadia/TerminalControl/TermControl.h | 1 + src/cascadia/TerminalCore/ITerminalApi.hpp | 2 + src/cascadia/TerminalCore/Terminal.cpp | 5 ++ src/cascadia/TerminalCore/Terminal.hpp | 4 ++ src/cascadia/TerminalCore/TerminalApi.cpp | 9 +++ .../TerminalCore/TerminalDispatch.cpp | 7 ++ .../TerminalCore/TerminalDispatch.hpp | 2 + src/host/VtIo.cpp | 13 ++++ src/host/VtIo.hpp | 2 + src/host/outputStream.cpp | 18 +++++ src/host/outputStream.hpp | 2 + src/renderer/vt/VtSequences.cpp | 30 +++++++++ src/renderer/vt/vtrenderer.hpp | 7 ++ src/terminal/adapter/ITermDispatch.hpp | 2 + src/terminal/adapter/adaptDispatch.cpp | 11 +++ src/terminal/adapter/adaptDispatch.hpp | 2 + src/terminal/adapter/conGetSet.hpp | 2 + src/terminal/adapter/termDispatch.hpp | 2 + .../adapter/ut_adapter/adapterTest.cpp | 5 ++ .../parser/OutputStateMachineEngine.cpp | 24 ++++--- .../parser/OutputStateMachineEngine.hpp | 5 +- src/terminal/parser/stateMachine.cpp | 2 +- .../parser/ut_parser/OutputEngineTest.cpp | 67 +++++++++++++++++++ 24 files changed, 224 insertions(+), 11 deletions(-) mode change 100644 => 100755 src/terminal/parser/OutputStateMachineEngine.cpp mode change 100644 => 100755 src/terminal/parser/OutputStateMachineEngine.hpp diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 94878332c51..0d35c07fabf 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -89,6 +89,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation auto pfnTerminalCursorPositionChanged = std::bind(&TermControl::_TerminalCursorPositionChanged, this); _terminal->SetCursorPositionChangedCallback(pfnTerminalCursorPositionChanged); + auto pfnCopyToClipboard = std::bind(&TermControl::_CopyToClipboard, this, std::placeholders::_1); + _terminal->SetCopyToClipboardCallback(pfnCopyToClipboard); + // This event is explicitly revoked in the destructor: does not need weak_ref auto onReceiveOutputFn = [this](const hstring str) { _terminal->Write(str); @@ -1906,6 +1909,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _titleChangedHandlers(winrt::hstring{ wstr }); } + void TermControl::_CopyToClipboard(const std::wstring_view& wstr) + { + auto copyArgs = winrt::make_self(winrt::hstring(wstr), + winrt::hstring(L""), + winrt::hstring(L"")); + _clipboardCopyHandlers(*this, *copyArgs); + } + // Method Description: // - Update the position and size of the scrollbar to match the given // viewport top, viewport height, and buffer size. diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index c42f2246abe..ad38d81ffd6 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -200,6 +200,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _DoResizeUnderLock(const double newWidth, const double newHeight); void _RefreshSizeUnderLock(); void _TerminalTitleChanged(const std::wstring_view& wstr); + void _CopyToClipboard(const std::wstring_view& wstr); winrt::fire_and_forget _TerminalScrollPositionChanged(const int viewTop, const int viewHeight, const int bufferSize); winrt::fire_and_forget _TerminalCursorPositionChanged(); diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index b521f4ecea2..e30abd86fdb 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -58,6 +58,8 @@ namespace Microsoft::Terminal::Core virtual bool IsVtInputEnabled() const = 0; + virtual bool CopyToClipboard(std::wstring_view content) noexcept = 0; + protected: ITerminalApi() = default; }; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 025d2abc7be..dd12d714db1 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -861,6 +861,11 @@ void Terminal::SetTitleChangedCallback(std::function pfn) noexcept +{ + _pfnCopyToClipboard.swap(pfn); +} + void Terminal::SetScrollPositionChangedCallback(std::function pfn) noexcept { _pfnScrollPositionChanged.swap(pfn); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 8655987ae33..72d8b0f2baf 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -111,6 +111,8 @@ class Microsoft::Terminal::Core::Terminal final : bool EnableAlternateScrollMode(const bool enabled) noexcept override; bool IsVtInputEnabled() const noexcept override; + + bool CopyToClipboard(std::wstring_view content) noexcept override; #pragma endregion #pragma region ITerminalInput @@ -169,6 +171,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetWriteInputCallback(std::function pfn) noexcept; void SetTitleChangedCallback(std::function pfn) noexcept; + void SetCopyToClipboardCallback(std::function pfn) noexcept; void SetScrollPositionChangedCallback(std::function pfn) noexcept; void SetCursorPositionChangedCallback(std::function pfn) noexcept; void SetBackgroundCallback(std::function pfn) noexcept; @@ -195,6 +198,7 @@ class Microsoft::Terminal::Core::Terminal final : private: std::function _pfnWriteInput; std::function _pfnTitleChanged; + std::function _pfnCopyToClipboard; std::function _pfnScrollPositionChanged; std::function _pfnBackgroundColorChanged; std::function _pfnCursorPositionChanged; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 4189e07b1d0..53500a1ba28 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -578,3 +578,12 @@ bool Terminal::EnableCursorBlinking(const bool enable) noexcept _buffer->GetCursor().SetIsOn(true); return true; } + +bool Terminal::CopyToClipboard(std::wstring_view content) noexcept +try +{ + _pfnCopyToClipboard(content); + + return true; +} +CATCH_LOG_RETURN_FALSE() diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index c209e810d25..70838b1185d 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -146,6 +146,13 @@ try } CATCH_LOG_RETURN_FALSE() +bool TerminalDispatch::CopyToClipboard(std::wstring_view content) noexcept +try +{ + return _terminalApi.CopyToClipboard(content); +} +CATCH_LOG_RETURN_FALSE() + // Method Description: // - Sets the default foreground color to a new value // Arguments: diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index e3be6ecbe92..611b6c4246d 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -34,6 +34,8 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override; bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override; + bool CopyToClipboard(std::wstring_view content) noexcept override; + bool SetDefaultForeground(const DWORD color) noexcept override; bool SetDefaultBackground(const DWORD color) noexcept override; bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; // ED diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 09b3af630bd..b46388a8a8f 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -484,3 +484,16 @@ bool VtIo::IsResizeQuirkEnabled() const } return S_OK; } + +// Method Description: +// - Process operating system control sequence. +// Arguments: +// - parameter: identifier of the OSC action to perform. +// - string: OSC string. +// Return Value: +// - S_OK if we successfully processed OSC or did nothing, else an +// appropriate HRESULT +[[nodiscard]] HRESULT VtIo::ProcessOsc(const size_t parameter, const std::wstring_view string) +{ + return _pVtRenderEngine->ProcessOsc(parameter, string); +} diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index 3ee6654d814..767f1899db3 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -33,6 +33,8 @@ namespace Microsoft::Console::VirtualTerminal [[nodiscard]] HRESULT SuppressResizeRepaint(); [[nodiscard]] HRESULT SetCursorPosition(const COORD coordCursor); + [[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string); + void CloseInput() override; void CloseOutput() override; diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index d3b40ffd120..1fabf7365f5 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -15,6 +15,7 @@ using namespace Microsoft::Console; using Microsoft::Console::Interactivity::ServiceLocator; +using Microsoft::Console::Render::VtEngine; WriteBuffer::WriteBuffer(_In_ Microsoft::Console::IIoProvider& io) : _io{ io }, @@ -728,3 +729,20 @@ bool ConhostInternalGetSet::PrivateIsVtInputEnabled() const { return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } + +// Routine Description: +// - Process copy-to-clipboard OSC. +// Arguments: +// - content - The content to copy to clipboard. +// Return Value: +// - true if not in VT mode or OSC was processed successfully, false otherwise. +bool ConhostInternalGetSet::CopyToClipboard(std::wstring_view content) +{ + HRESULT hr = S_OK; + CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + if (gci.IsInVtIoMode()) + { + hr = gci.GetVtIo()->ProcessOsc(VtEngine::OscActionCodes::CopyToClipboard, content); + } + return SUCCEEDED(hr); +} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index c39047bbdf7..05e8f0b4b03 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -142,6 +142,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool PrivateIsVtInputEnabled() const override; + bool CopyToClipboard(const std::wstring_view content) override; + private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/renderer/vt/VtSequences.cpp b/src/renderer/vt/VtSequences.cpp index 0b9c7baaf84..06dc0e90a0a 100644 --- a/src/renderer/vt/VtSequences.cpp +++ b/src/renderer/vt/VtSequences.cpp @@ -4,6 +4,7 @@ #include "precomp.h" #include "vtrenderer.hpp" #include "../../inc/conattrs.hpp" +#include "../../types/inc/convert.hpp" #pragma hdrstop using namespace Microsoft::Console::Render; @@ -436,3 +437,32 @@ using namespace Microsoft::Console::Render; { return _Write("\x1b[29m"); } + +// Method Description: +// - Writes operating system control sequence +// Arguments: +// - parameter: identifier of the OSC action to perform +// - string: OSC string +// Return Value: +// - S_OK if we succeeded or OSC code was ignored, else an appropriate HRESULT for failing to allocate or write. +[[nodiscard]] HRESULT VtEngine::ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept +{ + HRESULT hr = S_OK; + std::string contentFormat; + + switch (parameter) + { + case OscActionCodes::CopyToClipboard: + contentFormat = "\x1b]" + std::to_string(parameter) + ";" + ConvertToA(CP_UTF8, string) + "\x07"; + break; + default: + break; + } + + if (!contentFormat.empty()) + { + hr = _Write(contentFormat); + } + + return hr; +} diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 5f683bc9b15..46852bb9c57 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -102,6 +102,8 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0; + [[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept; + void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner); void BeginResizeRequest(); void EndResizeRequest(); @@ -110,6 +112,11 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept; + enum OscActionCodes : unsigned int + { + CopyToClipboard = 52, + }; + protected: wil::unique_hfile _hFile; std::string _buffer; diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index e627854ad36..01a30c2c069 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -102,6 +102,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor + virtual bool CopyToClipboard(std::wstring_view content) = 0; // OscCopyToClipboard + // DTTERM_WindowManipulation virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function, const std::basic_string_view parameters) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 65a6fdcf453..c462c7e3378 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2070,6 +2070,17 @@ bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor) return _pConApi->SetCursorColor(cursorColor); } +// Routine Description: +// - OSC Copy to Clipboard +// Arguments: +// - content - The content to copy to clipboard. Must be null terminated. +// Return Value: +// - True if handled successfully. False otherwise. +bool AdaptDispatch::CopyToClipboard(const std::wstring_view content) +{ + return _pConApi->CopyToClipboard(content); +} + // Method Description: // - Sets a single entry of the colortable to a new value // Arguments: diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 8bdd40b72ee..fe94b743f4f 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -98,6 +98,8 @@ namespace Microsoft::Console::VirtualTerminal bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; + bool CopyToClipboard(const std::wstring_view content) override; // OscCopyToClipboard + bool SetColorTableEntry(const size_t tableIndex, const DWORD color) override; // OscColorTable bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 36368c67448..8599f790f84 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -99,5 +99,7 @@ namespace Microsoft::Console::VirtualTerminal const std::optional clipRect, const COORD destinationOrigin, const bool standardFillAttrs) = 0; + + virtual bool CopyToClipboard(const std::wstring_view content) = 0; }; } diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index f3b1e1ccbda..c71b21b4fe7 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -96,6 +96,8 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) noexcept override { return false; } // DECSCUSR bool SetCursorColor(const COLORREF /*color*/) noexcept override { return false; } // OSCSetCursorColor, OSCResetCursorColor + bool CopyToClipboard(std::wstring_view /*content*/) noexcept override { return false; } // OscCopyToClipboard + // DTTERM_WindowManipulation bool WindowManipulation(const DispatchTypes::WindowManipulationType /*function*/, const std::basic_string_view /*params*/) noexcept override { return false; } diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index fbcbe3e6e47..a615eaa0e21 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -690,6 +690,11 @@ class TestGetSet final : public ConGetSet _expectedScrollRegion.Bottom = (bottom > 0) ? rect->Bottom - 1 : rect->Bottom; } + bool CopyToClipboard(const std::wstring_view /*content*/) + { + return true; + } + ~TestGetSet() { } diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp old mode 100644 new mode 100755 index 74a957c6bb3..879e49e1a16 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -726,6 +726,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, { bool success = false; std::wstring title; + std::wstring copyContent; size_t tableIndex = 0; DWORD color = 0; @@ -734,7 +735,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, case OscActionCodes::SetIconAndWindowTitle: case OscActionCodes::SetWindowIcon: case OscActionCodes::SetWindowTitle: - success = _GetOscTitle(string, title); + success = _GetOscString(string, title); break; case OscActionCodes::SetColor: success = _GetOscSetColorTable(string, tableIndex, color); @@ -744,6 +745,9 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, case OscActionCodes::SetCursorColor: success = _GetOscSetColor(string, color); break; + case OscActionCodes::CopyToClipboard: + success = _GetOscString(string, copyContent); + break; case OscActionCodes::ResetCursorColor: // the console uses 0xffffffff as an "invalid color" value color = 0xffffffff; @@ -780,6 +784,10 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, success = _dispatch->SetCursorColor(color); TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC); break; + case OscActionCodes::CopyToClipboard: + success = _dispatch->CopyToClipboard(copyContent); + TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC); + break; case OscActionCodes::ResetCursorColor: success = _dispatch->SetCursorColor(color); TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCRCC); @@ -1189,16 +1197,16 @@ bool OutputStateMachineEngine::_VerifyDeviceAttributesParams(const std::basic_st // Routine Description: // - Null terminates, then returns, the string that we've collected as part of the OSC string. // Arguments: -// - string - Osc String input -// - title - Where to place the Osc String to use as a title. +// - inString - Osc String input. +// - outString - Where to place the Osc String. // Return Value: -// - True if there was a title to output. (a title with length=0 is still valid) -bool OutputStateMachineEngine::_GetOscTitle(const std::wstring_view string, - std::wstring& title) const +// - True if there was a string to output. +bool OutputStateMachineEngine::_GetOscString(const std::wstring_view inString, + std::wstring& outString) const { - title = string; + outString = inString; - return !string.empty(); + return !inString.empty(); } // Routine Description: diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp old mode 100644 new mode 100755 index 23ae3922d55..6485b3463e7 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -136,6 +136,7 @@ namespace Microsoft::Console::VirtualTerminal SetForegroundColor = 10, SetBackgroundColor = 11, SetCursorColor = 12, + CopyToClipboard = 52, ResetForegroundColor = 110, // Not implemented ResetBackgroundColor = 111, // Not implemented ResetCursorColor = 112, @@ -191,8 +192,8 @@ namespace Microsoft::Console::VirtualTerminal size_t& topMargin, size_t& bottomMargin) const noexcept; - bool _GetOscTitle(const std::wstring_view string, - std::wstring& title) const; + bool _GetOscString(const std::wstring_view inString, + std::wstring& outString) const; static constexpr size_t DefaultTabDistance = 1; bool _GetTabDistance(const std::basic_string_view parameters, diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 4e360e8cd40..8f5eab09ca4 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -254,7 +254,7 @@ static constexpr bool _isOscTerminationInitiator(const wchar_t wch) noexcept // - True if it is. False if it isn't. static constexpr bool _isOscInvalid(const wchar_t wch) noexcept { - return wch <= L'\x17' || + return (wch <= L'\x17' && wch != L'\x0a' && wch != L'\x0d') || wch == L'\x19' || (wch >= L'\x1c' && wch <= L'\x1f'); } diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 6884a943223..9fbc09b3904 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -451,6 +451,43 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final mach.ProcessCharacter(AsciiChars::BEL); VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); } + TEST_METHOD(TestOscStringMultiLines) + { + auto dispatch = std::make_unique(); + auto engine = std::make_unique(std::move(dispatch)); + StateMachine mach(std::move(engine)); + + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); + mach.ProcessCharacter(AsciiChars::ESC); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape); + mach.ProcessCharacter(L']'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); + mach.ProcessCharacter(L'5'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); + mach.ProcessCharacter(L'2'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); + mach.ProcessCharacter(L';'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'f'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'o'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'o'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'\r'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'\n'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'b'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'a'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + mach.ProcessCharacter(L'r'); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); + VERIFY_ARE_EQUAL(mach._oscString.size(), 8u); + mach.ProcessCharacter(AsciiChars::BEL); + VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); + } TEST_METHOD(NormalTestOscParam) { @@ -990,6 +1027,12 @@ class StatefulDispatch final : public TermDispatch return true; } + bool CopyToClipboard(std::wstring_view content) noexcept override + { + _copyContent = { content.begin(), content.end() }; + return true; + } + size_t _cursorDistance; size_t _line; size_t _column; @@ -1030,6 +1073,7 @@ class StatefulDispatch final : public TermDispatch size_t _numTabs; bool _isDECCOLMAllowed; size_t _windowWidth; + std::wstring _copyContent; static const size_t s_cMaxOptions = 16; static const size_t s_uiGraphicsCleared = UINT_MAX; @@ -2011,4 +2055,27 @@ class StateMachineExternalTest final pDispatch->ClearState(); } + + TEST_METHOD(TestCopyToClipboard) + { + auto dispatch = std::make_unique(); + auto pDispatch = dispatch.get(); + auto engine = std::make_unique(std::move(dispatch)); + StateMachine mach(std::move(engine)); + + mach.ProcessString(L"\x1b]52;foo\x07"); + VERIFY_ARE_EQUAL(L"foo", pDispatch->_copyContent); + + pDispatch->ClearState(); + + mach.ProcessString(L"\x1b]52;foo\r\nbar\x07"); + VERIFY_ARE_EQUAL(L"foo\r\nbar", pDispatch->_copyContent); + + pDispatch->ClearState(); + + mach.ProcessString(L"\x1b]52;foo\r\nbar\r\n\x07"); + VERIFY_ARE_EQUAL(L"foo\r\nbar\r\n", pDispatch->_copyContent); + + pDispatch->ClearState(); + } }; From 946ac2725b75668780ca86d670a4cfc0cb1c18dc Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Wed, 13 May 2020 13:45:45 +0800 Subject: [PATCH 02/13] Base64-decode OSC 52 parameter Signed-off-by: Mingxiang Xue --- .../TerminalCore/TerminalDispatch.cpp | 2 +- .../TerminalCore/TerminalDispatch.hpp | 2 +- src/host/VtIo.cpp | 13 --- src/host/VtIo.hpp | 2 - src/host/outputStream.cpp | 18 ---- src/host/outputStream.hpp | 2 - src/renderer/vt/VtSequences.cpp | 30 ------- src/renderer/vt/vtrenderer.hpp | 7 -- src/terminal/adapter/ITermDispatch.hpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 4 +- src/terminal/adapter/adaptDispatch.hpp | 2 +- src/terminal/adapter/conGetSet.hpp | 2 - src/terminal/adapter/termDispatch.hpp | 2 +- .../adapter/ut_adapter/adapterTest.cpp | 5 -- .../parser/OutputStateMachineEngine.cpp | 76 ++++++++++++++--- .../parser/OutputStateMachineEngine.hpp | 10 ++- src/terminal/parser/stateMachine.cpp | 2 +- .../parser/ut_parser/OutputEngineTest.cpp | 85 +++++++++---------- 18 files changed, 120 insertions(+), 146 deletions(-) diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index 70838b1185d..7c0f529f625 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -146,7 +146,7 @@ try } CATCH_LOG_RETURN_FALSE() -bool TerminalDispatch::CopyToClipboard(std::wstring_view content) noexcept +bool TerminalDispatch::SetClipboard(std::wstring_view content) noexcept try { return _terminalApi.CopyToClipboard(content); diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 611b6c4246d..80983531255 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -34,7 +34,7 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept override; bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override; - bool CopyToClipboard(std::wstring_view content) noexcept override; + bool SetClipboard(std::wstring_view content) noexcept override; bool SetDefaultForeground(const DWORD color) noexcept override; bool SetDefaultBackground(const DWORD color) noexcept override; diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index b46388a8a8f..09b3af630bd 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -484,16 +484,3 @@ bool VtIo::IsResizeQuirkEnabled() const } return S_OK; } - -// Method Description: -// - Process operating system control sequence. -// Arguments: -// - parameter: identifier of the OSC action to perform. -// - string: OSC string. -// Return Value: -// - S_OK if we successfully processed OSC or did nothing, else an -// appropriate HRESULT -[[nodiscard]] HRESULT VtIo::ProcessOsc(const size_t parameter, const std::wstring_view string) -{ - return _pVtRenderEngine->ProcessOsc(parameter, string); -} diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index 767f1899db3..3ee6654d814 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -33,8 +33,6 @@ namespace Microsoft::Console::VirtualTerminal [[nodiscard]] HRESULT SuppressResizeRepaint(); [[nodiscard]] HRESULT SetCursorPosition(const COORD coordCursor); - [[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string); - void CloseInput() override; void CloseOutput() override; diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 1fabf7365f5..d3b40ffd120 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -15,7 +15,6 @@ using namespace Microsoft::Console; using Microsoft::Console::Interactivity::ServiceLocator; -using Microsoft::Console::Render::VtEngine; WriteBuffer::WriteBuffer(_In_ Microsoft::Console::IIoProvider& io) : _io{ io }, @@ -729,20 +728,3 @@ bool ConhostInternalGetSet::PrivateIsVtInputEnabled() const { return _io.GetActiveInputBuffer()->IsInVirtualTerminalInputMode(); } - -// Routine Description: -// - Process copy-to-clipboard OSC. -// Arguments: -// - content - The content to copy to clipboard. -// Return Value: -// - true if not in VT mode or OSC was processed successfully, false otherwise. -bool ConhostInternalGetSet::CopyToClipboard(std::wstring_view content) -{ - HRESULT hr = S_OK; - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - if (gci.IsInVtIoMode()) - { - hr = gci.GetVtIo()->ProcessOsc(VtEngine::OscActionCodes::CopyToClipboard, content); - } - return SUCCEEDED(hr); -} diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 05e8f0b4b03..c39047bbdf7 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -142,8 +142,6 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: bool PrivateIsVtInputEnabled() const override; - bool CopyToClipboard(const std::wstring_view content) override; - private: Microsoft::Console::IIoProvider& _io; }; diff --git a/src/renderer/vt/VtSequences.cpp b/src/renderer/vt/VtSequences.cpp index 06dc0e90a0a..0b9c7baaf84 100644 --- a/src/renderer/vt/VtSequences.cpp +++ b/src/renderer/vt/VtSequences.cpp @@ -4,7 +4,6 @@ #include "precomp.h" #include "vtrenderer.hpp" #include "../../inc/conattrs.hpp" -#include "../../types/inc/convert.hpp" #pragma hdrstop using namespace Microsoft::Console::Render; @@ -437,32 +436,3 @@ using namespace Microsoft::Console::Render; { return _Write("\x1b[29m"); } - -// Method Description: -// - Writes operating system control sequence -// Arguments: -// - parameter: identifier of the OSC action to perform -// - string: OSC string -// Return Value: -// - S_OK if we succeeded or OSC code was ignored, else an appropriate HRESULT for failing to allocate or write. -[[nodiscard]] HRESULT VtEngine::ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept -{ - HRESULT hr = S_OK; - std::string contentFormat; - - switch (parameter) - { - case OscActionCodes::CopyToClipboard: - contentFormat = "\x1b]" + std::to_string(parameter) + ";" + ConvertToA(CP_UTF8, string) + "\x07"; - break; - default: - break; - } - - if (!contentFormat.empty()) - { - hr = _Write(contentFormat); - } - - return hr; -} diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 46852bb9c57..5f683bc9b15 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -102,8 +102,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0; - [[nodiscard]] HRESULT ProcessOsc(const size_t parameter, const std::wstring_view string) noexcept; - void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner); void BeginResizeRequest(); void EndResizeRequest(); @@ -112,11 +110,6 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept; - enum OscActionCodes : unsigned int - { - CopyToClipboard = 52, - }; - protected: wil::unique_hfile _hFile; std::string _buffer; diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 01a30c2c069..5e5e3f93915 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -102,7 +102,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor - virtual bool CopyToClipboard(std::wstring_view content) = 0; // OscCopyToClipboard + virtual bool SetClipboard(std::wstring_view content) = 0; // OscSetClipboard // DTTERM_WindowManipulation virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function, diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index c462c7e3378..6256d531af2 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2076,9 +2076,9 @@ bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor) // - content - The content to copy to clipboard. Must be null terminated. // Return Value: // - True if handled successfully. False otherwise. -bool AdaptDispatch::CopyToClipboard(const std::wstring_view content) +bool AdaptDispatch::SetClipboard(const std::wstring_view /*content*/) { - return _pConApi->CopyToClipboard(content); + return false; } // Method Description: diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index fe94b743f4f..de46d262b28 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -98,7 +98,7 @@ namespace Microsoft::Console::VirtualTerminal bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; - bool CopyToClipboard(const std::wstring_view content) override; // OscCopyToClipboard + bool SetClipboard(const std::wstring_view content) override; // OscSetClipboard bool SetColorTableEntry(const size_t tableIndex, const DWORD color) override; // OscColorTable diff --git a/src/terminal/adapter/conGetSet.hpp b/src/terminal/adapter/conGetSet.hpp index 8599f790f84..36368c67448 100644 --- a/src/terminal/adapter/conGetSet.hpp +++ b/src/terminal/adapter/conGetSet.hpp @@ -99,7 +99,5 @@ namespace Microsoft::Console::VirtualTerminal const std::optional clipRect, const COORD destinationOrigin, const bool standardFillAttrs) = 0; - - virtual bool CopyToClipboard(const std::wstring_view content) = 0; }; } diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index c71b21b4fe7..aef6d384210 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -96,7 +96,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool SetCursorStyle(const DispatchTypes::CursorStyle /*cursorStyle*/) noexcept override { return false; } // DECSCUSR bool SetCursorColor(const COLORREF /*color*/) noexcept override { return false; } // OSCSetCursorColor, OSCResetCursorColor - bool CopyToClipboard(std::wstring_view /*content*/) noexcept override { return false; } // OscCopyToClipboard + bool SetClipboard(std::wstring_view /*content*/) noexcept override { return false; } // OscSetClipboard // DTTERM_WindowManipulation bool WindowManipulation(const DispatchTypes::WindowManipulationType /*function*/, diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index a615eaa0e21..fbcbe3e6e47 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -690,11 +690,6 @@ class TestGetSet final : public ConGetSet _expectedScrollRegion.Bottom = (bottom > 0) ? rect->Bottom - 1 : rect->Bottom; } - bool CopyToClipboard(const std::wstring_view /*content*/) - { - return true; - } - ~TestGetSet() { } diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 879e49e1a16..0f199caee9d 100755 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -3,12 +3,17 @@ #include "precomp.h" +#include +#include + #include "stateMachine.hpp" #include "OutputStateMachineEngine.hpp" #include "ascii.hpp" using namespace Microsoft::Console; using namespace Microsoft::Console::VirtualTerminal; +using namespace winrt::Windows::Security::Cryptography; +using namespace winrt::Windows::Storage::Streams; // takes ownership of pDispatch OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr pDispatch) : @@ -726,7 +731,8 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, { bool success = false; std::wstring title; - std::wstring copyContent; + std::wstring setClipboardContent; + bool queryClipboard = false; size_t tableIndex = 0; DWORD color = 0; @@ -735,7 +741,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, case OscActionCodes::SetIconAndWindowTitle: case OscActionCodes::SetWindowIcon: case OscActionCodes::SetWindowTitle: - success = _GetOscString(string, title); + success = _GetOscTitle(string, title); break; case OscActionCodes::SetColor: success = _GetOscSetColorTable(string, tableIndex, color); @@ -745,8 +751,8 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, case OscActionCodes::SetCursorColor: success = _GetOscSetColor(string, color); break; - case OscActionCodes::CopyToClipboard: - success = _GetOscString(string, copyContent); + case OscActionCodes::SetClipboard: + success = _GetOscSetClipboard(string, setClipboardContent, queryClipboard); break; case OscActionCodes::ResetCursorColor: // the console uses 0xffffffff as an "invalid color" value @@ -784,8 +790,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, success = _dispatch->SetCursorColor(color); TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC); break; - case OscActionCodes::CopyToClipboard: - success = _dispatch->CopyToClipboard(copyContent); + case OscActionCodes::SetClipboard: + if (!queryClipboard) + { + success = _dispatch->SetClipboard(setClipboardContent); + } TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC); break; case OscActionCodes::ResetCursorColor: @@ -1197,16 +1206,16 @@ bool OutputStateMachineEngine::_VerifyDeviceAttributesParams(const std::basic_st // Routine Description: // - Null terminates, then returns, the string that we've collected as part of the OSC string. // Arguments: -// - inString - Osc String input. -// - outString - Where to place the Osc String. +// - string - Osc String input +// - title - Where to place the Osc String to use as a title. // Return Value: -// - True if there was a string to output. -bool OutputStateMachineEngine::_GetOscString(const std::wstring_view inString, - std::wstring& outString) const +// - True if there was a title to output. (a title with length=0 is still valid) +bool OutputStateMachineEngine::_GetOscTitle(const std::wstring_view string, + std::wstring& title) const { - outString = inString; + title = string; - return !inString.empty(); + return !string.empty(); } // Routine Description: @@ -1739,6 +1748,47 @@ bool OutputStateMachineEngine::_GetRepeatCount(std::basic_string_view pa return success; } +// Routine Description: +// - Parse OscSetClipboard parameters with the format `Pc;Pd`. Currently the first parameter `Pc` is +// ignored. The second parameter `Pd` should be a valid base64 string or character `?`. +// Arguments: +// - string - Osc String input. +// - content - Content to set to clipboard. +// - queryClipboard - Whether to get clipboard content and return it to terminal with base64 encoded. +// Return Value: +// - True if there was a valid base64 string or the passed parameter was `?`. +bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view string, + std::wstring& content, + bool& queryClipboard) const noexcept +{ + bool success = false; + size_t pos = string.find(';'); + if (pos != std::wstring_view::npos) + { + std::wstring_view substr = string.substr(pos + 1); + if (substr == L"?") + { + queryClipboard = true; + success = true; + } + else + { + try + { + auto buffer = CryptographicBuffer::DecodeFromBase64String(winrt::hstring(substr)); + auto reader = DataReader::FromBuffer(buffer); + content = reader.ReadString(buffer.Length()); + success = true; + } + catch (...) + { + } + } + } + + return success; +} + // Method Description: // - Clears our last stored character. The last stored character is the last // graphical character we printed, which is reset if any other action is diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 6485b3463e7..ce320bbf028 100755 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -136,7 +136,7 @@ namespace Microsoft::Console::VirtualTerminal SetForegroundColor = 10, SetBackgroundColor = 11, SetCursorColor = 12, - CopyToClipboard = 52, + SetClipboard = 52, ResetForegroundColor = 110, // Not implemented ResetBackgroundColor = 111, // Not implemented ResetCursorColor = 112, @@ -192,8 +192,8 @@ namespace Microsoft::Console::VirtualTerminal size_t& topMargin, size_t& bottomMargin) const noexcept; - bool _GetOscString(const std::wstring_view inString, - std::wstring& outString) const; + bool _GetOscTitle(const std::wstring_view string, + std::wstring& title) const; static constexpr size_t DefaultTabDistance = 1; bool _GetTabDistance(const std::basic_string_view parameters, @@ -231,6 +231,10 @@ namespace Microsoft::Console::VirtualTerminal bool _GetRepeatCount(const std::basic_string_view parameters, size_t& repeatCount) const noexcept; + bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view string, + std::wstring& title, + bool& queryClipboard) const noexcept; + void _ClearLastChar() noexcept; }; } diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 8f5eab09ca4..4e360e8cd40 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -254,7 +254,7 @@ static constexpr bool _isOscTerminationInitiator(const wchar_t wch) noexcept // - True if it is. False if it isn't. static constexpr bool _isOscInvalid(const wchar_t wch) noexcept { - return (wch <= L'\x17' && wch != L'\x0a' && wch != L'\x0d') || + return wch <= L'\x17' || wch == L'\x19' || (wch >= L'\x1c' && wch <= L'\x1f'); } diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 9fbc09b3904..a435a81c0a8 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -451,43 +451,6 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final mach.ProcessCharacter(AsciiChars::BEL); VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); } - TEST_METHOD(TestOscStringMultiLines) - { - auto dispatch = std::make_unique(); - auto engine = std::make_unique(std::move(dispatch)); - StateMachine mach(std::move(engine)); - - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); - mach.ProcessCharacter(AsciiChars::ESC); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape); - mach.ProcessCharacter(L']'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); - mach.ProcessCharacter(L'5'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); - mach.ProcessCharacter(L'2'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam); - mach.ProcessCharacter(L';'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'f'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'o'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'o'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'\r'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'\n'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'b'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'a'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - mach.ProcessCharacter(L'r'); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString); - VERIFY_ARE_EQUAL(mach._oscString.size(), 8u); - mach.ProcessCharacter(AsciiChars::BEL); - VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground); - } TEST_METHOD(NormalTestOscParam) { @@ -1027,7 +990,7 @@ class StatefulDispatch final : public TermDispatch return true; } - bool CopyToClipboard(std::wstring_view content) noexcept override + bool SetClipboard(std::wstring_view content) noexcept override { _copyContent = { content.begin(), content.end() }; return true; @@ -2056,25 +2019,61 @@ class StateMachineExternalTest final pDispatch->ClearState(); } - TEST_METHOD(TestCopyToClipboard) + TEST_METHOD(TestSetClipboard) { auto dispatch = std::make_unique(); auto pDispatch = dispatch.get(); auto engine = std::make_unique(std::move(dispatch)); StateMachine mach(std::move(engine)); - mach.ProcessString(L"\x1b]52;foo\x07"); + mach.ProcessString(L"\x1b]52;;Zm9v\x07"); VERIFY_ARE_EQUAL(L"foo", pDispatch->_copyContent); pDispatch->ClearState(); - mach.ProcessString(L"\x1b]52;foo\r\nbar\x07"); + mach.ProcessString(L"\x1b]52;;Zm9vDQpiYXI=\x07"); VERIFY_ARE_EQUAL(L"foo\r\nbar", pDispatch->_copyContent); pDispatch->ClearState(); - mach.ProcessString(L"\x1b]52;foo\r\nbar\r\n\x07"); - VERIFY_ARE_EQUAL(L"foo\r\nbar\r\n", pDispatch->_copyContent); + mach.ProcessString(L"\x1b]52;s0;Zm9v\x07"); + VERIFY_ARE_EQUAL(L"foo", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;Zm9v\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;;foo\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;;;Zm9v\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;;?\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;?\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); + + pDispatch->ClearState(); + + pDispatch->_copyContent = L"UNCHANGED"; + mach.ProcessString(L"\x1b]52;;;?\x07"); + VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); } From 0fa22cb805a279e2749cf90e0e0717c6c3dd64fe Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Fri, 15 May 2020 10:14:19 +0800 Subject: [PATCH 03/13] Fix files mode and lint errors Signed-off-by: Mingxiang Xue --- src/terminal/adapter/adaptDispatch.cpp | 2 +- src/terminal/adapter/adaptDispatch.hpp | 2 +- src/terminal/parser/OutputStateMachineEngine.cpp | 7 ++----- src/terminal/parser/OutputStateMachineEngine.hpp | 0 src/terminal/parser/precomp.h | 3 +++ 5 files changed, 7 insertions(+), 7 deletions(-) mode change 100755 => 100644 src/terminal/parser/OutputStateMachineEngine.cpp mode change 100755 => 100644 src/terminal/parser/OutputStateMachineEngine.hpp diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 6256d531af2..d5b812e8686 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -2076,7 +2076,7 @@ bool AdaptDispatch::SetCursorColor(const COLORREF cursorColor) // - content - The content to copy to clipboard. Must be null terminated. // Return Value: // - True if handled successfully. False otherwise. -bool AdaptDispatch::SetClipboard(const std::wstring_view /*content*/) +bool AdaptDispatch::SetClipboard(const std::wstring_view /*content*/) noexcept { return false; } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index de46d262b28..5ec62f8ca81 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -98,7 +98,7 @@ namespace Microsoft::Console::VirtualTerminal bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; - bool SetClipboard(const std::wstring_view content) override; // OscSetClipboard + bool SetClipboard(const std::wstring_view content) noexcept override; // OscSetClipboard bool SetColorTableEntry(const size_t tableIndex, const DWORD color) override; // OscColorTable diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp old mode 100755 new mode 100644 index 0f199caee9d..8f97f7c163f --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -3,9 +3,6 @@ #include "precomp.h" -#include -#include - #include "stateMachine.hpp" #include "OutputStateMachineEngine.hpp" @@ -1762,10 +1759,10 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin bool& queryClipboard) const noexcept { bool success = false; - size_t pos = string.find(';'); + const size_t pos = string.find(';'); if (pos != std::wstring_view::npos) { - std::wstring_view substr = string.substr(pos + 1); + const std::wstring_view substr = string.substr(pos + 1); if (substr == L"?") { queryClipboard = true; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp old mode 100755 new mode 100644 diff --git a/src/terminal/parser/precomp.h b/src/terminal/parser/precomp.h index 38a4422bc24..5206a70cba2 100644 --- a/src/terminal/parser/precomp.h +++ b/src/terminal/parser/precomp.h @@ -10,6 +10,9 @@ Module Name: - Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). */ +#include +#include + // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" From dcac59839d2880723a99a7a975da8f0aac160cae Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Sat, 16 May 2020 10:12:44 +0800 Subject: [PATCH 04/13] Suppress spellcheck for test string Signed-off-by: Mingxiang Xue --- .github/actions/spell-check/patterns/patterns.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/spell-check/patterns/patterns.txt b/.github/actions/spell-check/patterns/patterns.txt index 6f8607216b3..410250d55ee 100644 --- a/.github/actions/spell-check/patterns/patterns.txt +++ b/.github/actions/spell-check/patterns/patterns.txt @@ -9,5 +9,5 @@ Scro\&ll # selectionInput.cpp :\\windows\\syste\b TestUtils::VerifyExpectedString\(tb, L"[^"]+" -hostSm\.ProcessString\(L"[^"]+" +(?:hostSm|mach)\.ProcessString\(L"[^"]+" \b([A-Za-z])\1{3,}\b From 1e462be546151514fe3eb40867647f7f1e70fe71 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Thu, 28 May 2020 13:42:53 +0800 Subject: [PATCH 05/13] Add comments to explain test cases Signed-off-by: Mingxiang Xue --- src/terminal/parser/OutputStateMachineEngine.cpp | 4 +--- src/terminal/parser/precomp.h | 7 ++++--- src/terminal/parser/ut_parser/OutputEngineTest.cpp | 9 +++++++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 8f97f7c163f..51508530ad3 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -1777,9 +1777,7 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin content = reader.ReadString(buffer.Length()); success = true; } - catch (...) - { - } + CATCH_LOG(); } } diff --git a/src/terminal/parser/precomp.h b/src/terminal/parser/precomp.h index 5206a70cba2..d53d23f9856 100644 --- a/src/terminal/parser/precomp.h +++ b/src/terminal/parser/precomp.h @@ -10,9 +10,6 @@ Module Name: - Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). */ -#include -#include - // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" @@ -27,3 +24,7 @@ Module Name: #include "telemetry.hpp" #include "tracing.hpp" + +#include +#include +#include diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index a435a81c0a8..a3238d2a8c7 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -2026,52 +2026,61 @@ class StateMachineExternalTest final auto engine = std::make_unique(std::move(dispatch)); StateMachine mach(std::move(engine)); + // Passing an empty `Pc` param and a base64-encoded simple text `Pd` param works. mach.ProcessString(L"\x1b]52;;Zm9v\x07"); VERIFY_ARE_EQUAL(L"foo", pDispatch->_copyContent); pDispatch->ClearState(); + // Passing an empty `Pc` param and a base64-encoded multi-lines text `Pd` works. mach.ProcessString(L"\x1b]52;;Zm9vDQpiYXI=\x07"); VERIFY_ARE_EQUAL(L"foo\r\nbar", pDispatch->_copyContent); pDispatch->ClearState(); + // Passing a non-empty `Pc` param (`s0` is ignored) and a valid `Pd` param works. mach.ProcessString(L"\x1b]52;s0;Zm9v\x07"); VERIFY_ARE_EQUAL(L"foo", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing only base64 `Pd` param is illegal, won't change the content. mach.ProcessString(L"\x1b]52;Zm9v\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing a non-base64 `Pd` param is illegal, won't change the content. mach.ProcessString(L"\x1b]52;;foo\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing a valid `Pc;Pd` with one more extra param is illegal, won't change the content. mach.ProcessString(L"\x1b]52;;;Zm9v\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing a query character won't change the content. mach.ProcessString(L"\x1b]52;;?\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing a query character with missing `Pc` param is illegal, won't change the content. mach.ProcessString(L"\x1b]52;?\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); pDispatch->ClearState(); pDispatch->_copyContent = L"UNCHANGED"; + // Passing a query character with one more extra param is illegal, won't change the content. mach.ProcessString(L"\x1b]52;;;?\x07"); VERIFY_ARE_EQUAL(L"UNCHANGED", pDispatch->_copyContent); From 559f7fc88003017ef0c275ac7e569c6606119d53 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Sat, 30 May 2020 11:12:19 +0800 Subject: [PATCH 06/13] Remove unexpected class prefix Signed-off-by: Mingxiang Xue --- src/terminal/parser/OutputStateMachineEngine.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index ce320bbf028..64e536dd4a7 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -231,9 +231,9 @@ namespace Microsoft::Console::VirtualTerminal bool _GetRepeatCount(const std::basic_string_view parameters, size_t& repeatCount) const noexcept; - bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view string, - std::wstring& title, - bool& queryClipboard) const noexcept; + bool _GetOscSetClipboard(const std::wstring_view string, + std::wstring& title, + bool& queryClipboard) const noexcept; void _ClearLastChar() noexcept; }; From 644ba349fe153946dd6950f4fdc994739f4a2093 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Sat, 20 Jun 2020 22:30:14 +0800 Subject: [PATCH 07/13] Add Base64 class Signed-off-by: Mingxiang Xue --- .../parser/OutputStateMachineEngine.cpp | 18 +- src/terminal/parser/base64.cpp | 170 ++++++++++++++++++ src/terminal/parser/base64.hpp | 25 +++ src/terminal/parser/parser-common.vcxitems | 2 + src/terminal/parser/precomp.h | 4 - src/terminal/parser/ut_parser/Base64Test.cpp | 88 +++++++++ .../parser/ut_parser/Parser.UnitTests.vcxproj | 1 + .../Parser.UnitTests.vcxproj.filters | 5 +- src/terminal/parser/ut_parser/sources | 1 + 9 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 src/terminal/parser/base64.cpp create mode 100644 src/terminal/parser/base64.hpp create mode 100644 src/terminal/parser/ut_parser/Base64Test.cpp diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 51508530ad3..4a6f4176b06 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -5,12 +5,12 @@ #include "stateMachine.hpp" #include "OutputStateMachineEngine.hpp" +#include "base64.hpp" #include "ascii.hpp" + using namespace Microsoft::Console; using namespace Microsoft::Console::VirtualTerminal; -using namespace winrt::Windows::Security::Cryptography; -using namespace winrt::Windows::Storage::Streams; // takes ownership of pDispatch OutputStateMachineEngine::OutputStateMachineEngine(std::unique_ptr pDispatch) : @@ -1758,7 +1758,6 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin std::wstring& content, bool& queryClipboard) const noexcept { - bool success = false; const size_t pos = string.find(';'); if (pos != std::wstring_view::npos) { @@ -1766,22 +1765,15 @@ bool OutputStateMachineEngine::_GetOscSetClipboard(const std::wstring_view strin if (substr == L"?") { queryClipboard = true; - success = true; + return true; } else { - try - { - auto buffer = CryptographicBuffer::DecodeFromBase64String(winrt::hstring(substr)); - auto reader = DataReader::FromBuffer(buffer); - content = reader.ReadString(buffer.Length()); - success = true; - } - CATCH_LOG(); + return Base64::s_Decode(substr, content); } } - return success; + return false; } // Method Description: diff --git a/src/terminal/parser/base64.cpp b/src/terminal/parser/base64.cpp new file mode 100644 index 00000000000..fdcaac9dcad --- /dev/null +++ b/src/terminal/parser/base64.cpp @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "base64.hpp" + +using namespace Microsoft::Console::VirtualTerminal; + +static const wchar_t base64Chars[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const wchar_t padChar = L'='; + +// Routine Description: +// - Encode a string using base64. When there are not enough characters +// for one quantum, paddings are added. +// Arguments: +// - src - String to base64 encode. +// Return Value: +// - the encoded string. +std::wstring Base64::s_Encode(const std::wstring_view src) +{ + std::wstring dst; + wchar_t input[3]; + + auto iter = src.cbegin(); + // Encode each three chars into one quantum (four chars). + while (iter < src.cend() - 2) + { + input[0] = *iter++; + input[1] = *iter++; + input[2] = *iter++; + dst.push_back(base64Chars[input[0] >> 2]); + dst.push_back(base64Chars[(input[0] & 0x03) << 4 | input[1] >> 4]); + dst.push_back(base64Chars[(input[1] & 0x0f) << 2 | input[2] >> 6]); + dst.push_back(base64Chars[(input[2] & 0x3f)]); + } + + // Here only zero, or one, or two chars are left. We may need to add paddings. + if (iter < src.cend()) + { + input[0] = *iter++; + dst.push_back(base64Chars[input[0] >> 2]); + if (iter < src.cend()) // Two chars left. + { + input[1] = *iter++; + dst.push_back(base64Chars[(input[0] & 0x03) << 4 | input[1] >> 4]); + dst.push_back(base64Chars[(input[1] & 0x0f) << 2]); + } + else // Only one char left. + { + dst.push_back(base64Chars[(input[0] & 0x03) << 4]); + dst.push_back(padChar); + } + dst.push_back(padChar); + } + + return dst; +} + +// Routine Description: +// - Decode a base64 string. This requires the base64 string is properly padded. +// Otherwise, false will be returned. +// Arguments: +// - src - String to decode. +// - dst - Destination to decode into. +// Return Value: +// - true if decoding successfully, otherwise false. +bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) +{ + int state = 0; + wchar_t tmp; + + auto iter = src.cbegin(); + while (iter < src.cend()) + { + if (s_IsSpace(*iter)) // Skip whitespace anywhere. + { + iter++; + continue; + } + + if (*iter == padChar) + { + break; + } + + auto pos = wcschr(base64Chars, *iter); + if (!pos) // A non-base64 character found. + { + return false; + } + + switch (state) + { + case 0: + tmp = (wchar_t)(pos - base64Chars) << 2; + state = 1; + break; + case 1: + tmp |= (pos - base64Chars) >> 4; + dst.push_back(tmp); + tmp = (wchar_t)((pos - base64Chars) & 0x0f) << 4; + state = 2; + break; + case 2: + tmp |= (pos - base64Chars) >> 2; + dst.push_back(tmp); + tmp = (wchar_t)((pos - base64Chars) & 0x03) << 6; + state = 3; + break; + case 3: + tmp |= pos - base64Chars; + dst.push_back(tmp); + state = 0; + break; + } + + iter++; + } + + if (iter < src.cend()) // Padding char is met. + { + iter++; + switch (state) + { + // Invalid when state is 0 or 1. + case 0: + case 1: + return false; + case 2: + // Skip any number of spaces. + while (iter < src.cend() && s_IsSpace(*iter)) + { + iter++; + } + // Make sure there is another trailing padding character. + if (iter == src.cend() || *iter != padChar) + { + return false; + } + iter++; // Skip the padding character and fallthrough to "single trailing padding character" case. + case 3: + while (iter < src.cend()) + { + if (!s_IsSpace(*iter)) + { + return false; + } + iter++; + } + } + } + else if (state != 0) // When no padding, we must be in state 0. + { + return false; + } + + return true; +} + +// Routine Description: +// - Check if parameter is a base64 whitespace. Only carriage return or line feed +// is valid whitespace. +// Arguments: +// - ch - Character to check. +// Return Value: +// - true iff ch is a carriage return or line feed. +bool Base64::s_IsSpace(const wchar_t ch) +{ + return ch == L'\r' || ch == L'\n'; +} diff --git a/src/terminal/parser/base64.hpp b/src/terminal/parser/base64.hpp new file mode 100644 index 00000000000..499ca3d5340 --- /dev/null +++ b/src/terminal/parser/base64.hpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/* +Module Name: +- base64.hpp + +Abstract: +- This declares standard base64 encoding and decoding, with paddings when needed. +*/ + +#pragma once + +namespace Microsoft::Console::VirtualTerminal +{ + class Base64 + { + public: + static std::wstring s_Encode(const std::wstring_view src); + static bool s_Decode(const std::wstring_view src, std::wstring& dst); + + private: + static bool s_IsSpace(const wchar_t ch); + }; +} diff --git a/src/terminal/parser/parser-common.vcxitems b/src/terminal/parser/parser-common.vcxitems index 35dc08e3368..75656dcb462 100644 --- a/src/terminal/parser/parser-common.vcxitems +++ b/src/terminal/parser/parser-common.vcxitems @@ -11,6 +11,7 @@ Create + @@ -20,5 +21,6 @@ + diff --git a/src/terminal/parser/precomp.h b/src/terminal/parser/precomp.h index d53d23f9856..38a4422bc24 100644 --- a/src/terminal/parser/precomp.h +++ b/src/terminal/parser/precomp.h @@ -24,7 +24,3 @@ Module Name: #include "telemetry.hpp" #include "tracing.hpp" - -#include -#include -#include diff --git a/src/terminal/parser/ut_parser/Base64Test.cpp b/src/terminal/parser/ut_parser/Base64Test.cpp new file mode 100644 index 00000000000..2b84d109d53 --- /dev/null +++ b/src/terminal/parser/ut_parser/Base64Test.cpp @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" +#include "WexTestClass.h" +#include "../../inc/consoletaeftemplates.hpp" + +#include "base64.hpp" + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +namespace Microsoft +{ + namespace Console + { + namespace VirtualTerminal + { + class Base64Test; + }; + }; +}; + +using namespace Microsoft::Console::VirtualTerminal; + +class Microsoft::Console::VirtualTerminal::Base64Test +{ + TEST_CLASS(Base64Test); + + TEST_METHOD(TestBase64Encode) + { + VERIFY_ARE_EQUAL(L"Zm9v", Base64::s_Encode(L"foo")); + VERIFY_ARE_EQUAL(L"Zm9vYg==", Base64::s_Encode(L"foob")); + VERIFY_ARE_EQUAL(L"Zm9vYmE=", Base64::s_Encode(L"fooba")); + VERIFY_ARE_EQUAL(L"Zm9vYmFy", Base64::s_Encode(L"foobar")); + VERIFY_ARE_EQUAL(L"Zm9vYmFyDQo=", Base64::s_Encode(L"foobar\r\n")); + } + + TEST_METHOD(TestBase64Decode) + { + std::wstring result; + bool success; + + success = Base64::s_Decode(L"Zm9v", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foo", result); + + result = L""; + success = Base64::s_Decode(L"Zm9vYg==", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foob", result); + + result = L""; + success = Base64::s_Decode(L"Zm9vYmE=", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"fooba", result); + + result = L""; + success = Base64::s_Decode(L"Zm9vYmFy", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foobar", result); + + result = L""; + success = Base64::s_Decode(L"Zm9vYmFyDQo=", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foobar\r\n", result); + + result = L""; + success = Base64::s_Decode(L"Zm9v\rYmFy", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foobar", result); + + result = L""; + success = Base64::s_Decode(L"Zm9v\r\nYmFy\n", result); + VERIFY_ARE_EQUAL(true, success); + VERIFY_ARE_EQUAL(L"foobar", result); + + success = Base64::s_Decode(L"Z", result); + VERIFY_ARE_EQUAL(false, success); + + success = Base64::s_Decode(L"Zm9vYg", result); + VERIFY_ARE_EQUAL(false, success); + + success = Base64::s_Decode(L"Zm9vYg=", result); + VERIFY_ARE_EQUAL(false, success); + } +}; diff --git a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj index 250a7f8b1b6..02e923f575e 100644 --- a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj +++ b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj @@ -21,6 +21,7 @@ + Create diff --git a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters index ecc61100147..adc0b0fec1f 100644 --- a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters +++ b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters @@ -24,6 +24,9 @@ Source Files + + Source Files + Source Files @@ -33,4 +36,4 @@ Header Files - \ No newline at end of file + diff --git a/src/terminal/parser/ut_parser/sources b/src/terminal/parser/ut_parser/sources index a7f53883e69..decf6b69e56 100644 --- a/src/terminal/parser/ut_parser/sources +++ b/src/terminal/parser/ut_parser/sources @@ -24,6 +24,7 @@ SOURCES = \ OutputEngineTest.cpp \ InputEngineTest.cpp \ StateMachineTest.cpp \ + Base64Test.cpp \ # The InputEngineTest requires VTRedirMapVirtualKeyW, which means we need the # ServiceLocator, which means we need the entire host and all it's dependencies, From 91a3843fcd73388200c2b6d3a62f7a0c565fa109 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Sun, 21 Jun 2020 01:00:17 +0800 Subject: [PATCH 08/13] Fix spellchecks and lint Signed-off-by: Mingxiang Xue --- .github/actions/spell-check/patterns/patterns.txt | 3 +++ src/terminal/parser/base64.cpp | 8 +++++--- src/terminal/parser/base64.hpp | 6 +++--- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/actions/spell-check/patterns/patterns.txt b/.github/actions/spell-check/patterns/patterns.txt index c6b738a41bf..413709e1202 100644 --- a/.github/actions/spell-check/patterns/patterns.txt +++ b/.github/actions/spell-check/patterns/patterns.txt @@ -16,3 +16,6 @@ Scro\&ll TestUtils::VerifyExpectedString\(tb, L"[^"]+" (?:hostSm|mach)\.ProcessString\(L"[^"]+" \b([A-Za-z])\1{3,}\b +Base64::s_(?:En|De)code\(L"[^"]+" +VERIFY_ARE_EQUAL\(L"[^"]+" +L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/" diff --git a/src/terminal/parser/base64.cpp b/src/terminal/parser/base64.cpp index fdcaac9dcad..f2ae6e33fd9 100644 --- a/src/terminal/parser/base64.cpp +++ b/src/terminal/parser/base64.cpp @@ -9,6 +9,8 @@ using namespace Microsoft::Console::VirtualTerminal; static const wchar_t base64Chars[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const wchar_t padChar = L'='; +#pragma warning(disable : 26446 26447 26482 26485 26493 26494) + // Routine Description: // - Encode a string using base64. When there are not enough characters // for one quantum, paddings are added. @@ -16,7 +18,7 @@ static const wchar_t padChar = L'='; // - src - String to base64 encode. // Return Value: // - the encoded string. -std::wstring Base64::s_Encode(const std::wstring_view src) +std::wstring Base64::s_Encode(const std::wstring_view src) noexcept { std::wstring dst; wchar_t input[3]; @@ -64,7 +66,7 @@ std::wstring Base64::s_Encode(const std::wstring_view src) // - dst - Destination to decode into. // Return Value: // - true if decoding successfully, otherwise false. -bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) +bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept { int state = 0; wchar_t tmp; @@ -164,7 +166,7 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) // - ch - Character to check. // Return Value: // - true iff ch is a carriage return or line feed. -bool Base64::s_IsSpace(const wchar_t ch) +constexpr bool Base64::s_IsSpace(const wchar_t ch) noexcept { return ch == L'\r' || ch == L'\n'; } diff --git a/src/terminal/parser/base64.hpp b/src/terminal/parser/base64.hpp index 499ca3d5340..976bcc5209a 100644 --- a/src/terminal/parser/base64.hpp +++ b/src/terminal/parser/base64.hpp @@ -16,10 +16,10 @@ namespace Microsoft::Console::VirtualTerminal class Base64 { public: - static std::wstring s_Encode(const std::wstring_view src); - static bool s_Decode(const std::wstring_view src, std::wstring& dst); + static std::wstring s_Encode(const std::wstring_view src) noexcept; + static bool s_Decode(const std::wstring_view src, std::wstring& dst) noexcept; private: - static bool s_IsSpace(const wchar_t ch); + static constexpr bool s_IsSpace(const wchar_t ch) noexcept; }; } From 2c9c29709fa8ceaa3595e5e67ac68869afe571b9 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Wed, 24 Jun 2020 12:14:53 +0800 Subject: [PATCH 09/13] Keep consistency Signed-off-by: Mingxiang Xue --- src/terminal/adapter/ITermDispatch.hpp | 2 +- src/terminal/adapter/adaptDispatch.hpp | 6 +++--- src/terminal/parser/OutputStateMachineEngine.cpp | 2 +- src/terminal/parser/OutputStateMachineEngine.hpp | 2 +- src/terminal/parser/telemetry.hpp | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index b98ec576fdd..66cd88f0449 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -110,7 +110,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) = 0; // DECSCUSR virtual bool SetCursorColor(const COLORREF color) = 0; // OSCSetCursorColor, OSCResetCursorColor - virtual bool SetClipboard(std::wstring_view content) = 0; // OscSetClipboard + virtual bool SetClipboard(std::wstring_view content) = 0; // OSCSetClipboard // DTTERM_WindowManipulation virtual bool WindowManipulation(const DispatchTypes::WindowManipulationType function, diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 0e3b440beba..0f28184e013 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -80,7 +80,7 @@ namespace Microsoft::Console::VirtualTerminal bool CarriageReturn() override; // CR bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) override; // IND, NEL, LF, FF, VT bool ReverseLineFeed() override; // RI - bool SetWindowTitle(const std::wstring_view title) override; // OscWindowTitle + bool SetWindowTitle(const std::wstring_view title) override; // OSCWindowTitle bool UseAlternateScreenBuffer() override; // ASBSET bool UseMainScreenBuffer() override; // ASBRST bool HorizontalTabSet() override; // HTS @@ -106,10 +106,10 @@ namespace Microsoft::Console::VirtualTerminal bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; - bool SetClipboard(const std::wstring_view content) noexcept override; // OscSetClipboard + bool SetClipboard(const std::wstring_view content) noexcept override; // OSCSetClipboard bool SetColorTableEntry(const size_t tableIndex, - const DWORD color) override; // OscColorTable + const DWORD color) override; // OSCColorTable bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground bool SetDefaultBackground(const DWORD color) override; // OSCDefaultBackground diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index e12cdb4821c..511cf4cb2d5 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -946,7 +946,7 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/, { success = _dispatch->SetClipboard(setClipboardContent); } - TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCC); + TermTelemetry::Instance().Log(TermTelemetry::Codes::OSCSCB); break; case OscActionCodes::ResetCursorColor: success = _dispatch->SetCursorColor(color); diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 3b878b6eee2..08c29aa07d6 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -254,7 +254,7 @@ namespace Microsoft::Console::VirtualTerminal size_t& repeatCount) const noexcept; bool _GetOscSetClipboard(const std::wstring_view string, - std::wstring& title, + std::wstring& content, bool& queryClipboard) const noexcept; void _ClearLastChar() noexcept; diff --git a/src/terminal/parser/telemetry.hpp b/src/terminal/parser/telemetry.hpp index 04396bdeb01..6f1b06ebb66 100644 --- a/src/terminal/parser/telemetry.hpp +++ b/src/terminal/parser/telemetry.hpp @@ -97,6 +97,7 @@ namespace Microsoft::Console::VirtualTerminal OSCFG, OSCBG, DECALN, + OSCSCB, // Only use this last enum as a count of the number of codes. NUMBER_OF_CODES }; From 3b16808c250c71d0551ecb2af07545c25841de99 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Wed, 24 Jun 2020 12:17:17 +0800 Subject: [PATCH 10/13] Reserve enough space to output when base64 encoding and decoding Signed-off-by: Mingxiang Xue --- src/terminal/parser/base64.cpp | 14 ++++++++++++++ src/terminal/parser/sources.inc | 1 + 2 files changed, 15 insertions(+) diff --git a/src/terminal/parser/base64.cpp b/src/terminal/parser/base64.cpp index f2ae6e33fd9..d935c0fe68d 100644 --- a/src/terminal/parser/base64.cpp +++ b/src/terminal/parser/base64.cpp @@ -23,6 +23,13 @@ std::wstring Base64::s_Encode(const std::wstring_view src) noexcept std::wstring dst; wchar_t input[3]; + auto len = (src.size() + 2) / 3 * 4; + if (len == 0) + { + return dst; + } + dst.reserve(len); + auto iter = src.cbegin(); // Encode each three chars into one quantum (four chars). while (iter < src.cend() - 2) @@ -71,6 +78,13 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept int state = 0; wchar_t tmp; + auto len = src.size() / 4 * 3; + if (len == 0) + { + return false; + } + dst.reserve(len); + auto iter = src.cbegin(); while (iter < src.cend()) { diff --git a/src/terminal/parser/sources.inc b/src/terminal/parser/sources.inc index d49cff67e2a..fbc49025354 100644 --- a/src/terminal/parser/sources.inc +++ b/src/terminal/parser/sources.inc @@ -35,6 +35,7 @@ SOURCES = \ ..\OutputStateMachineEngine.cpp \ ..\telemetry.cpp \ ..\tracing.cpp \ + ..\base64.cpp \ INCLUDES = \ $(INCLUDES); \ From 83d67005d5f7b77e3af404153852b0882c3a38a2 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Wed, 24 Jun 2020 12:31:01 +0800 Subject: [PATCH 11/13] Fix spellcheck Signed-off-by: Mingxiang Xue --- .github/actions/spell-check/expect/expect.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/spell-check/expect/expect.txt b/.github/actions/spell-check/expect/expect.txt index 32d4bc08b06..61a6c95d773 100644 --- a/.github/actions/spell-check/expect/expect.txt +++ b/.github/actions/spell-check/expect/expect.txt @@ -1611,6 +1611,7 @@ OSCCT OSCFG OSCRCC OSCSCC +OSCSCB OSCWT OSDEPENDSROOT osfhandle From 5d8a07bc54b11bdb0db5cba66c4724eb2a502d51 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Wed, 24 Jun 2020 14:03:35 +0800 Subject: [PATCH 12/13] Mark variable as const Signed-off-by: Mingxiang Xue --- src/terminal/parser/base64.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/terminal/parser/base64.cpp b/src/terminal/parser/base64.cpp index d935c0fe68d..f7cfc1e5c8c 100644 --- a/src/terminal/parser/base64.cpp +++ b/src/terminal/parser/base64.cpp @@ -23,7 +23,7 @@ std::wstring Base64::s_Encode(const std::wstring_view src) noexcept std::wstring dst; wchar_t input[3]; - auto len = (src.size() + 2) / 3 * 4; + const auto len = (src.size() + 2) / 3 * 4; if (len == 0) { return dst; @@ -78,7 +78,7 @@ bool Base64::s_Decode(const std::wstring_view src, std::wstring& dst) noexcept int state = 0; wchar_t tmp; - auto len = src.size() / 4 * 3; + const auto len = src.size() / 4 * 3; if (len == 0) { return false; From 56719e8833e3289d9fe052a203231e9ceac7d0d2 Mon Sep 17 00:00:00 2001 From: Mingxiang Xue Date: Fri, 26 Jun 2020 21:43:58 +0800 Subject: [PATCH 13/13] Trace logging OscSetClipboard Signed-off-by: Mingxiang Xue --- src/terminal/parser/telemetry.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/terminal/parser/telemetry.cpp b/src/terminal/parser/telemetry.cpp index 205efdbf298..6bed34f5434 100644 --- a/src/terminal/parser/telemetry.cpp +++ b/src/terminal/parser/telemetry.cpp @@ -268,6 +268,7 @@ void TermTelemetry::WriteFinalTraceLog() const TraceLoggingUInt32(_uiTimesUsed[OSCRCC], "OscResetCursorColor"), TraceLoggingUInt32(_uiTimesUsed[OSCFG], "OscForegroundColor"), TraceLoggingUInt32(_uiTimesUsed[OSCBG], "OscBackgroundColor"), + TraceLoggingUInt32(_uiTimesUsed[OSCSCB], "OscSetClipboard"), TraceLoggingUInt32(_uiTimesUsed[REP], "REP"), TraceLoggingUInt32(_uiTimesUsed[DECALN], "DECALN"), TraceLoggingUInt32Array(_uiTimesFailed, ARRAYSIZE(_uiTimesFailed), "Failed"),