Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the DECSCNM screen mode #3817

Merged
8 commits merged into from
Jan 22, 2020
29 changes: 29 additions & 0 deletions src/host/getset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,35 @@ void ApiRoutines::GetConsoleDisplayModeImpl(ULONG& flags) noexcept
return STATUS_SUCCESS;
}

// Routine Description:
// - A private API call for changing the screen mode between normal and reverse.
// When in reverse screen mode, the background and foreground colors are switched.
// Parameters:
// - reverseMode - set to true to enable reverse screen mode, false for normal mode.
// Return value:
// - STATUS_SUCCESS if handled successfully. Otherwise, an appropriate error code.
[[nodiscard]] NTSTATUS DoSrvPrivateSetScreenMode(const bool reverseMode)
{
try
{
Globals& g = ServiceLocator::LocateGlobals();
CONSOLE_INFORMATION& gci = g.getConsoleInformation();

gci.SetScreenReversed(reverseMode);

if (g.pRender)
j4james marked this conversation as resolved.
Show resolved Hide resolved
{
g.pRender->TriggerRedrawAll();
}

return STATUS_SUCCESS;
}
catch (...)
{
return NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException());
}
}

// Routine Description:
// - A private API call for making the cursor visible or not. Does not modify
// blinking state.
Expand Down
2 changes: 2 additions & 0 deletions src/host/getset.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ void DoSrvPrivateSetDefaultAttributes(SCREEN_INFORMATION& screenInfo, const bool
[[nodiscard]] NTSTATUS DoSrvPrivateSetCursorKeysMode(_In_ bool fApplicationMode);
[[nodiscard]] NTSTATUS DoSrvPrivateSetKeypadMode(_In_ bool fApplicationMode);

[[nodiscard]] NTSTATUS DoSrvPrivateSetScreenMode(const bool reverseMode);

void DoSrvPrivateShowCursor(SCREEN_INFORMATION& screenInfo, const bool show) noexcept;
void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool fEnable);

Expand Down
13 changes: 13 additions & 0 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ bool ConhostInternalGetSet::PrivateSetKeypadMode(const bool fApplicationMode)
return NT_SUCCESS(DoSrvPrivateSetKeypadMode(fApplicationMode));
}

// Routine Description:
// - Connects the PrivateSetScreenMode call directly into our Driver Message servicing call inside Conhost.exe
// PrivateSetScreenMode is an internal-only "API" call that the vt commands can execute,
// but it is not represented as a function call on our public API surface.
// Arguments:
// - reverseMode - set to true to enable reverse screen mode, false for normal mode.
// Return Value:
// - true if successful (see DoSrvPrivateSetScreenMode). false otherwise.
bool ConhostInternalGetSet::PrivateSetScreenMode(const bool reverseMode)
{
return NT_SUCCESS(DoSrvPrivateSetScreenMode(reverseMode));
}

// Routine Description:
// - Connects the PrivateShowCursor call directly into our Driver Message servicing call inside Conhost.exe
// PrivateShowCursor is an internal-only "API" call that the vt commands can execute,
Expand Down
2 changes: 2 additions & 0 deletions src/host/outputStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
bool PrivateSetCursorKeysMode(const bool applicationMode) override;
bool PrivateSetKeypadMode(const bool applicationMode) override;

bool PrivateSetScreenMode(const bool reverseMode) override;

bool PrivateShowCursor(const bool show) noexcept override;
bool PrivateAllowCursorBlinking(const bool enable) override;

Expand Down
28 changes: 26 additions & 2 deletions src/host/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Settings::Settings() :
_fUseWindowSizePixels(false),
_fAutoReturnOnNewline(true), // the historic Windows behavior defaults this to on.
_fRenderGridWorldwide(false), // historically grid lines were only rendered in DBCS codepages, so this is false by default unless otherwise specified.
_fScreenReversed(false),
// window size pixels initialized below
_fInterceptCopyPaste(0),
_DefaultForeground(INVALID_COLOR),
Expand Down Expand Up @@ -394,6 +395,15 @@ void Settings::SetGridRenderingAllowedWorldwide(const bool fGridRenderingAllowed
}
}

bool Settings::IsScreenReversed() const
{
return _fScreenReversed;
}
void Settings::SetScreenReversed(const bool fScreenReversed)
{
_fScreenReversed = fScreenReversed;
}

