From c42b25f977c626dc31fed5f6203ec3dae96b51e4 Mon Sep 17 00:00:00 2001 From: "Dan Thompson (SBS)" Date: Tue, 9 Jul 2019 22:04:03 -0700 Subject: [PATCH] Implement XTPUSHSGR, XTPOPSGR This change adds a new pair of methods to ITermDispatch: PushGraphicsRendition and PopGraphicsRendition, and then plumbs the change through AdaptDispatch, TerminalDispatch, ITerminalApi and TerminalApi. The stack logic is encapsulated in the SgrStack class, to allow it to be reused between the two APIs (AdaptDispatch and TerminalDispatch). Like xterm, only ten levels of nesting are supported. Pushes beyond ten will remain balanced (an equal number of pops will take you back down to zero), up to 100 pushes. Beyond 100 pushes, pushes will become unbalanced (the internal counter will no longer be incremented). This bound gives the terminal a deterministic way to recover from garbage--do 101 pops and you know you've cleared the stack back down to zero. For partial pushes (see the description of XTPUSHSGR in Issue #1796), only attributes that are supported by terminal are saved; others are ignored (this change does not including adding general support for double underlines, for example). A partial push of unsupported parameters results in an "empty" push--the subsequent pop will not change the current text attributes. --- src/buffer/out/TextAttribute.cpp | 6 +- src/buffer/out/TextAttribute.hpp | 6 +- src/buffer/out/sgrStack.hpp | 226 ++++++++++++++++++ src/cascadia/TerminalCore/ITerminalApi.hpp | 3 + src/cascadia/TerminalCore/Terminal.hpp | 6 + src/cascadia/TerminalCore/TerminalApi.cpp | 28 +++ .../TerminalCore/TerminalDispatch.hpp | 4 + .../TerminalCore/TerminalDispatchGraphics.cpp | 11 + src/host/outputStream.cpp | 8 +- src/terminal/adapter/DispatchTypes.hpp | 4 + src/terminal/adapter/ITermDispatch.hpp | 4 + src/terminal/adapter/adaptDispatch.hpp | 8 +- .../adapter/adaptDispatchGraphics.cpp | 46 ++++ src/terminal/adapter/termDispatch.hpp | 4 + .../parser/OutputStateMachineEngine.cpp | 98 +++++++- .../parser/OutputStateMachineEngine.hpp | 18 +- 16 files changed, 454 insertions(+), 26 deletions(-) create mode 100644 src/buffer/out/sgrStack.hpp diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index bdd2458a0f69..1dc74c7eb361 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -226,17 +226,17 @@ bool TextAttribute::IsRightVerticalDisplayed() const noexcept return WI_IsFlagSet(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL); } -void TextAttribute::SetLeftVerticalDisplayed(bool isDisplayed) noexcept +void TextAttribute::SetLeftVerticalDisplayed(const bool isDisplayed) noexcept { WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_LVERTICAL, isDisplayed); } -void TextAttribute::SetRightVerticalDisplayed(bool isDisplayed) noexcept +void TextAttribute::SetRightVerticalDisplayed(const bool isDisplayed) noexcept { WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_GRID_RVERTICAL, isDisplayed); } -void TextAttribute::SetBottomHorizontalDisplayed(bool isDisplayed) noexcept +void TextAttribute::SetBottomHorizontalDisplayed(const bool isDisplayed) noexcept { WI_UpdateFlag(_wAttrLegacy, COMMON_LVB_UNDERSCORE, isDisplayed); } diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index d16ae8a88ae9..2df018e72fca 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -105,9 +105,9 @@ class TextAttribute final bool IsLeftVerticalDisplayed() const noexcept; bool IsRightVerticalDisplayed() const noexcept; - void SetLeftVerticalDisplayed(bool isDisplayed) noexcept; - void SetRightVerticalDisplayed(bool isDisplayed) noexcept; - void SetBottomHorizontalDisplayed(bool isDisplayed) noexcept; + void SetLeftVerticalDisplayed(const bool isDisplayed) noexcept; + void SetRightVerticalDisplayed(const bool isDisplayed) noexcept; + void SetBottomHorizontalDisplayed(const bool isDisplayed) noexcept; void SetFromLegacy(const WORD wLegacy) noexcept; diff --git a/src/buffer/out/sgrStack.hpp b/src/buffer/out/sgrStack.hpp new file mode 100644 index 000000000000..65cf77dc7112 --- /dev/null +++ b/src/buffer/out/sgrStack.hpp @@ -0,0 +1,226 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- sgrStack.hpp + +Abstract: +- Encapsulates logic for the XTPUSHSGR / XTPOPSGR VT control sequences, which save and + restore text attributes on a stack. + +--*/ + +#pragma once + +#include "TextAttribute.hpp" +#include "..\..\terminal\adapter\DispatchTypes.hpp" + +namespace Microsoft::Console::VirtualTerminal +{ + class SgrStack + { + public: + SgrStack() : + _numSgrPushes{ 0 } + { + } + + // Method Description: + // - Saves the specified text attributes onto an internal stack. + // Arguments: + // - currentAttributes - The attributes to save onto the stack. + // - rgOptions, cOptions - If not supplied, the full attributes are saved. If + // options are supplied, only the specified parts of currentAttributes are + // saved. + // Return Value: + // - + void Push(const TextAttribute& currentAttributes, + _In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) noexcept + { + uint32_t validParts = 0; + + if (cOptions == 0) + { + // We save all current attributes. + validParts = UINT32_MAX; + } + else + { + // Each option is encoded as a bit in validParts. Options that aren't + // supported are ignored. So if you try to save only unsuppported aspects + // of the current text attributes, validParts will be zero, and you'll do + // what is effectively an "empty" push (the subsequent pop will not change + // the current attributes). + for (size_t i = 0; i < cOptions; i++) + { + validParts |= _GraphicsOptionToFlag(rgOptions[i]); + } + } + + if (_numSgrPushes < _countof(_storedSgrAttributes)) + { + _storedSgrAttributes[_numSgrPushes] = currentAttributes; + _validAttributes[_numSgrPushes] = validParts; + } + + if (_numSgrPushes < c_MaxBalancedPushes) + { + _numSgrPushes++; + } + } + + // Method Description: + // - Restores text attributes by removing from the top of the internal stack, + // combining them with the supplied currentAttributes, if appropriate. + // Arguments: + // - currentAttributes - The current text attributes. If only a portion of + // attributes were saved on the internal stack, then those attributes will be + // combined with the currentAttributes passed in to form the return value. + // Return Value: + // - The TextAttribute that has been removed from the top of the stack, possibly + // combined with currentAttributes. + const TextAttribute Pop(const TextAttribute& currentAttributes) noexcept + { + TextAttribute result; + + if (_numSgrPushes > 0) + { + _numSgrPushes--; + + if (_numSgrPushes < _countof(_storedSgrAttributes)) + { + uint32_t validParts = _validAttributes[_numSgrPushes]; + + if (validParts == UINT32_MAX) + { + return _storedSgrAttributes[_numSgrPushes]; + } + else + { + return _CombineWithCurrentAttributes(currentAttributes, + _storedSgrAttributes[_numSgrPushes], + validParts); + } + } + } + + return currentAttributes; + } + + // Xterm allows the save stack to go ten deep, so we'll follow suit. Pushes after + // ten deep will still remain "balanced"--once you pop back down below ten, you'll + // restore the appropriate text attributes. However, if you get more than a + // hundred pushes deep, we'll stop counting. Why unbalance somebody doing so many + // pushes? Putting a bound on it allows us to provide "reset" functionality: at + // any given point, you can execute 101 pops and know that you've taken the stack + // (push count) to zero. (Then you reset text attributes, and your state is + // clean.) + static constexpr int c_MaxStoredSgrPushes = 10; + static constexpr int c_MaxBalancedPushes = 100; + + private: + static constexpr uint32_t _GraphicsOptionToFlag(DispatchTypes::GraphicsOptions option) + { + int iOption = static_cast(option); + + if (iOption < (sizeof(uint32_t) * 8)) + { + iOption = 1 << iOption; + } + // else it's a bad parameter; we'll just ignore it + + return iOption; + } + + TextAttribute _CombineWithCurrentAttributes(const TextAttribute& currentAttributes, + const TextAttribute& savedAttribute, + uint32_t validParts) // of savedAttribute + { + TextAttribute result = currentAttributes; + + // From xterm documentation: + // + // CSI # { + // CSI Ps ; Ps # { + // Push video attributes onto stack (XTPUSHSGR), xterm. The + // optional parameters correspond to the SGR encoding for video + // attributes, except for colors (which do not have a unique SGR + // code): + // Ps = 1 -> Bold. + // Ps = 2 -> Faint. + // Ps = 3 -> Italicized. + // Ps = 4 -> Underlined. + // Ps = 5 -> Blink. + // Ps = 7 -> Inverse. + // Ps = 8 -> Invisible. + // Ps = 9 -> Crossed-out characters. + // Ps = 1 0 -> Foreground color. + // Ps = 1 1 -> Background color. + // Ps = 2 1 -> Doubly-underlined. + // + // (some closing braces for people with editors that get thrown off without them: }}) + // + // Attributes that are not currently supported are simply ignored. + + if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::BoldBright) & validParts) + { + if (savedAttribute.IsBold()) + { + result.Embolden(); + } + else + { + result.Debolden(); + } + } + + if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::Underline) & validParts) + { + if (savedAttribute.IsUnderline()) + { + result.EnableUnderline(); + } + else + { + result.DisableUnderline(); + } + } + + if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::Negative) & validParts) + { + if (savedAttribute.IsReverseVideo()) + { + if (!result.IsReverseVideo()) + { + result.Invert(); + } + } + else + { + if (result.IsReverseVideo()) + { + result.Invert(); + } + } + } + + if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::SaveForegroundColor) & validParts) + { + result.SetForegroundFrom(savedAttribute); + } + + if (_GraphicsOptionToFlag(DispatchTypes::GraphicsOptions::SaveBackgroundColor) & validParts) + { + result.SetBackgroundFrom(savedAttribute); + } + + return result; + } + + int _numSgrPushes; // used as an index into the following arrays + TextAttribute _storedSgrAttributes[c_MaxStoredSgrPushes]; + uint32_t _validAttributes[c_MaxStoredSgrPushes]; // flags that indicate which portions of the attributes are valid + }; +} diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index e85fb29c9503..470b2f079175 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -38,5 +38,8 @@ namespace Microsoft::Terminal::Core virtual bool SetDefaultForeground(const DWORD dwColor) = 0; virtual bool SetDefaultBackground(const DWORD dwColor) = 0; + + virtual bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions* options, size_t cOptions) = 0; + virtual bool PopGraphicsRendition() = 0; }; } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index ba2f2bdc4cf1..27ec446ad4c6 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -6,6 +6,7 @@ #include #include "../../buffer/out/textBuffer.hpp" +#include "../../buffer/out/sgrStack.hpp" #include "../../renderer/inc/IRenderData.hpp" #include "../../terminal/parser/StateMachine.hpp" #include "../../terminal/input/terminalInput.hpp" @@ -78,6 +79,9 @@ class Microsoft::Terminal::Core::Terminal final : bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) override; bool SetDefaultForeground(const COLORREF dwColor) override; bool SetDefaultBackground(const COLORREF dwColor) override; + + bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions* options, size_t cOptions) override; + bool PopGraphicsRendition() override; #pragma endregion #pragma region ITerminalInput @@ -225,4 +229,6 @@ class Microsoft::Terminal::Core::Terminal final : const COORD _ConvertToBufferCell(const COORD viewportPos) const; const bool _isSingleCellSelection() const noexcept; #pragma endregion + + Microsoft::Console::VirtualTerminal::SgrStack _sgrStack; }; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 8aad5abc0108..9935665ab783 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -474,3 +474,31 @@ bool Terminal::SetDefaultBackground(const COLORREF dwColor) _buffer->GetRenderTarget().TriggerRedrawAll(); return true; } + +// Method Description: +// - Saves the current text attributes to an internal stack. +// Arguments: +// - options, cOptions: if present, specify which portions of the current text attributes +// should be saved. Only a small subset of GraphicsOptions are actually supported; +// others are ignored. If no options are specified, all attributes are stored. +// Return Value: +// - true +bool Terminal::PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions* options, size_t cOptions) +{ + _sgrStack.Push(_buffer->GetCurrentAttributes(), options, cOptions); + return true; +} + +// Method Description: +// - Restores text attributes from the internal stack. If only portions of text attributes +// were saved, combines those with the current attributes. +// Arguments: +// - +// Return Value: +// - true +bool Terminal::PopGraphicsRendition() +{ + TextAttribute current = _buffer->GetCurrentAttributes(); + _buffer->SetCurrentAttributes(_sgrStack.Pop(current)); + return true; +} diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 5fa9758856ee..2988d60f41d3 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -16,6 +16,10 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions* const rgOptions, const size_t cOptions) override; + bool PushGraphicsRendition(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) override; + bool PopGraphicsRendition() override; + virtual bool CursorPosition(const unsigned int uiLine, const unsigned int uiColumn) override; // CUP diff --git a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp index 03dab6c849aa..831464da96b6 100644 --- a/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatchGraphics.cpp @@ -326,3 +326,14 @@ bool TerminalDispatch::SetGraphicsRendition(const DispatchTypes::GraphicsOptions } return fSuccess; } + +bool TerminalDispatch::PushGraphicsRendition(const DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) +{ + return _terminalApi.PushGraphicsRendition(rgOptions, cOptions); +} + +bool TerminalDispatch::PopGraphicsRendition() +{ + return _terminalApi.PopGraphicsRendition(); +} diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 98d7f024af07..fc3837dd9832 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -229,7 +229,7 @@ BOOL ConhostInternalGetSet::PrivateSetDefaultAttributes(const bool fForeground, // - fBackground - The new attributes contain an update to the background attributes // - fMeta - The new attributes contain an update to the meta attributes // Return Value: -// - TRUE if successful (see DoSrvVtSetLegacyAttributes). FALSE otherwise. +// - TRUE. BOOL ConhostInternalGetSet::PrivateSetLegacyAttributes(const WORD wAttr, const bool fForeground, const bool fBackground, @@ -239,6 +239,12 @@ BOOL ConhostInternalGetSet::PrivateSetLegacyAttributes(const WORD wAttr, return TRUE; } +// Routine Description: +// - Similar to PrivateSetLegacyAttributes, but sets the full fidelity TextAttribute. +// Arguments: +// - attributes - new text attributes to apply as default within the console text buffer +// Return Value: +// - TRUE. BOOL ConhostInternalGetSet::PrivateSetAttributes(const TextAttribute& attributes) { DoSrvPrivateSetAttributes(_io.GetActiveOutputBuffer(), attributes); diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index e76349ce0c0f..66e2399adf51 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -25,6 +25,10 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes // the 2 and 5 entries here are only for the extended graphics options // as we do not currently support those features individually Negative = 7, + // 10 - 19 are actually for selecting fonts, but rarely implemented. 10 and 11 + // have been repurposed by xterm for XTPUSHGR. + SaveForegroundColor = 10, + SaveBackgroundColor = 11, UnBold = 22, NoUnderline = 24, Positive = 27, diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 59b9d7490023..b4fe1793fab6 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -77,6 +77,10 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool SetGraphicsRendition(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, const size_t cOptions) = 0; // SGR + virtual bool PushGraphicsRendition(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) = 0; // XTPUSHSGR + virtual bool PopGraphicsRendition() = 0; // XTPOPSGR + virtual bool SetPrivateModes(_In_reads_(cParams) const DispatchTypes::PrivateModeParams* const rgParams, const size_t cParams) = 0; // DECSET diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index a4fc702c5ced..dbd3d22cf44f 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -19,6 +19,7 @@ Author(s): #include "conGetSet.hpp" #include "adaptDefaults.hpp" #include "terminalOutput.hpp" +#include "..\..\buffer\out\sgrStack.hpp" #include #define XTERM_COLOR_TABLE_SIZE (256) @@ -58,6 +59,9 @@ namespace Microsoft::Console::VirtualTerminal bool DeleteCharacter(_In_ unsigned int const uiCount) override; // DCH bool SetGraphicsRendition(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, const size_t cOptions) override; // SGR + bool PushGraphicsRendition(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) override; // XTPUSHSGR + bool PopGraphicsRendition() override; // XTPOPSGR bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) override; // DSR bool DeviceAttributes() override; // DA bool ScrollUp(_In_ unsigned int const uiDistance) override; // SU @@ -154,14 +158,14 @@ namespace Microsoft::Console::VirtualTerminal bool _fIsOriginModeRelative; bool _fIsSavedOriginModeRelative; - bool _fIsSetColumnsEnabled; - bool _fIsDECCOLMAllowed; bool _fChangedForeground; bool _fChangedBackground; bool _fChangedMetaAttrs; + SgrStack _sgrStack; + bool _SetRgbColorsHelper(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, const size_t cOptions, _Out_ COLORREF* const prgbColor, diff --git a/src/terminal/adapter/adaptDispatchGraphics.cpp b/src/terminal/adapter/adaptDispatchGraphics.cpp index 680bca51a0da..99839a4e29ad 100644 --- a/src/terminal/adapter/adaptDispatchGraphics.cpp +++ b/src/terminal/adapter/adaptDispatchGraphics.cpp @@ -448,3 +448,49 @@ bool AdaptDispatch::SetGraphicsRendition(_In_reads_(cOptions) const DispatchType return fSuccess; } + +// Method Description: +// - Saves the current text attributes to an internal stack. +// Arguments: +// - rgOptions, cOptions: if present, specify which portions of the current text attributes +// should be saved. Only a small subset of GraphicsOptions are actually supported; +// others are ignored. If no options are specified, all attributes are stored. +// Return Value: +// - True if handled successfully. False otherwise. +bool AdaptDispatch::PushGraphicsRendition(_In_reads_(cOptions) const DispatchTypes::GraphicsOptions* const rgOptions, + const size_t cOptions) +{ + bool fSuccess = true; + TextAttribute currentAttributes; + + fSuccess = _conApi->PrivateGetConsoleScreenBufferAttributes(¤tAttributes); + + if (fSuccess) + { + _sgrStack.Push(currentAttributes, rgOptions, cOptions); + } + + return fSuccess; +} + +// Method Description: +// - Restores text attributes from the internal stack. If only portions of text attributes +// were saved, combines those with the current attributes. +// Arguments: +// - +// Return Value: +// - True if handled successfully. False otherwise. +bool AdaptDispatch::PopGraphicsRendition() +{ + bool fSuccess = true; + TextAttribute currentAttributes; + + fSuccess = _conApi->PrivateGetConsoleScreenBufferAttributes(¤tAttributes); + + if (fSuccess) + { + fSuccess = _conApi->PrivateSetAttributes(_sgrStack.Pop(currentAttributes)); + } + + return fSuccess; +} diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index 8fb7bce91613..e8dc85bc0d33 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -74,6 +74,10 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool SetGraphicsRendition(_In_reads_(_Param_(2)) const DispatchTypes::GraphicsOptions* const /*rgOptions*/, const size_t /*cOptions*/) override { return false; } // SGR + bool PushGraphicsRendition(_In_reads_(_Param_(2)) const DispatchTypes::GraphicsOptions* const /*rgOptions*/, + const size_t /*cOptions*/) override { return false; } // XTPUSHSGR + bool PopGraphicsRendition() override { return false; } // XTPOPSGR + bool SetPrivateModes(_In_reads_(_Param_(2)) const DispatchTypes::PrivateModeParams* const /*rgParams*/, const size_t /*cParams*/) override { return false; } // DECSET diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index a17699a5247c..41542ca2592a 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -504,6 +504,9 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, case L' ': fSuccess = _IntermediateSpaceDispatch(wch, rgusParams, cParams); break; + case L'#': + fSuccess = _IntermediateHashDispatch(wch, rgusParams, cParams); + break; default: // If no functions to call, overall dispatch was a failure. fSuccess = false; @@ -541,7 +544,7 @@ bool OutputStateMachineEngine::_IntermediateQuestionMarkDispatch(const wchar_t w { case VTActionCodes::DECSET_PrivateModeSet: case VTActionCodes::DECRST_PrivateModeReset: - fSuccess = _GetPrivateModeParams(rgusParams, cParams, rgPrivateModeParams, &cOptions); + fSuccess = _GetTypedParams(rgusParams, cParams, rgPrivateModeParams, &cOptions); break; default: @@ -639,6 +642,71 @@ bool OutputStateMachineEngine::_IntermediateSpaceDispatch(const wchar_t wchActio return fSuccess; } +// Routine Description: +// - Handles actions that have an intermediate '#' (0x23), such as XTPUSHSGR, XTPOPSGR +// Arguments: +// - wch - Character to dispatch. +// Return Value: +// - True if handled successfully. False otherwise. +bool OutputStateMachineEngine::_IntermediateHashDispatch(const wchar_t wchAction, + _In_reads_(cParams) const unsigned short* const rgusParams, + const unsigned short cParams) +{ + bool fSuccess = false; + + DispatchTypes::GraphicsOptions rgPushPopParams[StateMachine::s_cParamsMax]; + size_t cOptions = StateMachine::s_cParamsMax; + // Ensure that there was the right number of params + switch (wchAction) + { + case VTActionCodes::XT_PushSgr: + case VTActionCodes::XT_PushSgrAlias: + fSuccess = _GetTypedParams(rgusParams, cParams, rgPushPopParams, &cOptions); + break; + + case VTActionCodes::XT_PopSgr: + case VTActionCodes::XT_PopSgrAlias: + if (cParams > 0) + { + // Can't supply params for XTPOPSGR. + fSuccess = false; + } + else + { + fSuccess = true; + } + break; + + default: + // If no params to fill, param filling was successful. + fSuccess = true; + break; + } + if (fSuccess) + { + switch (wchAction) + { + case VTActionCodes::XT_PushSgr: + case VTActionCodes::XT_PushSgrAlias: + fSuccess = _dispatch->PushGraphicsRendition(rgPushPopParams, cOptions); + // TODO: telemetry + break; + + case VTActionCodes::XT_PopSgr: + case VTActionCodes::XT_PopSgrAlias: + fSuccess = _dispatch->PopGraphicsRendition(); + // TODO: telemetry + break; + + default: + // If no functions to call, overall dispatch was a failure. + fSuccess = false; + break; + } + } + return fSuccess; +} + // Routine Description: // - Triggers the Clear action to indicate that the state machine should erase // all internal state. @@ -1071,6 +1139,7 @@ _Success_(return ) bool OutputStateMachineEngine::_GetTopBottomMargins(_In_reads } return fSuccess; } + // Routine Description: // - Retrieves the status type parameter for an upcoming device query operation // Arguments: @@ -1103,27 +1172,27 @@ _Success_(return ) bool OutputStateMachineEngine::_GetDeviceStatusOperation(_In_ } // Routine Description: -// - Retrieves the listed private mode params be set/reset by DECSET/DECRST +// - Converts the untyped array of numeric parameters into an array of the specified type. // Arguments: -// - rPrivateModeParams - Pointer to array space (expected 16 max, the max number of params this can generate) that will be filled with valid params from the PrivateModeParams enum -// - pcParams - Pointer to the length of rPrivateModeParams on the way in, and the count of the array used on the way out. +// - rgTypedParams - Pointer to array space (expected 16 max, the max number of params this can generate) that will be filled with valid params from the PrivateModeParams enum +// - pcParams - Pointer to the length of rgTypedParams on the way in, and the count of the array used on the way out. // Return Value: -// - True if we successfully retrieved an array of private mode params from the parameters we've stored. False otherwise. -_Success_(return ) bool OutputStateMachineEngine::_GetPrivateModeParams(_In_reads_(cParams) const unsigned short* const rgusParams, - const unsigned short cParams, - _Out_writes_(*pcParams) DispatchTypes::PrivateModeParams* const rgPrivateModeParams, - _Inout_ size_t* const pcParams) const +// - True if we successfully retrieved an array of strongly-typed params from the parameters we've stored. False otherwise. +template +_Success_(return ) bool OutputStateMachineEngine::_GetTypedParams(_In_reads_(cParams) const unsigned short* const rgusParams, + const unsigned short cParams, + _Out_writes_(*pcParams) TParamType* const rgTypedParams, + _Inout_ size_t* const pcParams) const { bool fSuccess = false; - // Can't just set nothing at all if (cParams > 0) { if (*pcParams >= cParams) { for (size_t i = 0; i < cParams; i++) { - // No memcpy. The parameters are shorts. The graphics options are unsigned ints. - rgPrivateModeParams[i] = (DispatchTypes::PrivateModeParams)rgusParams[i]; + // No memcpy because the parameters are shorts, and the destination type may be a different size. + rgTypedParams[i] = static_cast(rgusParams[i]); } *pcParams = cParams; fSuccess = true; @@ -1133,6 +1202,11 @@ _Success_(return ) bool OutputStateMachineEngine::_GetPrivateModeParams(_In_read fSuccess = false; // not enough space in buffer to hold response. } } + else + { + *pcParams = 0; + fSuccess = true; + } return fSuccess; } diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index b5ac240a0fad..48832f98d5f2 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -81,6 +81,9 @@ namespace Microsoft::Console::VirtualTerminal bool _IntermediateSpaceDispatch(const wchar_t wchAction, _In_reads_(cParams) const unsigned short* const rgusParams, const unsigned short cParams); + bool _IntermediateHashDispatch(const wchar_t wchAction, + _In_reads_(cParams) const unsigned short* const rgusParams, + const unsigned short cParams); enum VTActionCodes : wchar_t { @@ -126,7 +129,11 @@ namespace Microsoft::Console::VirtualTerminal // 'q' is overloaded - no postfix is DECLL, ' ' postfix is DECSCUSR, and '"' is DECSCA DECSCUSR_SetCursorStyle = L'q', // I believe we'll only ever implement DECSCUSR DTTERM_WindowManipulation = L't', - REP_RepeatCharacter = L'b' + REP_RepeatCharacter = L'b', + XT_PushSgr = L'{', + XT_PushSgrAlias = L'p', + XT_PopSgr = L'}', + XT_PopSgrAlias = L'q', }; enum OscActionCodes : unsigned int @@ -194,10 +201,11 @@ namespace Microsoft::Console::VirtualTerminal _Success_(return ) bool _VerifyDeviceAttributesParams(_In_reads_(cParams) const unsigned short* const rgusParams, const unsigned short cParams) const; - _Success_(return ) bool _GetPrivateModeParams(_In_reads_(cParams) const unsigned short* const rgusParams, - const unsigned short cParams, - _Out_writes_(*pcParams) DispatchTypes::PrivateModeParams* const rgPrivateModeParams, - _Inout_ size_t* const pcParams) const; + template + _Success_(return ) bool _GetTypedParams(_In_reads_(cParams) const unsigned short* const rgusParams, + const unsigned short cParams, + _Out_writes_(*pcParams) TParamType* const rgXtPushParams, + _Inout_ size_t* const pcParams) const; static const SHORT s_sDefaultTopMargin = 0; static const SHORT s_sDefaultBottomMargin = 0;