From 695ebffca1e7d8fbee18755032e60512cef4778e Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 9 Jul 2020 12:25:30 +0100 Subject: [PATCH] Add support for DECSCNM in Windows Terminal (#6809) ## Summary of the Pull Request This PR adds full support for the `DECSCNM` reverse screen mode in the Windows Terminal to align with the implementation in conhost. ## References * The conhost implementation of `DECSCNM` was in PR #3817. * WT originally inherited that functionality via the colors being passed through, but that behaviour was lost in PR #6506. ## PR Checklist * [x] Closes #6622 * [x] CLA signed. * [ ] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #6622 ## Detailed Description of the Pull Request / Additional comments The `AdaptDispatch::SetScreenMode` now checks if it's in conpty mode and simply returns false to force a pass-through of the mode change. And the `TerminalDispatch` now has its own `SetScreenMode` implementation that tracks any changes to the reversed state, and triggers a redraw in the renderer. To make the renderer work, we just needed to update the `GetForegroundColor` and `GetBackgroundColor` methods of the terminal's `IRenderData` implementation to check the reversed state, and switch the colors being calculated, the same way the `LookupForegroundColor` and `LookupBackgroundColor` methods work in the conhost `Settings` class. ## Validation Steps Performed I've manually tested the `DECSCNM` functionality for Windows Terminal in Vttest, and also with some of my own test scripts. --- src/cascadia/TerminalCore/ITerminalApi.hpp | 1 + src/cascadia/TerminalCore/Terminal.cpp | 1 + src/cascadia/TerminalCore/Terminal.hpp | 2 ++ src/cascadia/TerminalCore/TerminalApi.cpp | 11 ++++++++ .../TerminalCore/TerminalDispatch.cpp | 19 ++++++++++++-- .../TerminalCore/TerminalDispatch.hpp | 1 + .../TerminalCore/terminalrenderdata.cpp | 26 +++++++++++++++---- src/terminal/adapter/adaptDispatch.cpp | 6 +++++ 8 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index e74cf6905ee..d92a536c9d6 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -46,6 +46,7 @@ namespace Microsoft::Terminal::Core virtual bool EnableWin32InputMode(const bool win32InputMode) noexcept = 0; virtual bool SetCursorKeysMode(const bool applicationMode) noexcept = 0; virtual bool SetKeypadMode(const bool applicationMode) noexcept = 0; + virtual bool SetScreenMode(const bool reverseMode) noexcept = 0; virtual bool EnableVT200MouseMode(const bool enabled) noexcept = 0; virtual bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept = 0; virtual bool EnableSGRExtendedMouseMode(const bool enabled) noexcept = 0; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4bf52299801..6534ba71b2e 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -41,6 +41,7 @@ Terminal::Terminal() : _colorTable{}, _defaultFg{ RGB(255, 255, 255) }, _defaultBg{ ARGB(0, 0, 0, 0) }, + _screenReversed{ false }, _pfnWriteInput{ nullptr }, _scrollOffset{ 0 }, _snapOnInput{ true }, diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index f7a19148a7c..d2f60e18861 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -99,6 +99,7 @@ class Microsoft::Terminal::Core::Terminal final : bool EnableWin32InputMode(const bool win32InputMode) noexcept override; bool SetCursorKeysMode(const bool applicationMode) noexcept override; bool SetKeypadMode(const bool applicationMode) noexcept override; + bool SetScreenMode(const bool reverseMode) noexcept override; bool EnableVT200MouseMode(const bool enabled) noexcept override; bool EnableUTF8ExtendedMouseMode(const bool enabled) noexcept override; bool EnableSGRExtendedMouseMode(const bool enabled) noexcept override; @@ -208,6 +209,7 @@ class Microsoft::Terminal::Core::Terminal final : std::array _colorTable; COLORREF _defaultFg; COLORREF _defaultBg; + bool _screenReversed; bool _snapOnInput; bool _altGrAliasing; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9628b3acca9..4c7f27a88ea 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -471,6 +471,17 @@ bool Terminal::SetKeypadMode(const bool applicationMode) noexcept return true; } +bool Terminal::SetScreenMode(const bool reverseMode) noexcept +try +{ + _screenReversed = reverseMode; + + // Repaint everything - the colors will have changed + _buffer->GetRenderTarget().TriggerRedrawAll(); + return true; +} +CATCH_LOG_RETURN_FALSE() + bool Terminal::EnableVT200MouseMode(const bool enabled) noexcept { _terminalInput->EnableDefaultTracking(enabled); diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index 730d579ed75..26826ddf34e 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -253,6 +253,18 @@ bool TerminalDispatch::SetCursorKeysMode(const bool applicationMode) noexcept 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 TerminalDispatch::SetScreenMode(const bool reverseMode) noexcept +{ + return _terminalApi.SetScreenMode(reverseMode); +} + // Method Description: // - win32-input-mode: Enable sending full input records encoded as a string of // characters to the client application. @@ -390,6 +402,9 @@ bool TerminalDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateMode // set - Enable Application Mode, reset - Normal mode success = SetCursorKeysMode(enable); break; + case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode: + success = SetScreenMode(enable); + break; case DispatchTypes::PrivateModeParams::VT200_MOUSE_MODE: success = EnableVT200MouseMode(enable); break; @@ -493,8 +508,8 @@ bool TerminalDispatch::HardReset() noexcept success = EraseInDisplay(DispatchTypes::EraseType::All) && success; success = EraseInDisplay(DispatchTypes::EraseType::Scrollback) && success; - // // Set the DECSCNM screen mode back to normal. - // success = SetScreenMode(false) && success; + // Set the DECSCNM screen mode back to normal. + success = SetScreenMode(false) && success; // Cursor to 1,1 - the Soft Reset guarantees this is absolute success = CursorPosition(1, 1) && success; diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index b01ce406078..c916bb35895 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -47,6 +47,7 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetCursorKeysMode(const bool applicationMode) noexcept override; // DECCKM bool SetKeypadMode(const bool applicationMode) noexcept override; // DECKPAM, DECKPNM + bool SetScreenMode(const bool reverseMode) noexcept override; // DECSCNM bool SoftReset() noexcept override; // DECSTR bool HardReset() noexcept override; // RIS diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 17b57a28240..294ac780274 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -52,15 +52,32 @@ const TextAttribute Terminal::GetDefaultBrushColors() noexcept const COLORREF Terminal::GetForegroundColor(const TextAttribute& attr) const noexcept { - return 0xff000000 | attr.CalculateRgbForeground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + COLORREF fgColor{}; + if (_screenReversed) + { + fgColor = attr.CalculateRgbBackground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + } + else + { + fgColor = attr.CalculateRgbForeground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + } + return 0xff000000 | fgColor; } const COLORREF Terminal::GetBackgroundColor(const TextAttribute& attr) const noexcept { - const auto bgColor = attr.CalculateRgbBackground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + COLORREF bgColor{}; + if (_screenReversed) + { + bgColor = attr.CalculateRgbForeground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + } + else + { + bgColor = attr.CalculateRgbBackground({ _colorTable.data(), _colorTable.size() }, _defaultFg, _defaultBg); + } // We only care about alpha for the default BG (which enables acrylic) // If the bg isn't the default bg color, or reverse video is enabled, make it fully opaque. - if (!attr.BackgroundIsDefault() || attr.IsReverseVideo()) + if (!attr.BackgroundIsDefault() || (attr.IsReverseVideo() ^ _screenReversed)) { return 0xff000000 | bgColor; } @@ -213,10 +230,9 @@ void Terminal::UnlockConsole() noexcept // Method Description: // - Returns whether the screen is inverted; -// This state is not currently known to Terminal. // Return Value: // - false. bool Terminal::IsScreenReversed() const noexcept { - return false; + return _screenReversed; } diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 5d9a922ee14..39995bdf86b 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1205,6 +1205,12 @@ bool AdaptDispatch::SetAnsiMode(const bool ansiMode) // - True if handled successfully. False otherwise. bool AdaptDispatch::SetScreenMode(const bool reverseMode) { + // If we're a conpty, always return false + if (_pConApi->IsConsolePty()) + { + return false; + } + return _pConApi->PrivateSetScreenMode(reverseMode); }