bool Settings::GetFilterOnPaste() const
{
return _fFilterOnPaste;
Expand Down Expand Up @@ -942,7 +952,14 @@ COLORREF Settings::CalculateDefaultBackground() const noexcept
COLORREF Settings::LookupForegroundColor(const TextAttribute& attr) const noexcept
{
const auto tableView = std::basic_string_view<COLORREF>(&GetColorTable()[0], GetColorTableSize());
return attr.CalculateRgbForeground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
if (_fScreenReversed)
{
return attr.CalculateRgbBackground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}
else
{
return attr.CalculateRgbForeground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}
}

// Method Description:
Expand All @@ -955,7 +972,14 @@ COLORREF Settings::LookupForegroundColor(const TextAttribute& attr) const noexce
COLORREF Settings::LookupBackgroundColor(const TextAttribute& attr) const noexcept
{
const auto tableView = std::basic_string_view<COLORREF>(&GetColorTable()[0], GetColorTableSize());
return attr.CalculateRgbBackground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
if (_fScreenReversed)
{
return attr.CalculateRgbForeground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}
else
{
return attr.CalculateRgbBackground(tableView, CalculateDefaultForeground(), CalculateDefaultBackground());
}
}

bool Settings::GetCopyColor() const noexcept
Expand Down
4 changes: 4 additions & 0 deletions src/host/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class Settings
bool IsGridRenderingAllowedWorldwide() const;
void SetGridRenderingAllowedWorldwide(const bool fGridRenderingAllowed);

bool IsScreenReversed() const;
void SetScreenReversed(const bool fScreenReversed);

bool GetFilterOnPaste() const;
void SetFilterOnPaste(const bool fFilterOnPaste);

Expand Down Expand Up @@ -231,6 +234,7 @@ class Settings
DWORD _dwVirtTermLevel;
bool _fAutoReturnOnNewline;
bool _fRenderGridWorldwide;
bool _fScreenReversed;
bool _fUseDx;
bool _fCopyColor;

Expand Down
29 changes: 29 additions & 0 deletions src/host/ut_host/ScreenBufferTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ class ScreenBufferTests

TEST_METHOD(ScrollLines256Colors);

TEST_METHOD(SetScreenMode);
TEST_METHOD(SetOriginMode);

TEST_METHOD(HardResetBuffer);
Expand Down Expand Up @@ -4409,6 +4410,34 @@ void ScreenBufferTests::ScrollLines256Colors()
}
}

void ScreenBufferTests::SetScreenMode()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer();
auto& stateMachine = si.GetStateMachine();

const auto rgbForeground = RGB(12, 34, 56);
const auto rgbBackground = RGB(78, 90, 12);
const auto testAttr = TextAttribute{ rgbForeground, rgbBackground };

Log::Comment(L"By default the screen mode is normal.");
VERIFY_IS_FALSE(gci.IsScreenReversed());
VERIFY_ARE_EQUAL(rgbForeground, gci.LookupForegroundColor(testAttr));
VERIFY_ARE_EQUAL(rgbBackground, gci.LookupBackgroundColor(testAttr));

Log::Comment(L"When DECSCNM is set, background and foreground colors are switched.");
stateMachine.ProcessString(L"\x1B[?5h");
VERIFY_IS_TRUE(gci.IsScreenReversed());
VERIFY_ARE_EQUAL(rgbBackground, gci.LookupForegroundColor(testAttr));
VERIFY_ARE_EQUAL(rgbForeground, gci.LookupBackgroundColor(testAttr));

Log::Comment(L"When DECSCNM is reset, the colors are normal again.");
stateMachine.ProcessString(L"\x1B[?5l");
VERIFY_IS_FALSE(gci.IsScreenReversed());
VERIFY_ARE_EQUAL(rgbForeground, gci.LookupForegroundColor(testAttr));
VERIFY_ARE_EQUAL(rgbBackground, gci.LookupBackgroundColor(testAttr));
}

void ScreenBufferTests::SetOriginMode()
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/DispatchTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
{
DECCKM_CursorKeysMode = 1,
DECCOLM_SetNumberOfColumns = 3,
DECSCNM_ScreenMode = 5,
DECOM_OriginMode = 6,
ATT610_StartCursorBlink = 12,
DECTCEM_TextCursorEnableMode = 25,
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/ITermDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual bool SetCursorKeysMode(const bool applicationMode) = 0; // DECCKM
virtual bool SetKeypadMode(const bool applicationMode) = 0; // DECKPAM, DECKPNM
virtual bool EnableCursorBlinking(const bool enable) = 0; // ATT610
virtual bool SetScreenMode(const bool reverseMode) = 0; //DECSCNM
virtual bool SetOriginMode(const bool relativeMode) = 0; // DECOM
virtual bool SetTopBottomScrollingMargins(const size_t topMargin, const size_t bottomMargin) = 0; // DECSTBM
virtual bool ReverseLineFeed() = 0; // RI
Expand Down
21 changes: 21 additions & 0 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,9 @@ bool AdaptDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateModePar
case DispatchTypes::PrivateModeParams::DECCOLM_SetNumberOfColumns:
success = _DoDECCOLMHelper(enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns);
break;
case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode:
success = SetScreenMode(enable);
break;
case DispatchTypes::PrivateModeParams::DECOM_OriginMode:
// The cursor is also moved to the new home position when the origin mode is set or reset.
success = SetOriginMode(enable) && CursorPosition(1, 1);
Expand Down Expand Up @@ -1212,6 +1215,18 @@ bool AdaptDispatch::DeleteLine(const size_t distance)
return _pConApi->DeleteLines(distance);
}

