diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 8f8f4828d0a..013bd8b8a79 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -61,7 +61,6 @@ argb ARRAYSIZE ARROWKEYS asan -ASBRST ASBSET ASDF asdfghjkl @@ -433,6 +432,7 @@ DECRARA DECRC DECREQTPARM DECRLM +DECRPM DECRQM DECRQSS DECRQTSR diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 7dcc2395402..901b08b07ba 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -101,6 +101,13 @@ void Cursor::SetIsOn(const bool fIsOn) noexcept void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) noexcept { _fBlinkingAllowed = fBlinkingAllowed; + // GH#2642 - From what we've gathered from other terminals, when blinking is + // disabled, the cursor should remain On always, and have the visibility + // controlled by the IsVisible property. So when you do a printf "\e[?12l" + // to disable blinking, the cursor stays stuck On. At this point, only the + // cursor visibility property controls whether the user can see it or not. + // (Yes, the cursor can be On and NOT Visible) + _fIsOn = true; _RedrawCursorAlways(); } diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 85fdfd41d5f..65fafbb891b 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -115,6 +115,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetViewportPosition(const til::point position) noexcept override; void SetTextAttributes(const TextAttribute& attrs) noexcept override; void SetAutoWrapMode(const bool wrapAtEOL) noexcept override; + bool GetAutoWrapMode() const noexcept override; void SetScrollingRegion(const til::inclusive_rect& scrollMargins) noexcept override; void WarningBell() override; bool GetLineFeedMode() const noexcept override; @@ -124,7 +125,8 @@ class Microsoft::Terminal::Core::Terminal final : bool ResizeWindow(const til::CoordType width, const til::CoordType height) noexcept override; void SetConsoleOutputCP(const unsigned int codepage) noexcept override; unsigned int GetConsoleOutputCP() const noexcept override; - void EnableXtermBracketedPasteMode(const bool enabled) noexcept override; + void SetBracketedPasteMode(const bool enabled) noexcept override; + std::optional GetBracketedPasteMode() const noexcept override; void CopyToClipboard(std::wstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(std::wstring_view uri) override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 7e2e7e8b6ce..7fc1fa2f593 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -60,6 +60,12 @@ void Terminal::SetAutoWrapMode(const bool /*wrapAtEOL*/) noexcept // TODO: This will be needed to support DECAWM. } +bool Terminal::GetAutoWrapMode() const noexcept +{ + // TODO: This will be needed to support DECAWM. + return true; +} + void Terminal::SetScrollingRegion(const til::inclusive_rect& /*scrollMargins*/) noexcept { // TODO: This will be needed to fully support DECSTBM. @@ -123,11 +129,16 @@ unsigned int Terminal::GetConsoleOutputCP() const noexcept return CP_UTF8; } -void Terminal::EnableXtermBracketedPasteMode(const bool enabled) noexcept +void Terminal::SetBracketedPasteMode(const bool enabled) noexcept { _bracketedPasteMode = enabled; } +std::optional Terminal::GetBracketedPasteMode() const noexcept +{ + return _bracketedPasteMode; +} + void Terminal::CopyToClipboard(std::wstring_view content) { _pfnCopyToClipboard(content); diff --git a/src/host/outputStream.cpp b/src/host/outputStream.cpp index 99af19715f0..c3670046500 100644 --- a/src/host/outputStream.cpp +++ b/src/host/outputStream.cpp @@ -159,6 +159,18 @@ void ConhostInternalGetSet::SetAutoWrapMode(const bool wrapAtEOL) WI_UpdateFlag(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT, wrapAtEOL); } +// Routine Description: +// - Retrieves the current state of ENABLE_WRAP_AT_EOL_OUTPUT mode. +// Arguments: +// - +// Return Value: +// - true if the mode is enabled. false otherwise. +bool ConhostInternalGetSet::GetAutoWrapMode() const +{ + const auto outputMode = _io.GetActiveOutputBuffer().OutputMode; + return WI_IsFlagSet(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT); +} + // Routine Description: // - Sets the top and bottom scrolling margins for the current page. This creates // a subsection of the screen that scrolls when input reaches the end of the @@ -325,9 +337,24 @@ unsigned int ConhostInternalGetSet::GetConsoleOutputCP() const // - enable - set to true to enable bracketing, false to disable. // Return Value: // - -void ConhostInternalGetSet::EnableXtermBracketedPasteMode(const bool /*enabled*/) +void ConhostInternalGetSet::SetBracketedPasteMode(const bool enabled) { - // TODO + // TODO GH#395: Bracketed Paste Mode is not yet supported in conhost, but we + // still keep track of the state so it can be reported by DECRQM. + _bracketedPasteMode = enabled; +} + +// Routine Description: +// - Gets the current state of XTerm bracketed paste mode. +// Arguments: +// - +// Return Value: +// - true if the mode is enabled, false if not, nullopt if unsupported. +std::optional ConhostInternalGetSet::GetBracketedPasteMode() const +{ + // TODO GH#395: Bracketed Paste Mode is not yet supported in conhost, so we + // only report the state if we're tracking it for conpty. + return IsConsolePty() ? std::optional{ _bracketedPasteMode } : std::nullopt; } // Routine Description: diff --git a/src/host/outputStream.hpp b/src/host/outputStream.hpp index 2acd85ae295..bcd43f1aa80 100644 --- a/src/host/outputStream.hpp +++ b/src/host/outputStream.hpp @@ -40,6 +40,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetTextAttributes(const TextAttribute& attrs) override; void SetAutoWrapMode(const bool wrapAtEOL) override; + bool GetAutoWrapMode() const override; void SetScrollingRegion(const til::inclusive_rect& scrollMargins) override; @@ -63,7 +64,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: void SetConsoleOutputCP(const unsigned int codepage) override; unsigned int GetConsoleOutputCP() const override; - void EnableXtermBracketedPasteMode(const bool enabled) override; + void SetBracketedPasteMode(const bool enabled) override; + std::optional GetBracketedPasteMode() const override; void CopyToClipboard(const std::wstring_view content) override; void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override; void SetWorkingDirectory(const std::wstring_view uri) override; @@ -78,4 +80,5 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal:: private: Microsoft::Console::IIoProvider& _io; + bool _bracketedPasteMode{ false }; }; diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 44f3de5dbb3..4fe673e527b 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -403,6 +403,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes ATT610_StartCursorBlink = DECPrivateMode(12), DECTCEM_TextCursorEnableMode = DECPrivateMode(25), XTERM_EnableDECCOLMSupport = DECPrivateMode(40), + DECNKM_NumericKeypadMode = DECPrivateMode(66), DECBKM_BackarrowKeyMode = DECPrivateMode(67), VT200_MOUSE_MODE = DECPrivateMode(1000), BUTTON_EVENT_MOUSE_MODE = DECPrivateMode(1002), diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index eab7c1b4000..de0bfe19e37 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -45,43 +45,24 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool CursorPosition(const VTInt line, const VTInt column) = 0; // CUP, HVP virtual bool CursorSaveState() = 0; // DECSC virtual bool CursorRestoreState() = 0; // DECRC - virtual bool CursorVisibility(const bool isVisible) = 0; // DECTCEM virtual bool InsertCharacter(const VTInt count) = 0; // ICH virtual bool DeleteCharacter(const VTInt count) = 0; // DCH virtual bool ScrollUp(const VTInt distance) = 0; // SU virtual bool ScrollDown(const VTInt distance) = 0; // SD virtual bool InsertLine(const VTInt distance) = 0; // IL virtual bool DeleteLine(const VTInt distance) = 0; // DL - virtual bool SetColumns(const VTInt columns) = 0; // DECCOLM - virtual bool SetCursorKeysMode(const bool applicationMode) = 0; // DECCKM virtual bool SetKeypadMode(const bool applicationMode) = 0; // DECKPAM, DECKPNM - virtual bool EnableWin32InputMode(const bool win32InputMode) = 0; // win32-input-mode - virtual bool EnableCursorBlinking(const bool enable) = 0; // ATT610 virtual bool SetAnsiMode(const bool ansiMode) = 0; // DECANM - virtual bool SetScreenMode(const bool reverseMode) = 0; // DECSCNM - virtual bool SetOriginMode(const bool relativeMode) = 0; // DECOM - virtual bool SetAutoWrapMode(const bool wrapAtEOL) = 0; // DECAWM virtual bool SetTopBottomScrollingMargins(const VTInt topMargin, const VTInt bottomMargin) = 0; // DECSTBM virtual bool WarningBell() = 0; // BEL virtual bool CarriageReturn() = 0; // CR virtual bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) = 0; // IND, NEL, LF, FF, VT virtual bool ReverseLineFeed() = 0; // RI virtual bool SetWindowTitle(std::wstring_view title) = 0; // OscWindowTitle - virtual bool UseAlternateScreenBuffer() = 0; // ASBSET - virtual bool UseMainScreenBuffer() = 0; // ASBRST virtual bool HorizontalTabSet() = 0; // HTS virtual bool ForwardTab(const VTInt numTabs) = 0; // CHT, HT virtual bool BackwardsTab(const VTInt numTabs) = 0; // CBT virtual bool TabClear(const DispatchTypes::TabClearType clearType) = 0; // TBC - virtual bool EnableDECCOLMSupport(const bool enabled) = 0; // ?40 - virtual bool EnableVT200MouseMode(const bool enabled) = 0; // ?1000 - virtual bool EnableUTF8ExtendedMouseMode(const bool enabled) = 0; // ?1005 - virtual bool EnableSGRExtendedMouseMode(const bool enabled) = 0; // ?1006 - virtual bool EnableButtonEventMouseMode(const bool enabled) = 0; // ?1002 - virtual bool EnableAnyEventMouseMode(const bool enabled) = 0; // ?1003 - virtual bool EnableFocusEventMode(const bool enabled) = 0; // ?1004 - virtual bool EnableAlternateScroll(const bool enabled) = 0; // ?1007 - virtual bool EnableXtermBracketedPasteMode(const bool enabled) = 0; // ?2004 virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCColorTable virtual bool SetDefaultForeground(const DWORD color) = 0; // OSCDefaultForeground virtual bool SetDefaultBackground(const DWORD color) = 0; // OSCDefaultBackground @@ -109,8 +90,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool PopGraphicsRendition() = 0; // XTPOPSGR virtual bool SetMode(const DispatchTypes::ModeParams param) = 0; // DECSET - virtual bool ResetMode(const DispatchTypes::ModeParams param) = 0; // DECRST + virtual bool RequestMode(const DispatchTypes::ModeParams param) = 0; // DECRQM virtual bool DeviceStatusReport(const DispatchTypes::StatusType statusType) = 0; // DSR, DSR-OS, DSR-CPR virtual bool DeviceAttributes() = 0; // DA1 diff --git a/src/terminal/adapter/ITerminalApi.hpp b/src/terminal/adapter/ITerminalApi.hpp index 32910133560..0effa78f073 100644 --- a/src/terminal/adapter/ITerminalApi.hpp +++ b/src/terminal/adapter/ITerminalApi.hpp @@ -50,6 +50,7 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetTextAttributes(const TextAttribute& attrs) = 0; virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0; + virtual bool GetAutoWrapMode() const = 0; virtual void SetScrollingRegion(const til::inclusive_rect& scrollMargins) = 0; virtual void WarningBell() = 0; @@ -66,7 +67,8 @@ namespace Microsoft::Console::VirtualTerminal virtual void SetConsoleOutputCP(const unsigned int codepage) = 0; virtual unsigned int GetConsoleOutputCP() const = 0; - virtual void EnableXtermBracketedPasteMode(const bool enabled) = 0; + virtual void SetBracketedPasteMode(const bool enabled) = 0; + virtual std::optional GetBracketedPasteMode() const = 0; virtual void CopyToClipboard(const std::wstring_view content) = 0; virtual void SetTaskbarProgress(const DispatchTypes::TaskbarState state, const size_t progress) = 0; virtual void SetWorkingDirectory(const std::wstring_view uri) = 0; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index daf1517553f..b57ea6cb393 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -21,9 +21,6 @@ AdaptDispatch::AdaptDispatch(ITerminalApi& api, Renderer& renderer, RenderSettin _renderSettings{ renderSettings }, _terminalInput{ terminalInput }, _usingAltBuffer(false), - _isOriginModeRelative(false), // by default, the DECOM origin mode is absolute. - _isDECCOLMAllowed(false), // by default, DECCOLM is not allowed. - _isChangeExtentRectangular(false), _termOutput() { } @@ -206,7 +203,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // viewport, or the top margin, depending on the origin mode. if (rowOffset.IsAbsolute) { - row = _isOriginModeRelative ? topMargin : viewport.top; + row = _modes.test(Mode::Origin) ? topMargin : viewport.top; } // And if the column is absolute, it'll be relative to column 0. @@ -225,7 +222,7 @@ bool AdaptDispatch::_CursorMovePosition(const Offset rowOffset, const Offset col // If the operation needs to be clamped inside the margins, or the origin // mode is relative (which always requires margin clamping), then the row // may need to be adjusted further. - if (clampInMargins || _isOriginModeRelative) + if (clampInMargins || _modes.test(Mode::Origin)) { // See microsoft/terminal#2929 - If the cursor is _below_ the top // margin, it should stay below the top margin. If it's _above_ the @@ -351,10 +348,10 @@ bool AdaptDispatch::CursorSaveState() auto& savedCursorState = _savedCursorState.at(_usingAltBuffer); savedCursorState.Column = cursorPosition.X + 1; savedCursorState.Row = cursorPosition.Y + 1; - savedCursorState.IsOriginModeRelative = _isOriginModeRelative; + savedCursorState.IsOriginModeRelative = _modes.test(Mode::Origin); savedCursorState.Attributes = attributes; savedCursorState.TermOutput = _termOutput; - savedCursorState.C1ControlsAccepted = _GetParserMode(StateMachine::Mode::AcceptC1); + savedCursorState.C1ControlsAccepted = _api.GetStateMachine().GetParserMode(StateMachine::Mode::AcceptC1); savedCursorState.CodePage = _api.GetConsoleOutputCP(); return true; @@ -386,11 +383,11 @@ bool AdaptDispatch::CursorRestoreState() } // The saved coordinates are always absolute, so we need reset the origin mode temporarily. - _isOriginModeRelative = false; + _modes.reset(Mode::Origin); CursorPosition(row, col); // Once the cursor position is restored, we can then restore the actual origin mode. - _isOriginModeRelative = savedCursorState.IsOriginModeRelative; + _modes.set(Mode::Origin, savedCursorState.IsOriginModeRelative); // Restore text attributes. _api.SetTextAttributes(savedCursorState.Attributes); @@ -410,18 +407,6 @@ bool AdaptDispatch::CursorRestoreState() return true; } -// Routine Description: -// - DECTCEM - Sets the show/hide visibility status of the cursor. -// Arguments: -// - fIsVisible - Turns the cursor rendering on (TRUE) or off (FALSE). -// Return Value: -// - True. -bool AdaptDispatch::CursorVisibility(const bool fIsVisible) -{ - _api.GetTextBuffer().GetCursor().SetIsVisible(fIsVisible); - return true; -} - // Routine Description: // - Scrolls an area of the buffer in a vertical direction. // Arguments: @@ -826,7 +811,7 @@ void AdaptDispatch::_ChangeRectAttributes(TextBuffer& textBuffer, const til::rec // Arguments: // - changeArea - Area of the buffer that will be affected. This may be // interpreted as a rectangle or a stream depending on the state of the -// _isChangeExtentRectangular field. +// RectangularChangeExtent mode. // - changeOps - Changes that will be applied to each of the attributes. // Return Value: // - @@ -839,7 +824,7 @@ void AdaptDispatch::_ChangeRectOrStreamAttributes(const til::rect& changeArea, c // If the change extent is rectangular, we can apply the change with a // single call. The same is true for a stream extent that is only one line. - if (_isChangeExtentRectangular || lineCount == 1) + if (_modes.test(Mode::RectangularChangeExtent) || lineCount == 1) { _ChangeRectAttributes(textBuffer, changeRect, changeOps); } @@ -875,8 +860,8 @@ til::rect AdaptDispatch::_CalculateRectArea(const VTInt top, const VTInt left, c // We start by calculating the margin offsets and maximum dimensions. // If the origin mode isn't set, we use the viewport extent. const auto [topMargin, bottomMargin] = _GetVerticalMargins(viewport, false); - const auto yOffset = _isOriginModeRelative ? topMargin : 0; - const auto yMaximum = _isOriginModeRelative ? bottomMargin + 1 : viewport.height(); + const auto yOffset = _modes.test(Mode::Origin) ? topMargin : 0; + const auto yMaximum = _modes.test(Mode::Origin) ? bottomMargin + 1 : viewport.height(); const auto xMaximum = bufferSize.width; auto fillRect = til::inclusive_rect{}; @@ -1139,10 +1124,10 @@ bool AdaptDispatch::SelectAttributeChangeExtent(const DispatchTypes::ChangeExten { case DispatchTypes::ChangeExtent::Default: case DispatchTypes::ChangeExtent::Stream: - _isChangeExtentRectangular = false; + _modes.reset(Mode::RectangularChangeExtent); return true; case DispatchTypes::ChangeExtent::Rectangle: - _isChangeExtentRectangular = true; + _modes.set(Mode::RectangularChangeExtent); return true; default: return false; @@ -1313,7 +1298,7 @@ void AdaptDispatch::_CursorPositionReport(const bool extendedReport) cursorPosition.Y++; // If the origin mode is relative, line numbers start at top margin of the scrolling region. - if (_isOriginModeRelative) + if (_modes.test(Mode::Origin)) { const auto topMargin = _GetVerticalMargins(viewport, false).first; cursorPosition.Y -= topMargin; @@ -1375,87 +1360,69 @@ bool AdaptDispatch::ScrollDown(const VTInt uiDistance) return true; } -// Routine Description: -// - DECSCPP / DECCOLM Sets the number of columns "per page" AKA sets the console width. -// DECCOLM also clear the screen (like a CSI 2 J sequence), while DECSCPP just sets the width. -// (DECCOLM will do this separately of this function) -// Arguments: -// - columns - Number of columns -// Return Value: -// - True. -bool AdaptDispatch::SetColumns(const VTInt columns) -{ - const auto viewport = _api.GetViewport(); - const auto viewportHeight = viewport.bottom - viewport.top; - _api.ResizeWindow(columns, viewportHeight); - return true; -} - // Routine Description: // - DECCOLM not only sets the number of columns, but also clears the screen buffer, // resets the page margins and origin mode, and places the cursor at 1,1 // Arguments: -// - columns - Number of columns +// - enable - the number of columns is set to 132 if true, 80 if false. // Return Value: -// - True. -bool AdaptDispatch::_DoDECCOLMHelper(const VTInt columns) +// - +void AdaptDispatch::_SetColumnMode(const bool enable) { // Only proceed if DECCOLM is allowed. Return true, as this is technically a successful handling. - if (_isDECCOLMAllowed) + if (_modes.test(Mode::AllowDECCOLM) && !_api.IsConsolePty()) { - SetColumns(columns); - SetOriginMode(false); + const auto viewport = _api.GetViewport(); + const auto viewportHeight = viewport.bottom - viewport.top; + const auto viewportWidth = (enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns); + _api.ResizeWindow(viewportWidth, viewportHeight); + _modes.set(Mode::Column, enable); + _modes.reset(Mode::Origin); CursorPosition(1, 1); EraseInDisplay(DispatchTypes::EraseType::All); _DoSetTopBottomScrollingMargins(0, 0); } - return true; -} - -// Routine Description : -// - Retrieves the various StateMachine parser modes. -// Arguments: -// - mode - the parser mode to query. -// Return Value: -// - true if the mode is enabled. false if disabled. -bool AdaptDispatch::_GetParserMode(const StateMachine::Mode mode) const -{ - return _api.GetStateMachine().GetParserMode(mode); } // Routine Description: -// - Sets the various StateMachine parser modes. +// - Set the alternate screen buffer mode. In virtual terminals, there exists +// both a "main" screen buffer and an alternate. This mode is used to switch +// between the two. // Arguments: -// - mode - the parser mode to change. -// - enable - set to true to enable the mode, false to disable it. +// - enable - true selects the alternate buffer, false returns to the main buffer. // Return Value: // - -void AdaptDispatch::_SetParserMode(const StateMachine::Mode mode, const bool enable) +void AdaptDispatch::_SetAlternateScreenBufferMode(const bool enable) { - _api.GetStateMachine().SetParserMode(mode, enable); + if (enable) + { + CursorSaveState(); + _api.UseAlternateScreenBuffer(); + _usingAltBuffer = true; + } + else + { + _api.UseMainScreenBuffer(); + _usingAltBuffer = false; + CursorRestoreState(); + } } // Routine Description: -// - Sets the various terminal input modes. -// Arguments: -// - mode - the input mode to change. -// - enable - set to true to enable the mode, false to disable it. +// - Determines whether we need to pass through input mode requests. +// If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests +// The VT Input mode check is to work around ssh.exe v7.7, which uses VT +// output, but not Input. +// The original comment said, "Once the conpty supports these types of input, +// this check can be removed. See GH#4911". Unfortunately, time has shown +// us that SSH 7.7 _also_ requests mouse input and that can have a user interface +// impact on the actual connected terminal. We can't remove this check, +// because SSH <=7.7 is out in the wild on all versions of Windows <=2004. // Return Value: -// - true if successful. false otherwise. -bool AdaptDispatch::_SetInputMode(const TerminalInput::Mode mode, const bool enable) +// - True if we should pass through. False otherwise. +bool AdaptDispatch::_PassThroughInputModes() { - _terminalInput.SetInputMode(mode, enable); - - // If we're a conpty, AND WE'RE IN VT INPUT MODE, always pass input mode requests - // The VT Input mode check is to work around ssh.exe v7.7, which uses VT - // output, but not Input. - // The original comment said, "Once the conpty supports these types of input, - // this check can be removed. See GH#4911". Unfortunately, time has shown - // us that SSH 7.7 _also_ requests mouse input and that can have a user interface - // impact on the actual connected terminal. We can't remove this check, - // because SSH <=7.7 is out in the wild on all versions of Windows <=2004. - - return !_api.IsConsolePty() || !_api.IsVtInputEnabled(); + return _api.IsConsolePty() && _api.IsVtInputEnabled(); } // Routine Description: @@ -1467,80 +1434,87 @@ bool AdaptDispatch::_SetInputMode(const TerminalInput::Mode mode, const bool ena // - True if handled successfully. False otherwise. bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable) { - auto success = false; switch (param) { case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: - // set - Enable Application Mode, reset - Normal mode - success = SetCursorKeysMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::CursorKey, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::DECANM_AnsiMode: - success = SetAnsiMode(enable); - break; + return SetAnsiMode(enable); case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns: - success = _DoDECCOLMHelper(enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns); - break; + _SetColumnMode(enable); + return true; case DispatchTypes::ModeParams::DECSCNM_ScreenMode: - success = SetScreenMode(enable); - break; + _renderSettings.SetRenderMode(RenderSettings::Mode::ScreenReversed, enable); + // No need to force a redraw in pty mode. + if (_api.IsConsolePty()) + { + return false; + } + _renderer.TriggerRedrawAll(); + return true; case DispatchTypes::ModeParams::DECOM_OriginMode: + _modes.set(Mode::Origin, enable); // The cursor is also moved to the new home position when the origin mode is set or reset. - success = SetOriginMode(enable) && CursorPosition(1, 1); - break; + CursorPosition(1, 1); + return true; case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: - success = SetAutoWrapMode(enable); - break; + _api.SetAutoWrapMode(enable); + return true; case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: - success = _SetInputMode(TerminalInput::Mode::AutoRepeat, enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::AutoRepeat, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - success = EnableCursorBlinking(enable); - break; + _api.GetTextBuffer().GetCursor().SetBlinkingAllowed(enable); + return !_api.IsConsolePty(); case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - success = CursorVisibility(enable); - break; + _api.GetTextBuffer().GetCursor().SetIsVisible(enable); + return true; case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: - success = EnableDECCOLMSupport(enable); - break; + _modes.set(Mode::AllowDECCOLM, enable); + return true; + case DispatchTypes::ModeParams::DECNKM_NumericKeypadMode: + _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::DECBKM_BackarrowKeyMode: - success = _SetInputMode(TerminalInput::Mode::BackarrowKey, enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::BackarrowKey, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::VT200_MOUSE_MODE: - success = EnableVT200MouseMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::BUTTON_EVENT_MOUSE_MODE: - success = EnableButtonEventMouseMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::ANY_EVENT_MOUSE_MODE: - success = EnableAnyEventMouseMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::UTF8_EXTENDED_MODE: - success = EnableUTF8ExtendedMouseMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::SGR_EXTENDED_MODE: - success = EnableSGRExtendedMouseMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::FOCUS_EVENT_MODE: - success = EnableFocusEventMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::FocusEvent, enable); + // GH#12799 - If the app requested that we disable focus events, DON'T pass + // that through. ConPTY would _always_ like to know about focus events. + return !_PassThroughInputModes() || !enable; case DispatchTypes::ModeParams::ALTERNATE_SCROLL: - success = EnableAlternateScroll(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::AlternateScroll, enable); + return !_PassThroughInputModes(); case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer: - success = enable ? UseAlternateScreenBuffer() : UseMainScreenBuffer(); - break; + _SetAlternateScreenBufferMode(enable); + return true; case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: - success = EnableXtermBracketedPasteMode(enable); - break; + _api.SetBracketedPasteMode(enable); + return !_api.IsConsolePty(); case DispatchTypes::ModeParams::W32IM_Win32InputMode: - success = EnableWin32InputMode(enable); - break; + _terminalInput.SetInputMode(TerminalInput::Mode::Win32, enable); + return !_PassThroughInputModes(); default: // If no functions to call, overall dispatch was a failure. - success = false; - break; + return false; } - return success; } // Routine Description: @@ -1565,57 +1539,117 @@ bool AdaptDispatch::ResetMode(const DispatchTypes::ModeParams param) return _ModeParamsHelper(param, false); } -// - DECKPAM, DECKPNM - Sets the keypad input mode to either Application mode or Numeric mode (true, false respectively) +// Routine Description: +// - DECRQM - Requests the current state of a given mode number. The result +// is reported back with a DECRPM escape sequence. // Arguments: -// - applicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input. +// - param - the mode number being queried // Return Value: // - True if handled successfully. False otherwise. -bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) +bool AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param) { - return _SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode); -} + auto enabled = std::optional{}; -// Method Description: -// - win32-input-mode: Enable sending full input records encoded as a string of -// characters to the client application. -// Arguments: -// - win32InputMode - set to true to enable win32-input-mode, false to disable. -// Return Value: -// - True if handled successfully. False otherwise. -bool AdaptDispatch::EnableWin32InputMode(const bool win32InputMode) -{ - return _SetInputMode(TerminalInput::Mode::Win32, win32InputMode); -} + switch (param) + { + case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::CursorKey); + break; + case DispatchTypes::ModeParams::DECANM_AnsiMode: + enabled = _api.GetStateMachine().GetParserMode(StateMachine::Mode::Ansi); + break; + case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns: + // DECCOLM is not supported in conpty mode + if (!_api.IsConsolePty()) + { + enabled = _modes.test(Mode::Column); + } + break; + case DispatchTypes::ModeParams::DECSCNM_ScreenMode: + enabled = _renderSettings.GetRenderMode(RenderSettings::Mode::ScreenReversed); + break; + case DispatchTypes::ModeParams::DECOM_OriginMode: + enabled = _modes.test(Mode::Origin); + break; + case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: + enabled = _api.GetAutoWrapMode(); + break; + case DispatchTypes::ModeParams::DECARM_AutoRepeatMode: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AutoRepeat); + break; + case DispatchTypes::ModeParams::ATT610_StartCursorBlink: + enabled = _api.GetTextBuffer().GetCursor().IsBlinkingAllowed(); + break; + case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: + enabled = _api.GetTextBuffer().GetCursor().IsVisible(); + break; + case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: + // DECCOLM is not supported in conpty mode + if (!_api.IsConsolePty()) + { + enabled = _modes.test(Mode::AllowDECCOLM); + } + break; + case DispatchTypes::ModeParams::DECNKM_NumericKeypadMode: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Keypad); + break; + case DispatchTypes::ModeParams::DECBKM_BackarrowKeyMode: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::BackarrowKey); + break; + case DispatchTypes::ModeParams::VT200_MOUSE_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking); + break; + case DispatchTypes::ModeParams::BUTTON_EVENT_MOUSE_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking); + break; + case DispatchTypes::ModeParams::ANY_EVENT_MOUSE_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking); + break; + case DispatchTypes::ModeParams::UTF8_EXTENDED_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding); + break; + case DispatchTypes::ModeParams::SGR_EXTENDED_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding); + break; + case DispatchTypes::ModeParams::FOCUS_EVENT_MODE: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::FocusEvent); + break; + case DispatchTypes::ModeParams::ALTERNATE_SCROLL: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll); + break; + case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer: + enabled = _usingAltBuffer; + break; + case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: + enabled = _api.GetBracketedPasteMode(); + break; + case DispatchTypes::ModeParams::W32IM_Win32InputMode: + enabled = _terminalInput.GetInputMode(TerminalInput::Mode::Win32); + break; + default: + enabled = std::nullopt; + break; + } -// - DECCKM - Sets the cursor keys input mode to either Application mode or Normal mode (true, false respectively) -// Arguments: -// - applicationMode - set to true to enable Application Mode Input, false for Normal Mode Input. -// Return Value: -// - True if handled successfully. False otherwise. -bool AdaptDispatch::SetCursorKeysMode(const bool applicationMode) -{ - return _SetInputMode(TerminalInput::Mode::CursorKey, applicationMode); + // 1 indicates the mode is enabled, 2 it's disabled, and 0 it's unsupported + const auto state = enabled.has_value() ? (enabled.value() ? 1 : 2) : 0; + const auto isPrivate = param >= DispatchTypes::DECPrivateMode(0); + const auto prefix = isPrivate ? L"?" : L""; + const auto mode = isPrivate ? param - DispatchTypes::DECPrivateMode(0) : param; + const auto response = wil::str_printf(L"\x1b[%s%d;%d$y", prefix, mode, state); + _api.ReturnResponse(response); + return true; } -// - att610 - Enables or disables the cursor blinking. +// - DECKPAM, DECKPNM - Sets the keypad input mode to either Application mode or Numeric mode (true, false respectively) // Arguments: -// - enable - set to true to enable blinking, false to disable +// - applicationMode - set to true to enable Application Mode Input, false for Numeric Mode Input. // Return Value: // - True if handled successfully. False otherwise. -bool AdaptDispatch::EnableCursorBlinking(const bool enable) +bool AdaptDispatch::SetKeypadMode(const bool fApplicationMode) { - auto& cursor = _api.GetTextBuffer().GetCursor(); - cursor.SetBlinkingAllowed(enable); - - // GH#2642 - From what we've gathered from other terminals, when blinking is - // disabled, the cursor should remain On always, and have the visibility - // controlled by the IsVisible property. So when you do a printf "\e[?12l" - // to disable blinking, the cursor stays stuck On. At this point, only the - // cursor visibility property controls whether the user can see it or not. - // (Yes, the cursor can be On and NOT Visible) - cursor.SetIsOn(true); - - return !_api.IsConsolePty(); + _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, fApplicationMode); + return !_PassThroughInputModes(); } // Routine Description: @@ -1690,58 +1724,11 @@ bool AdaptDispatch::SetAnsiMode(const bool ansiMode) // need to be reset to defaults, even if the mode doesn't actually change. _termOutput = {}; - _SetParserMode(StateMachine::Mode::Ansi, ansiMode); - _SetInputMode(TerminalInput::Mode::Ansi, ansiMode); - - // We don't check the _SetInputMode return value, because we'll never want - // to forward a DECANM mode change over conpty. - return true; -} - -// Routine Description: -// - DECSCNM - Sets the screen mode to either normal or reverse. -// When in reverse screen mode, the background and foreground colors are switched. -// Arguments: -// - reverseMode - set to true to enable reverse screen mode, false for normal mode. -// Return Value: -// - True if handled successfully. False otherwise. -bool AdaptDispatch::SetScreenMode(const bool reverseMode) -{ - // If we're a conpty, always return false - if (_api.IsConsolePty()) - { - return false; - } - _renderSettings.SetRenderMode(RenderSettings::Mode::ScreenReversed, reverseMode); - _renderer.TriggerRedrawAll(); - return true; -} - -// Routine Description: -// - DECOM - Sets the cursor addressing origin mode to relative or absolute. -// When relative, line numbers start at top margin of the user-defined scrolling region. -// When absolute, line numbers are independent of the scrolling region. -// Arguments: -// - relativeMode - set to true to use relative addressing, false for absolute addressing. -// Return Value: -// - True. -bool AdaptDispatch::SetOriginMode(const bool relativeMode) noexcept -{ - _isOriginModeRelative = relativeMode; - return true; -} + _api.GetStateMachine().SetParserMode(StateMachine::Mode::Ansi, ansiMode); + _terminalInput.SetInputMode(TerminalInput::Mode::Ansi, ansiMode); -// Routine Description: -// - DECAWM - Sets the Auto Wrap Mode. -// This controls whether the cursor moves to the beginning of the next row -// when it reaches the end of the current row. -// Arguments: -// - wrapAtEOL - set to true to wrap, false to overwrite the last character. -// Return Value: -// - True. -bool AdaptDispatch::SetAutoWrapMode(const bool wrapAtEOL) -{ - _api.SetAutoWrapMode(wrapAtEOL); + // While input mode changes are often forwarded over conpty, we never want + // to do that for the DECANM mode. return true; } @@ -1916,36 +1903,6 @@ bool AdaptDispatch::SetWindowTitle(std::wstring_view title) return true; } -// - ASBSET - Creates and swaps to the alternate screen buffer. In virtual terminals, there exists both a "main" -// screen buffer and an alternate. ASBSET creates a new alternate, and switches to it. If there is an already -// existing alternate, it is discarded. -// Arguments: -// - None -// Return Value: -// - True. -bool AdaptDispatch::UseAlternateScreenBuffer() -{ - CursorSaveState(); - _api.UseAlternateScreenBuffer(); - _usingAltBuffer = true; - return true; -} - -// Routine Description: -// - ASBRST - From the alternate buffer, returns to the main screen buffer. -// From the main screen buffer, does nothing. The alternate is discarded. -// Arguments: -// - None -// Return Value: -// - True. -bool AdaptDispatch::UseMainScreenBuffer() -{ - _api.UseMainScreenBuffer(); - _usingAltBuffer = false; - CursorRestoreState(); - return true; -} - //Routine Description: // HTS - sets a VT tab stop in the cursor's current column. //Arguments: @@ -2226,7 +2183,7 @@ bool AdaptDispatch::SingleShift(const VTInt gsetNumber) // - True. bool AdaptDispatch::AcceptC1Controls(const bool enabled) { - _SetParserMode(StateMachine::Mode::AcceptC1, enabled); + _api.GetStateMachine().SetParserMode(StateMachine::Mode::AcceptC1, enabled); return true; } @@ -2263,11 +2220,11 @@ bool AdaptDispatch::AcceptC1Controls(const bool enabled) // True if handled successfully. False otherwise. bool AdaptDispatch::SoftReset() { - CursorVisibility(true); // Cursor enabled. - SetOriginMode(false); // Absolute cursor addressing. - SetAutoWrapMode(true); // Wrap at end of line. - SetCursorKeysMode(false); // Normal characters. - SetKeypadMode(false); // Numeric characters. + _api.GetTextBuffer().GetCursor().SetIsVisible(true); // Cursor enabled. + _modes.reset(Mode::Origin); // Absolute cursor addressing. + _api.SetAutoWrapMode(true); // Wrap at end of line. + _terminalInput.SetInputMode(TerminalInput::Mode::CursorKey, false); // Normal characters. + _terminalInput.SetInputMode(TerminalInput::Mode::Keypad, false); // Numeric characters. // Top margin = 1; bottom margin = page length. _DoSetTopBottomScrollingMargins(0, 0); @@ -2333,20 +2290,19 @@ bool AdaptDispatch::HardReset() EraseInDisplay(DispatchTypes::EraseType::Scrollback); // Set the DECSCNM screen mode back to normal. - SetScreenMode(false); + _renderSettings.SetRenderMode(RenderSettings::Mode::ScreenReversed, false); // Cursor to 1,1 - the Soft Reset guarantees this is absolute CursorPosition(1, 1); - // Reset the mouse mode - EnableSGRExtendedMouseMode(false); - EnableAnyEventMouseMode(false); + // Reset input modes to their initial state + _terminalInput.ResetInputModes(); - // Reset the Backarrow Key mode - _SetInputMode(TerminalInput::Mode::BackarrowKey, false); + // Reset bracketed paste mode + _api.SetBracketedPasteMode(false); - // Set the keyboard Auto Repeat mode - _SetInputMode(TerminalInput::Mode::AutoRepeat, true); + // Restore cursor blinking mode. + _api.GetTextBuffer().GetCursor().SetBlinkingAllowed(true); // Delete all current tab stops and reapply _ResetTabStops(); @@ -2355,8 +2311,8 @@ bool AdaptDispatch::HardReset() _renderer.UpdateSoftFont({}, {}, false); _fontBuffer = nullptr; - // Reset the attribute change extent (DECSACE) to a stream. - _isChangeExtentRectangular = false; + // Reset internal modes to their initial state + _modes = {}; // GH#2715 - If all this succeeded, but we're in a conpty, return `false` to // make the state machine propagate this RIS sequence to the connected @@ -2388,7 +2344,7 @@ bool AdaptDispatch::ScreenAlignmentPattern() attr.SetStandardErase(); _api.SetTextAttributes(attr); // Reset the origin mode to absolute. - SetOriginMode(false); + _modes.reset(Mode::Origin); // Clear the scrolling margins. _DoSetTopBottomScrollingMargins(0, 0); // Set the cursor position to home. @@ -2484,122 +2440,6 @@ void AdaptDispatch::_EraseAll() textBuffer.ResetLineRenditionRange(newViewportTop, newViewportBottom); } -// Routine Description: -// - Enables or disables support for the DECCOLM escape sequence. -// Arguments: -// - enabled - set to true to allow DECCOLM to be used, false to disallow. -// Return Value: -// - True. -bool AdaptDispatch::EnableDECCOLMSupport(const bool enabled) noexcept -{ - _isDECCOLMAllowed = enabled; - return true; -} - -//Routine Description: -// Enable VT200 Mouse Mode - Enables/disables the mouse input handler in default tracking mode. -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableVT200MouseMode(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::DefaultMouseTracking, enabled); -} - -//Routine Description: -// Enable UTF-8 Extended Encoding - this changes the encoding scheme for sequences -// emitted by the mouse input handler. Does not enable/disable mouse mode on its own. -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableUTF8ExtendedMouseMode(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, enabled); -} - -//Routine Description: -// Enable SGR Extended Encoding - this changes the encoding scheme for sequences -// emitted by the mouse input handler. Does not enable/disable mouse mode on its own. -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableSGRExtendedMouseMode(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::SgrMouseEncoding, enabled); -} - -//Routine Description: -// Enable Button Event mode - send mouse move events WITH A BUTTON PRESSED to the input. -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableButtonEventMouseMode(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, enabled); -} - -//Routine Description: -// Enable Any Event mode - send all mouse events to the input. -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableAnyEventMouseMode(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, enabled); -} - -// Method Description: -// - Enables/disables focus event mode. A client may enable this if they want to -// receive focus events. -// - ConPTY always enables this mode and never disables it. Internally, we'll -// always set this mode, but conpty will never request this to be disabled by -// the hosting terminal. -// Arguments: -// - enabled - true to enable, false to disable. -// Return Value: -// - True if handled successfully. False otherwise. -bool AdaptDispatch::EnableFocusEventMode(const bool enabled) -{ - // GH#12799 - If the app requested that we disable focus events, DON'T pass - // that through. ConPTY would _always_ like to know about focus events. - return _SetInputMode(TerminalInput::Mode::FocusEvent, enabled) || !enabled; -} - -//Routine Description: -// Enable Alternate Scroll Mode - When in the Alt Buffer, send CUP and CUD on -// scroll up/down events instead of the usual sequences -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableAlternateScroll(const bool enabled) -{ - return _SetInputMode(TerminalInput::Mode::AlternateScroll, enabled); -} - -//Routine Description: -// Enable "bracketed paste mode". -//Arguments: -// - enabled - true to enable, false to disable. -// Return value: -// True if handled successfully. False otherwise. -bool AdaptDispatch::EnableXtermBracketedPasteMode(const bool enabled) -{ - // Return false to forward the operation to the hosting terminal, - // since ConPTY can't handle this itself. - if (_api.IsConsolePty()) - { - return false; - } - _api.EnableXtermBracketedPasteMode(enabled); - return true; -} - //Routine Description: // Set Cursor Style - Changes the cursor's style to match the given Dispatch // cursor style. Unix styles are a combination of the shape and the blinking state. @@ -2653,7 +2493,6 @@ bool AdaptDispatch::SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) auto& cursor = _api.GetTextBuffer().GetCursor(); cursor.SetType(actualType); cursor.SetBlinkingAllowed(fEnableBlinking); - cursor.SetIsOn(true); // If we're a conpty, always return false, so that this cursor state will be // sent to the connected terminal @@ -3435,7 +3274,7 @@ void AdaptDispatch::_ReportDECSACESetting() const response.append(L"\033P1$r"sv); const auto attr = _api.GetTextBuffer().GetCurrentAttributes(); - response.append(_isChangeExtentRectangular ? L"2"sv : L"1"sv); + response.append(_modes.test(Mode::RectangularChangeExtent) ? L"2"sv : L"1"sv); // The '*x' indicates this is an DECSACE response, and ST ends the sequence. response.append(L"*x\033\\"sv); diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index aa233ce145d..ce19f1143b3 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -47,7 +47,6 @@ namespace Microsoft::Console::VirtualTerminal bool CursorPosition(const VTInt line, const VTInt column) override; // CUP, HVP bool CursorSaveState() override; // DECSC bool CursorRestoreState() override; // DECRC - bool CursorVisibility(const bool isVisible) override; // DECTCEM bool EraseInDisplay(const DispatchTypes::EraseType eraseType) override; // ED bool EraseInLine(const DispatchTypes::EraseType eraseType) override; // EL bool EraseCharacters(const VTInt numChars) override; // ECH @@ -77,17 +76,11 @@ namespace Microsoft::Console::VirtualTerminal bool ScrollDown(const VTInt distance) override; // SD bool InsertLine(const VTInt distance) override; // IL bool DeleteLine(const VTInt distance) override; // DL - bool SetColumns(const VTInt columns) override; // DECCOLM bool SetMode(const DispatchTypes::ModeParams param) override; // DECSET bool ResetMode(const DispatchTypes::ModeParams param) override; // DECRST - bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM + bool RequestMode(const DispatchTypes::ModeParams param) override; // DECRQM bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM - bool EnableWin32InputMode(const bool win32InputMode) override; // win32-input-mode - bool EnableCursorBlinking(const bool enable) override; // ATT610 bool SetAnsiMode(const bool ansiMode) override; // DECANM - bool SetScreenMode(const bool reverseMode) override; // DECSCNM - bool SetOriginMode(const bool relativeMode) noexcept override; // DECOM - bool SetAutoWrapMode(const bool wrapAtEOL) override; // DECAWM bool SetTopBottomScrollingMargins(const VTInt topMargin, const VTInt bottomMargin) override; // DECSTBM bool WarningBell() override; // BEL @@ -95,8 +88,6 @@ namespace Microsoft::Console::VirtualTerminal 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 UseAlternateScreenBuffer() override; // ASBSET - bool UseMainScreenBuffer() override; // ASBRST bool HorizontalTabSet() override; // HTS bool ForwardTab(const VTInt numTabs) override; // CHT, HT bool BackwardsTab(const VTInt numTabs) override; // CBT @@ -111,15 +102,6 @@ namespace Microsoft::Console::VirtualTerminal bool SoftReset() override; // DECSTR bool HardReset() override; // RIS bool ScreenAlignmentPattern() override; // DECALN - bool EnableDECCOLMSupport(const bool enabled) noexcept override; // ?40 - bool EnableVT200MouseMode(const bool enabled) override; // ?1000 - bool EnableUTF8ExtendedMouseMode(const bool enabled) override; // ?1005 - bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006 - bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002 - bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003 - bool EnableFocusEventMode(const bool enabled) override; // ?1004 - bool EnableAlternateScroll(const bool enabled) override; // ?1007 - bool EnableXtermBracketedPasteMode(const bool enabled) override; // ?2004 bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR bool SetCursorColor(const COLORREF cursorColor) override; @@ -160,6 +142,13 @@ namespace Microsoft::Console::VirtualTerminal bool PlaySounds(const VTParameters parameters) override; // DECPS private: + enum class Mode + { + Origin, + Column, + AllowDECCOLM, + RectangularChangeExtent + }; enum class ScrollDirection { Up, @@ -214,11 +203,10 @@ namespace Microsoft::Console::VirtualTerminal void _OperatingStatus() const; void _CursorPositionReport(const bool extendedReport); - bool _GetParserMode(const StateMachine::Mode mode) const; - void _SetParserMode(const StateMachine::Mode mode, const bool enable); - bool _SetInputMode(const TerminalInput::Mode mode, const bool enable); + void _SetColumnMode(const bool enable); + void _SetAlternateScreenBufferMode(const bool enable); + bool _PassThroughInputModes(); bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable); - bool _DoDECCOLMHelper(const VTInt columns); void _ClearSingleTabStop(); void _ClearAllTabStops() noexcept; @@ -256,9 +244,7 @@ namespace Microsoft::Console::VirtualTerminal til::inclusive_rect _scrollMargins; - bool _isOriginModeRelative; - bool _isDECCOLMAllowed; - bool _isChangeExtentRectangular; + til::enumset _modes; SgrStack _sgrStack; diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index 683bd14e621..25ba2b22a7a 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -38,43 +38,24 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool CursorPosition(const VTInt /*line*/, const VTInt /*column*/) override { return false; } // CUP, HVP bool CursorSaveState() override { return false; } // DECSC bool CursorRestoreState() override { return false; } // DECRC - bool CursorVisibility(const bool /*isVisible*/) override { return false; } // DECTCEM bool InsertCharacter(const VTInt /*count*/) override { return false; } // ICH bool DeleteCharacter(const VTInt /*count*/) override { return false; } // DCH bool ScrollUp(const VTInt /*distance*/) override { return false; } // SU bool ScrollDown(const VTInt /*distance*/) override { return false; } // SD bool InsertLine(const VTInt /*distance*/) override { return false; } // IL bool DeleteLine(const VTInt /*distance*/) override { return false; } // DL - bool SetColumns(const VTInt /*columns*/) override { return false; } // DECCOLM - bool SetCursorKeysMode(const bool /*applicationMode*/) override { return false; } // DECCKM bool SetKeypadMode(const bool /*applicationMode*/) override { return false; } // DECKPAM, DECKPNM - bool EnableWin32InputMode(const bool /*win32InputMode*/) override { return false; } // win32-input-mode - bool EnableCursorBlinking(const bool /*enable*/) override { return false; } // ATT610 bool SetAnsiMode(const bool /*ansiMode*/) override { return false; } // DECANM - bool SetScreenMode(const bool /*reverseMode*/) override { return false; } // DECSCNM - bool SetOriginMode(const bool /*relativeMode*/) override { return false; }; // DECOM - bool SetAutoWrapMode(const bool /*wrapAtEOL*/) override { return false; }; // DECAWM bool SetTopBottomScrollingMargins(const VTInt /*topMargin*/, const VTInt /*bottomMargin*/) override { return false; } // DECSTBM bool WarningBell() override { return false; } // BEL bool CarriageReturn() override { return false; } // CR bool LineFeed(const DispatchTypes::LineFeedType /*lineFeedType*/) override { return false; } // IND, NEL, LF, FF, VT bool ReverseLineFeed() override { return false; } // RI bool SetWindowTitle(std::wstring_view /*title*/) override { return false; } // OscWindowTitle - bool UseAlternateScreenBuffer() override { return false; } // ASBSET - bool UseMainScreenBuffer() override { return false; } // ASBRST bool HorizontalTabSet() override { return false; } // HTS bool ForwardTab(const VTInt /*numTabs*/) override { return false; } // CHT, HT bool BackwardsTab(const VTInt /*numTabs*/) override { return false; } // CBT bool TabClear(const DispatchTypes::TabClearType /*clearType*/) override { return false; } // TBC - bool EnableDECCOLMSupport(const bool /*enabled*/) override { return false; } // ?40 - bool EnableVT200MouseMode(const bool /*enabled*/) override { return false; } // ?1000 - bool EnableUTF8ExtendedMouseMode(const bool /*enabled*/) override { return false; } // ?1005 - bool EnableSGRExtendedMouseMode(const bool /*enabled*/) override { return false; } // ?1006 - bool EnableButtonEventMouseMode(const bool /*enabled*/) override { return false; } // ?1002 - bool EnableAnyEventMouseMode(const bool /*enabled*/) override { return false; } // ?1003 - bool EnableFocusEventMode(const bool /*enabled*/) override { return false; } // ?1004 - bool EnableAlternateScroll(const bool /*enabled*/) override { return false; } // ?1007 - bool EnableXtermBracketedPasteMode(const bool /*enabled*/) override { return false; } // ?2004 bool SetColorTableEntry(const size_t /*tableIndex*/, const DWORD /*color*/) override { return false; } // OSCColorTable bool SetDefaultForeground(const DWORD /*color*/) override { return false; } // OSCDefaultForeground bool SetDefaultBackground(const DWORD /*color*/) override { return false; } // OSCDefaultBackground @@ -102,8 +83,8 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool PopGraphicsRendition() override { return false; } // XTPOPSGR bool SetMode(const DispatchTypes::ModeParams /*param*/) override { return false; } // DECSET - bool ResetMode(const DispatchTypes::ModeParams /*param*/) override { return false; } // DECRST + bool RequestMode(const DispatchTypes::ModeParams /*param*/) override { return false; } // DECRQM bool DeviceStatusReport(const DispatchTypes::StatusType /*statusType*/) override { return false; } // DSR, DSR-OS, DSR-CPR bool DeviceAttributes() override { return false; } // DA1 diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 74e1381328c..8ecb44d6f80 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -105,6 +105,12 @@ class TestGetSet final : public ITerminalApi Log::Comment(L"SetAutoWrapMode MOCK called..."); } + bool GetAutoWrapMode() const override + { + Log::Comment(L"GetAutoWrapMode MOCK called..."); + return true; + } + bool IsVtInputEnabled() const override { return false; @@ -201,9 +207,15 @@ class TestGetSet final : public ITerminalApi return _expectedOutputCP; } - void EnableXtermBracketedPasteMode(const bool /*enabled*/) + void SetBracketedPasteMode(const bool /*enabled*/) override + { + Log::Comment(L"SetBracketedPasteMode MOCK called..."); + } + + std::optional GetBracketedPasteMode() const override { - Log::Comment(L"EnableXtermBracketedPasteMode MOCK called..."); + Log::Comment(L"GetBracketedPasteMode MOCK called..."); + return {}; } void CopyToClipboard(const std::wstring_view /*content*/) @@ -772,7 +784,14 @@ class AdapterTest Log::Comment(L"Verify successful API call modifies visibility state."); _testGetSet->PrepData(); _testGetSet->_textBuffer->GetCursor().SetIsVisible(fStart); - VERIFY_IS_TRUE(_pDispatch->CursorVisibility(fEnd)); + if (fEnd) + { + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::DECTCEM_TextCursorEnableMode)); + } + else + { + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::DECTCEM_TextCursorEnableMode)); + } VERIFY_ARE_EQUAL(fEnd, _testGetSet->_textBuffer->GetCursor().IsVisible()); } @@ -1665,6 +1684,45 @@ class AdapterTest _testGetSet->ValidateInputEvent(L"\033P0$r\033\\"); } + TEST_METHOD(RequestModeTests) + { + // The mode numbers below correspond to the DECPrivateMode values + // in the ModeParams enum in DispatchTypes.hpp. We don't include + // AnsiMode (2), because once that's disabled we'd be in VT52 mode, + // and DECRQM would not then be applicable. + + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{1, 3, 5, 6, 8, 12, 25, 40, 66, 67, 1000, 1002, 1003, 1004, 1005, 1006, 1007, 1049, 9001}") + END_TEST_METHOD_PROPERTIES() + + VTInt modeNumber; + VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"modeNumber", modeNumber)); + const auto mode = DispatchTypes::DECPrivateMode(modeNumber); + + if (mode == DispatchTypes::DECCOLM_SetNumberOfColumns) + { + Log::Comment(L"Make sure DECCOLM is allowed"); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::XTERM_EnableDECCOLMSupport)); + } + + Log::Comment(NoThrowString().Format(L"Setting private mode %d", modeNumber)); + _testGetSet->PrepData(); + VERIFY_IS_TRUE(_pDispatch->SetMode(mode)); + VERIFY_IS_TRUE(_pDispatch->RequestMode(mode)); + + wchar_t expectedResponse[20]; + swprintf_s(expectedResponse, ARRAYSIZE(expectedResponse), L"\x1b[?%d;1$y", modeNumber); + _testGetSet->ValidateInputEvent(expectedResponse); + + Log::Comment(NoThrowString().Format(L"Resetting private mode %d", modeNumber)); + _testGetSet->PrepData(); + VERIFY_IS_TRUE(_pDispatch->ResetMode(mode)); + VERIFY_IS_TRUE(_pDispatch->RequestMode(mode)); + + swprintf_s(expectedResponse, ARRAYSIZE(expectedResponse), L"\x1b[?%d;2$y", modeNumber); + _testGetSet->ValidateInputEvent(expectedResponse); + } + TEST_METHOD(CursorKeysModeTest) { Log::Comment(L"Starting test..."); @@ -1673,12 +1731,12 @@ class AdapterTest // success cases // set numeric mode = true Log::Comment(L"Test 1: application mode = false"); - VERIFY_IS_TRUE(_pDispatch->SetCursorKeysMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::DECCKM_CursorKeysMode)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::CursorKey)); // set numeric mode = false Log::Comment(L"Test 2: application mode = true"); - VERIFY_IS_TRUE(_pDispatch->SetCursorKeysMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::DECCKM_CursorKeysMode)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::CursorKey)); } @@ -1726,13 +1784,13 @@ class AdapterTest // set blinking mode = true Log::Comment(L"Test 1: enable blinking = true"); _testGetSet->_textBuffer->GetCursor().SetBlinkingAllowed(false); - VERIFY_IS_TRUE(_pDispatch->EnableCursorBlinking(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::ATT610_StartCursorBlink)); VERIFY_IS_TRUE(_testGetSet->_textBuffer->GetCursor().IsBlinkingAllowed()); // set blinking mode = false Log::Comment(L"Test 2: enable blinking = false"); _testGetSet->_textBuffer->GetCursor().SetBlinkingAllowed(true); - VERIFY_IS_TRUE(_pDispatch->EnableCursorBlinking(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::ATT610_StartCursorBlink)); VERIFY_IS_FALSE(_testGetSet->_textBuffer->GetCursor().IsBlinkingAllowed()); } @@ -1885,44 +1943,44 @@ class AdapterTest Log::Comment(L"Test 1: Test Default Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::DefaultMouseTracking, false); - VERIFY_IS_TRUE(_pDispatch->EnableVT200MouseMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::VT200_MOUSE_MODE)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking)); - VERIFY_IS_TRUE(_pDispatch->EnableVT200MouseMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::VT200_MOUSE_MODE)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::DefaultMouseTracking)); Log::Comment(L"Test 2: Test UTF-8 Extended Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::Utf8MouseEncoding, false); - VERIFY_IS_TRUE(_pDispatch->EnableUTF8ExtendedMouseMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::UTF8_EXTENDED_MODE)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding)); - VERIFY_IS_TRUE(_pDispatch->EnableUTF8ExtendedMouseMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::UTF8_EXTENDED_MODE)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::Utf8MouseEncoding)); Log::Comment(L"Test 3: Test SGR Extended Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::SgrMouseEncoding, false); - VERIFY_IS_TRUE(_pDispatch->EnableSGRExtendedMouseMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::SGR_EXTENDED_MODE)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding)); - VERIFY_IS_TRUE(_pDispatch->EnableSGRExtendedMouseMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::SGR_EXTENDED_MODE)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::SgrMouseEncoding)); Log::Comment(L"Test 4: Test Button-Event Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::ButtonEventMouseTracking, false); - VERIFY_IS_TRUE(_pDispatch->EnableButtonEventMouseMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::BUTTON_EVENT_MOUSE_MODE)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking)); - VERIFY_IS_TRUE(_pDispatch->EnableButtonEventMouseMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::BUTTON_EVENT_MOUSE_MODE)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::ButtonEventMouseTracking)); Log::Comment(L"Test 5: Test Any-Event Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::AnyEventMouseTracking, false); - VERIFY_IS_TRUE(_pDispatch->EnableAnyEventMouseMode(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::ANY_EVENT_MOUSE_MODE)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking)); - VERIFY_IS_TRUE(_pDispatch->EnableAnyEventMouseMode(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::ANY_EVENT_MOUSE_MODE)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::AnyEventMouseTracking)); Log::Comment(L"Test 6: Test Alt Scroll Mouse Mode"); _terminalInput.SetInputMode(TerminalInput::Mode::AlternateScroll, false); - VERIFY_IS_TRUE(_pDispatch->EnableAlternateScroll(true)); + VERIFY_IS_TRUE(_pDispatch->SetMode(DispatchTypes::ALTERNATE_SCROLL)); VERIFY_IS_TRUE(_terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll)); - VERIFY_IS_TRUE(_pDispatch->EnableAlternateScroll(false)); + VERIFY_IS_TRUE(_pDispatch->ResetMode(DispatchTypes::ALTERNATE_SCROLL)); VERIFY_IS_FALSE(_terminalInput.GetInputMode(TerminalInput::Mode::AlternateScroll)); } diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index f712dc611ce..db3d3fd805c 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -266,6 +266,13 @@ bool TerminalInput::GetInputMode(const Mode mode) const noexcept return _inputMode.test(mode); } +void TerminalInput::ResetInputModes() noexcept +{ + _inputMode = { Mode::Ansi, Mode::AutoRepeat }; + _mouseInputState.lastPos = { -1, -1 }; + _mouseInputState.lastButton = 0; +} + void TerminalInput::ForceDisableWin32InputMode(const bool win32InputMode) noexcept { _forceDisableWin32InputMode = win32InputMode; diff --git a/src/terminal/input/terminalInput.hpp b/src/terminal/input/terminalInput.hpp index 7df69b41f53..41c91072c8b 100644 --- a/src/terminal/input/terminalInput.hpp +++ b/src/terminal/input/terminalInput.hpp @@ -59,6 +59,7 @@ namespace Microsoft::Console::VirtualTerminal void SetInputMode(const Mode mode, const bool enabled) noexcept; bool GetInputMode(const Mode mode) const noexcept; + void ResetInputModes() noexcept; void ForceDisableWin32InputMode(const bool win32InputMode) noexcept; #pragma region MouseInput diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index c7fa93a2bbf..3c2de8aee73 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -636,6 +636,14 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete success = _dispatch->PopGraphicsRendition(); TermTelemetry::Instance().Log(TermTelemetry::Codes::XTPOPSGR); break; + case CsiActionCodes::DECRQM_RequestMode: + success = _dispatch->RequestMode(DispatchTypes::ANSIStandardMode(parameters.at(0))); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRQM); + break; + case CsiActionCodes::DECRQM_PrivateRequestMode: + success = _dispatch->RequestMode(DispatchTypes::DECPrivateMode(parameters.at(0))); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRQM); + break; case CsiActionCodes::DECCARA_ChangeAttributesRectangularArea: success = _dispatch->ChangeAttributesRectangularArea(parameters.at(0), parameters.at(1), parameters.at(2).value_or(0), parameters.at(3).value_or(0), parameters.subspan(4)); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECCARA); diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index b125c7b714e..cdbed0fed35 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -147,6 +147,8 @@ namespace Microsoft::Console::VirtualTerminal XT_PopSgrAlias = VTID("#q"), XT_PushSgr = VTID("#{"), XT_PopSgr = VTID("#}"), + DECRQM_RequestMode = VTID("$p"), + DECRQM_PrivateRequestMode = VTID("?$p"), DECCARA_ChangeAttributesRectangularArea = VTID("$r"), DECRARA_ReverseAttributesRectangularArea = VTID("$t"), DECCRA_CopyRectangularArea = VTID("$v"), diff --git a/src/terminal/parser/telemetry.cpp b/src/terminal/parser/telemetry.cpp index 4812fb77544..05b7ab20dcd 100644 --- a/src/terminal/parser/telemetry.cpp +++ b/src/terminal/parser/telemetry.cpp @@ -283,6 +283,7 @@ void TermTelemetry::WriteFinalTraceLog() const TraceLoggingUInt32(_uiTimesUsed[DECALN], "DECALN"), TraceLoggingUInt32(_uiTimesUsed[XTPUSHSGR], "XTPUSHSGR"), TraceLoggingUInt32(_uiTimesUsed[XTPOPSGR], "XTPOPSGR"), + TraceLoggingUInt32(_uiTimesUsed[DECRQM], "DECRQM"), TraceLoggingUInt32(_uiTimesUsed[DECCARA], "DECCARA"), TraceLoggingUInt32(_uiTimesUsed[DECRARA], "DECRARA"), TraceLoggingUInt32(_uiTimesUsed[DECCRA], "DECCRA"), diff --git a/src/terminal/parser/telemetry.hpp b/src/terminal/parser/telemetry.hpp index 283a163402b..432fd56d3c1 100644 --- a/src/terminal/parser/telemetry.hpp +++ b/src/terminal/parser/telemetry.hpp @@ -110,6 +110,7 @@ namespace Microsoft::Console::VirtualTerminal OSCSCB, XTPUSHSGR, XTPOPSGR, + DECRQM, DECCARA, DECRARA, DECCRA, diff --git a/src/terminal/parser/ut_parser/OutputEngineTest.cpp b/src/terminal/parser/ut_parser/OutputEngineTest.cpp index 12246eb14d1..5cfe3e311b7 100644 --- a/src/terminal/parser/ut_parser/OutputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/OutputEngineTest.cpp @@ -1009,7 +1009,6 @@ class StatefulDispatch final : public TermDispatch _cursorPosition{ false }, _cursorSave{ false }, _cursorLoad{ false }, - _cursorVisible{ true }, _eraseDisplay{ false }, _eraseLine{ false }, _insertCharacter{ false }, @@ -1025,13 +1024,9 @@ class StatefulDispatch final : public TermDispatch _vt52DeviceAttributes{ false }, _requestTerminalParameters{ false }, _reportingPermission{ (DispatchTypes::ReportingPermission)-1 }, - _isAltBuffer{ false }, - _cursorKeysMode{ false }, - _cursorBlinking{ true }, - _isInAnsiMode{ true }, - _isScreenModeReversed{ false }, - _isOriginModeRelative{ false }, - _isAutoWrapEnabled{ true }, + _modeType{ (DispatchTypes::ModeParams)-1 }, + _modeTypes{}, + _modeEnabled{ false }, _warningBell{ false }, _carriageReturn{ false }, _lineFeed{ false }, @@ -1041,10 +1036,6 @@ class StatefulDispatch final : public TermDispatch _numTabs{ 0 }, _tabClear{ false }, _tabClearTypes{}, - _isDECCOLMAllowed{ false }, - _windowWidth{ 80 }, - _bracketedPasteMode{ false }, - _win32InputMode{ false }, _setDefaultForeground(false), _defaultForegroundColor{ RGB(0, 0, 0) }, _setDefaultBackground(false), @@ -1184,12 +1175,6 @@ class StatefulDispatch final : public TermDispatch return true; } - bool CursorVisibility(const bool fIsVisible) noexcept override - { - _cursorVisible = fIsVisible; - return true; - } - bool SetGraphicsRendition(const VTParameters options) noexcept override try { @@ -1249,53 +1234,11 @@ class StatefulDispatch final : public TermDispatch bool _ModeParamsHelper(_In_ DispatchTypes::ModeParams const param, const bool fEnable) { - auto fSuccess = false; - switch (param) - { - case DispatchTypes::ModeParams::DECCKM_CursorKeysMode: - // set - Enable Application Mode, reset - Numeric/normal mode - fSuccess = SetVirtualTerminalInputMode(fEnable); - break; - case DispatchTypes::ModeParams::DECANM_AnsiMode: - fSuccess = SetAnsiMode(fEnable); - break; - case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns: - fSuccess = SetColumns(static_cast(fEnable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns)); - break; - case DispatchTypes::ModeParams::DECSCNM_ScreenMode: - fSuccess = SetScreenMode(fEnable); - break; - case DispatchTypes::ModeParams::DECOM_OriginMode: - // The cursor is also moved to the new home position when the origin mode is set or reset. - fSuccess = SetOriginMode(fEnable) && CursorPosition(1, 1); - break; - case DispatchTypes::ModeParams::DECAWM_AutoWrapMode: - fSuccess = SetAutoWrapMode(fEnable); - break; - case DispatchTypes::ModeParams::ATT610_StartCursorBlink: - fSuccess = EnableCursorBlinking(fEnable); - break; - case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode: - fSuccess = CursorVisibility(fEnable); - break; - case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport: - fSuccess = EnableDECCOLMSupport(fEnable); - break; - case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer: - fSuccess = fEnable ? UseAlternateScreenBuffer() : UseMainScreenBuffer(); - break; - case DispatchTypes::ModeParams::XTERM_BracketedPasteMode: - fSuccess = EnableXtermBracketedPasteMode(fEnable); - break; - case DispatchTypes::ModeParams::W32IM_Win32InputMode: - fSuccess = EnableWin32InputMode(fEnable); - break; - default: - // If no functions to call, overall dispatch was a failure. - fSuccess = false; - break; - } - return fSuccess; + _modeType = param; + _modeTypes.push_back(param); + _modeEnabled = fEnable; + + return true; } bool SetMode(const DispatchTypes::ModeParams param) noexcept override @@ -1308,57 +1251,9 @@ class StatefulDispatch final : public TermDispatch return _ModeParamsHelper(param, false); } - bool SetColumns(const VTInt uiColumns) noexcept override - { - _windowWidth = uiColumns; - return true; - } - - bool SetVirtualTerminalInputMode(const bool fApplicationMode) noexcept - { - _cursorKeysMode = fApplicationMode; - return true; - } - - bool EnableXtermBracketedPasteMode(const bool enable) noexcept - { - _bracketedPasteMode = enable; - return true; - } - - bool EnableWin32InputMode(const bool enable) noexcept - { - _win32InputMode = enable; - return true; - } - - bool EnableCursorBlinking(const bool bEnable) noexcept override - { - _cursorBlinking = bEnable; - return true; - } - bool SetAnsiMode(const bool ansiMode) noexcept override { - _isInAnsiMode = ansiMode; - return true; - } - - bool SetScreenMode(const bool reverseMode) noexcept override - { - _isScreenModeReversed = reverseMode; - return true; - } - - bool SetOriginMode(const bool fRelativeMode) noexcept override - { - _isOriginModeRelative = fRelativeMode; - return true; - } - - bool SetAutoWrapMode(const bool wrapAtEOL) noexcept override - { - _isAutoWrapEnabled = wrapAtEOL; + _ModeParamsHelper(DispatchTypes::DECANM_AnsiMode, ansiMode); return true; } @@ -1400,25 +1295,6 @@ class StatefulDispatch final : public TermDispatch _tabClearTypes.push_back(clearType); return true; } - - bool EnableDECCOLMSupport(const bool fEnabled) noexcept override - { - _isDECCOLMAllowed = fEnabled; - return true; - } - - bool UseAlternateScreenBuffer() noexcept override - { - _isAltBuffer = true; - return true; - } - - bool UseMainScreenBuffer() noexcept override - { - _isAltBuffer = false; - return true; - } - bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override { _setColorTableEntry = true; @@ -1494,7 +1370,6 @@ class StatefulDispatch final : public TermDispatch bool _cursorPosition; bool _cursorSave; bool _cursorLoad; - bool _cursorVisible; bool _eraseDisplay; bool _eraseLine; bool _insertCharacter; @@ -1510,13 +1385,9 @@ class StatefulDispatch final : public TermDispatch bool _vt52DeviceAttributes; bool _requestTerminalParameters; DispatchTypes::ReportingPermission _reportingPermission; - bool _isAltBuffer; - bool _cursorKeysMode; - bool _cursorBlinking; - bool _isInAnsiMode; - bool _isScreenModeReversed; - bool _isOriginModeRelative; - bool _isAutoWrapEnabled; + DispatchTypes::ModeParams _modeType; + std::vector _modeTypes; + bool _modeEnabled; bool _warningBell; bool _carriageReturn; bool _lineFeed; @@ -1526,10 +1397,6 @@ class StatefulDispatch final : public TermDispatch size_t _numTabs; bool _tabClear; std::vector _tabClearTypes; - bool _isDECCOLMAllowed; - size_t _windowWidth; - bool _bracketedPasteMode; - bool _win32InputMode; bool _setDefaultForeground; DWORD _defaultForegroundColor; bool _setDefaultBackground; @@ -1805,24 +1672,6 @@ class StateMachineExternalTest final pDispatch->ClearState(); } - TEST_METHOD(TestCursorKeysMode) - { - 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[?1h"); - VERIFY_IS_TRUE(pDispatch->_cursorKeysMode); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?1l"); - VERIFY_IS_FALSE(pDispatch->_cursorKeysMode); - - pDispatch->ClearState(); - } - TEST_METHOD(TestAnsiMode) { auto dispatch = std::make_unique(); @@ -1830,190 +1679,49 @@ class StateMachineExternalTest final auto engine = std::make_unique(std::move(dispatch)); StateMachine mach(std::move(engine)); + pDispatch->_modeEnabled = true; mach.ProcessString(L"\x1b[?2l"); - VERIFY_IS_FALSE(pDispatch->_isInAnsiMode); + VERIFY_ARE_EQUAL(DispatchTypes::DECANM_AnsiMode, pDispatch->_modeType); + VERIFY_IS_FALSE(pDispatch->_modeEnabled); pDispatch->ClearState(); - pDispatch->_isInAnsiMode = false; mach.SetParserMode(StateMachine::Mode::Ansi, false); mach.ProcessString(L"\x1b<"); - VERIFY_IS_TRUE(pDispatch->_isInAnsiMode); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestSetNumberOfColumns) - { - 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[?3h"); - VERIFY_ARE_EQUAL(pDispatch->_windowWidth, static_cast(DispatchTypes::s_sDECCOLMSetColumns)); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?3l"); - VERIFY_ARE_EQUAL(pDispatch->_windowWidth, static_cast(DispatchTypes::s_sDECCOLMResetColumns)); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestScreenMode) - { - 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[?5h"); - VERIFY_IS_TRUE(pDispatch->_isScreenModeReversed); - - pDispatch->ClearState(); - pDispatch->_isScreenModeReversed = true; - - mach.ProcessString(L"\x1b[?5l"); - VERIFY_IS_FALSE(pDispatch->_isScreenModeReversed); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestOriginMode) - { - 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[?6h"); - VERIFY_IS_TRUE(pDispatch->_isOriginModeRelative); - VERIFY_IS_TRUE(pDispatch->_cursorPosition); - VERIFY_ARE_EQUAL(pDispatch->_line, 1u); - VERIFY_ARE_EQUAL(pDispatch->_column, 1u); - - pDispatch->ClearState(); - pDispatch->_isOriginModeRelative = true; - - mach.ProcessString(L"\x1b[?6l"); - VERIFY_IS_FALSE(pDispatch->_isOriginModeRelative); - VERIFY_IS_TRUE(pDispatch->_cursorPosition); - VERIFY_ARE_EQUAL(pDispatch->_line, 1u); - VERIFY_ARE_EQUAL(pDispatch->_column, 1u); + VERIFY_ARE_EQUAL(DispatchTypes::DECANM_AnsiMode, pDispatch->_modeType); + VERIFY_IS_TRUE(pDispatch->_modeEnabled); pDispatch->ClearState(); } - TEST_METHOD(TestAutoWrapMode) + TEST_METHOD(TestPrivateModes) { - 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[?7l"); - VERIFY_IS_FALSE(pDispatch->_isAutoWrapEnabled); - - pDispatch->ClearState(); - pDispatch->_isAutoWrapEnabled = false; - - mach.ProcessString(L"\x1b[?7h"); - VERIFY_IS_TRUE(pDispatch->_isAutoWrapEnabled); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestCursorBlinking) - { - 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[?12h"); - VERIFY_IS_TRUE(pDispatch->_cursorBlinking); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?12l"); - VERIFY_IS_FALSE(pDispatch->_cursorBlinking); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestCursorVisibility) - { - 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[?25h"); - VERIFY_IS_TRUE(pDispatch->_cursorVisible); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?25l"); - VERIFY_IS_FALSE(pDispatch->_cursorVisible); - - pDispatch->ClearState(); - } - - TEST_METHOD(TestAltBufferSwapping) - { - 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[?1049h"); - VERIFY_IS_TRUE(pDispatch->_isAltBuffer); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?1049h"); - VERIFY_IS_TRUE(pDispatch->_isAltBuffer); - mach.ProcessString(L"\x1b[?1049h"); - VERIFY_IS_TRUE(pDispatch->_isAltBuffer); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?1049l"); - VERIFY_IS_FALSE(pDispatch->_isAltBuffer); - - pDispatch->ClearState(); - - mach.ProcessString(L"\x1b[?1049h"); - VERIFY_IS_TRUE(pDispatch->_isAltBuffer); - mach.ProcessString(L"\x1b[?1049l"); - VERIFY_IS_FALSE(pDispatch->_isAltBuffer); - - pDispatch->ClearState(); + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:modeNumber", L"{1, 3, 5, 6, 7, 12, 25, 40, 1049}") + END_TEST_METHOD_PROPERTIES() - mach.ProcessString(L"\x1b[?1049l"); - VERIFY_IS_FALSE(pDispatch->_isAltBuffer); - mach.ProcessString(L"\x1b[?1049l"); - VERIFY_IS_FALSE(pDispatch->_isAltBuffer); + VTInt modeNumber; + VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"modeNumber", modeNumber)); - pDispatch->ClearState(); - } + const auto modeType = DispatchTypes::DECPrivateMode(modeNumber); + const auto setModeSequence = wil::str_printf(L"\x1b[?%dh", modeNumber); + const auto resetModeSequence = wil::str_printf(L"\x1b[?%dl", modeNumber); - TEST_METHOD(TestEnableDECCOLMSupport) - { 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[?40h"); - VERIFY_IS_TRUE(pDispatch->_isDECCOLMAllowed); + mach.ProcessString(setModeSequence); + VERIFY_ARE_EQUAL(modeType, pDispatch->_modeType); + VERIFY_IS_TRUE(pDispatch->_modeEnabled); pDispatch->ClearState(); - pDispatch->_isDECCOLMAllowed = true; + pDispatch->_modeEnabled = true; - mach.ProcessString(L"\x1b[?40l"); - VERIFY_IS_FALSE(pDispatch->_isDECCOLMAllowed); + mach.ProcessString(resetModeSequence); + VERIFY_ARE_EQUAL(modeType, pDispatch->_modeType); + VERIFY_IS_FALSE(pDispatch->_modeEnabled); pDispatch->ClearState(); } @@ -2025,20 +1733,18 @@ class StateMachineExternalTest final auto engine = std::make_unique(std::move(dispatch)); StateMachine mach(std::move(engine)); + const auto expectedModes = std::vector{ DispatchTypes::DECSCNM_ScreenMode, DispatchTypes::DECCKM_CursorKeysMode, DispatchTypes::DECOM_OriginMode }; + mach.ProcessString(L"\x1b[?5;1;6h"); - VERIFY_IS_TRUE(pDispatch->_isScreenModeReversed); - VERIFY_IS_TRUE(pDispatch->_cursorKeysMode); - VERIFY_IS_TRUE(pDispatch->_isOriginModeRelative); + VERIFY_IS_TRUE(pDispatch->_modeEnabled); + VERIFY_ARE_EQUAL(expectedModes, pDispatch->_modeTypes); pDispatch->ClearState(); - pDispatch->_isScreenModeReversed = true; - pDispatch->_cursorKeysMode = true; - pDispatch->_isOriginModeRelative = true; + pDispatch->_modeEnabled = true; mach.ProcessString(L"\x1b[?5;1;6l"); - VERIFY_IS_FALSE(pDispatch->_isScreenModeReversed); - VERIFY_IS_FALSE(pDispatch->_cursorKeysMode); - VERIFY_IS_FALSE(pDispatch->_isOriginModeRelative); + VERIFY_IS_FALSE(pDispatch->_modeEnabled); + VERIFY_ARE_EQUAL(expectedModes, pDispatch->_modeTypes); pDispatch->ClearState(); }