// 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)
{
return _pConApi->PrivateSetScreenMode(reverseMode);
}

// 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.
Expand Down Expand Up @@ -1562,6 +1577,12 @@ bool AdaptDispatch::HardReset()
success = _EraseScrollback();
}

// Set the DECSCNM screen mode back to normal.
if (success)
{
success = SetScreenMode(false);
}

// Cursor to 1,1 - the Soft Reset guarantees this is absolute
if (success)
{
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ namespace Microsoft::Console::VirtualTerminal
bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM
bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM
bool EnableCursorBlinking(const bool enable) override; // ATT610
bool SetScreenMode(const bool reverseMode) override; //DECSCNM
bool SetOriginMode(const bool relativeMode) noexcept override; // DECOM
bool SetTopBottomScrollingMargins(const size_t topMargin,
const size_t bottomMargin) override; // DECSTBM
Expand Down
2 changes: 2 additions & 0 deletions src/terminal/adapter/conGetSet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ namespace Microsoft::Console::VirtualTerminal
virtual bool PrivateSetCursorKeysMode(const bool applicationMode) = 0;
virtual bool PrivateSetKeypadMode(const bool applicationMode) = 0;

virtual bool PrivateSetScreenMode(const bool reverseMode) = 0;

virtual bool PrivateShowCursor(const bool show) = 0;
virtual bool PrivateAllowCursorBlinking(const bool enable) = 0;

Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/termDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
bool SetCursorKeysMode(const bool /*applicationMode*/) noexcept override { return false; } // DECCKM
bool SetKeypadMode(const bool /*applicationMode*/) noexcept override { return false; } // DECKPAM, DECKPNM
bool EnableCursorBlinking(const bool /*enable*/) noexcept override { return false; } // ATT610
bool SetScreenMode(const bool /*reverseMode*/) noexcept override { return false; } //DECSCNM
bool SetOriginMode(const bool /*relativeMode*/) noexcept override { return false; }; // DECOM
bool SetTopBottomScrollingMargins(const size_t /*topMargin*/, const size_t /*bottomMargin*/) noexcept override { return false; } // DECSTBM
bool ReverseLineFeed() noexcept override { return false; } // RI
Expand Down
7 changes: 7 additions & 0 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ class TestGetSet final : public ConGetSet
return _privateSetKeypadModeResult;
}

bool PrivateSetScreenMode(const bool /*reverseMode*/) override
{
Log::Comment(L"PrivateSetScreenMode MOCK called...");

return true;
}

bool PrivateShowCursor(const bool show) override
{
Log::Comment(L"PrivateShowCursor MOCK called...");
Expand Down
30 changes: 30 additions & 0 deletions src/terminal/parser/ut_parser/OutputEngineTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ class StatefulDispatch final : public TermDispatch
_isAltBuffer{ false },
_cursorKeysMode{ false },
_cursorBlinking{ true },
_isScreenModeReversed{ false },
_isOriginModeRelative{ false },
_isDECCOLMAllowed{ false },
_windowWidth{ 80 },
Expand Down Expand Up @@ -835,6 +836,9 @@ class StatefulDispatch final : public TermDispatch
case DispatchTypes::PrivateModeParams::DECCOLM_SetNumberOfColumns:
fSuccess = SetColumns(static_cast<size_t>(fEnable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns));
break;
case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode:
fSuccess = SetScreenMode(fEnable);
break;
case DispatchTypes::PrivateModeParams::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);
Expand Down Expand Up @@ -898,6 +902,12 @@ class StatefulDispatch final : public TermDispatch
return true;
}

bool SetScreenMode(const bool reverseMode) noexcept override
{
_isScreenModeReversed = reverseMode;
return true;
}

bool SetOriginMode(const bool fRelativeMode) noexcept override
{
_isOriginModeRelative = fRelativeMode;
Expand Down Expand Up @@ -949,6 +959,7 @@ class StatefulDispatch final : public TermDispatch
bool _isAltBuffer;
bool _cursorKeysMode;
bool _cursorBlinking;
bool _isScreenModeReversed;
bool _isOriginModeRelative;
bool _isDECCOLMAllowed;
size_t _windowWidth;
Expand Down Expand Up @@ -1226,6 +1237,25 @@ class StateMachineExternalTest final
pDispatch->ClearState();
}

TEST_METHOD(TestScreenMode)
{
auto dispatch = std::make_unique<StatefulDispatch>();
auto pDispatch = dispatch.get();
auto engine = std::make_unique<OutputStateMachineEngine>(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<StatefulDispatch>();
Expand Down