Enable DECCOLM support via a private mode escape sequence (#1709)

* Implement XTerm's private mode escape sequence for enabling DECCOLM support.
* Add output engine and screen buffer units test for the private mode 40 escape sequence.
This commit is contained in:
James Holderness 2019-07-02 18:24:11 +01:00 committed by Michael Niksa
parent c791b7870d
commit 2e0e9628fc
7 changed files with 188 additions and 9 deletions

View file

@ -107,6 +107,7 @@ class ScreenBufferTests
TEST_METHOD(VtResize);
TEST_METHOD(VtResizeComprehensive);
TEST_METHOD(VtResizeDECCOLM);
TEST_METHOD(VtSoftResetCursorPosition);
@ -972,6 +973,137 @@ void ScreenBufferTests::VtResizeComprehensive()
VERIFY_ARE_EQUAL(expectedViewHeight, newViewHeight);
}
void ScreenBufferTests::VtResizeDECCOLM()
{
// Run this test in isolation - for one reason or another, this breaks other tests.
BEGIN_TEST_METHOD_PROPERTIES()
TEST_METHOD_PROPERTY(L"IsolationLevel", L"Method")
END_TEST_METHOD_PROPERTIES()
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer();
auto& stateMachine = si.GetStateMachine();
WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
const auto setInitialMargins = L"\x1b[5;15r";
const auto setInitialCursor = L"\x1b[10;40HABCDEF";
const auto allowDECCOLM = L"\x1b[?40h";
const auto disallowDECCOLM = L"\x1b[?40l";
const auto setDECCOLM = L"\x1b[?3h";
const auto resetDECCOLM = L"\x1b[?3l";
auto getRelativeCursorPosition = [&]() {
return si.GetTextBuffer().GetCursor().GetPosition() - si.GetViewport().Origin();
};
stateMachine.ProcessString(setInitialMargins);
stateMachine.ProcessString(setInitialCursor);
auto initialMargins = si.GetRelativeScrollMargins();
auto initialCursorPosition = getRelativeCursorPosition();
auto initialSbHeight = si.GetBufferSize().Height();
auto initialSbWidth = si.GetBufferSize().Width();
auto initialViewHeight = si.GetViewport().Height();
auto initialViewWidth = si.GetViewport().Width();
Log::Comment(L"By default, setting DECCOLM should have no effect");
stateMachine.ProcessString(setDECCOLM);
auto newSbHeight = si.GetBufferSize().Height();
auto newSbWidth = si.GetBufferSize().Width();
auto newViewHeight = si.GetViewport().Height();
auto newViewWidth = si.GetViewport().Width();
VERIFY_IS_TRUE(si.AreMarginsSet());
VERIFY_ARE_EQUAL(initialMargins, si.GetRelativeScrollMargins());
VERIFY_ARE_EQUAL(initialCursorPosition, getRelativeCursorPosition());
VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight);
VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight);
VERIFY_ARE_EQUAL(initialSbWidth, newSbWidth);
VERIFY_ARE_EQUAL(initialViewWidth, newViewWidth);
stateMachine.ProcessString(setInitialMargins);
stateMachine.ProcessString(setInitialCursor);
initialSbHeight = newSbHeight;
initialSbWidth = newSbWidth;
initialViewHeight = newViewHeight;
initialViewWidth = newViewWidth;
Log::Comment(
L"Once DECCOLM is allowed, setting it "
L"should change the width to 132 columns "
L"and reset the margins and cursor position");
stateMachine.ProcessString(allowDECCOLM);
stateMachine.ProcessString(setDECCOLM);
newSbHeight = si.GetBufferSize().Height();
newSbWidth = si.GetBufferSize().Width();
newViewHeight = si.GetViewport().Height();
newViewWidth = si.GetViewport().Width();
VERIFY_IS_FALSE(si.AreMarginsSet());
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), getRelativeCursorPosition());
VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight);
VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight);
VERIFY_ARE_EQUAL(132, newSbWidth);
VERIFY_ARE_EQUAL(132, newViewWidth);
stateMachine.ProcessString(setInitialMargins);
stateMachine.ProcessString(setInitialCursor);
initialMargins = si.GetRelativeScrollMargins();
initialCursorPosition = getRelativeCursorPosition();
initialSbHeight = newSbHeight;
initialSbWidth = newSbWidth;
initialViewHeight = newViewHeight;
initialViewWidth = newViewWidth;
Log::Comment(L"If DECCOLM is disallowed, resetting it should have no effect");
stateMachine.ProcessString(disallowDECCOLM);
stateMachine.ProcessString(resetDECCOLM);
newSbHeight = si.GetBufferSize().Height();
newSbWidth = si.GetBufferSize().Width();
newViewHeight = si.GetViewport().Height();
newViewWidth = si.GetViewport().Width();
VERIFY_IS_TRUE(si.AreMarginsSet());
VERIFY_ARE_EQUAL(initialMargins, si.GetRelativeScrollMargins());
VERIFY_ARE_EQUAL(initialCursorPosition, getRelativeCursorPosition());
VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight);
VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight);
VERIFY_ARE_EQUAL(initialSbWidth, newSbWidth);
VERIFY_ARE_EQUAL(initialViewWidth, newViewWidth);
stateMachine.ProcessString(setInitialMargins);
stateMachine.ProcessString(setInitialCursor);
initialSbHeight = newSbHeight;
initialSbWidth = newSbWidth;
initialViewHeight = newViewHeight;
initialViewWidth = newViewWidth;
Log::Comment(
L"Once DECCOLM is allowed again, resetting it "
L"should change the width to 80 columns "
L"and reset the margins and cursor position");
stateMachine.ProcessString(allowDECCOLM);
stateMachine.ProcessString(resetDECCOLM);
newSbHeight = si.GetBufferSize().Height();
newSbWidth = si.GetBufferSize().Width();
newViewHeight = si.GetViewport().Height();
newViewWidth = si.GetViewport().Width();
VERIFY_IS_FALSE(si.AreMarginsSet());
VERIFY_ARE_EQUAL(COORD({ 0, 0 }), getRelativeCursorPosition());
VERIFY_ARE_EQUAL(initialSbHeight, newSbHeight);
VERIFY_ARE_EQUAL(initialViewHeight, newViewHeight);
VERIFY_ARE_EQUAL(80, newSbWidth);
VERIFY_ARE_EQUAL(80, newViewWidth);
}
void ScreenBufferTests::VtSoftResetCursorPosition()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();

View file

@ -77,6 +77,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
DECCOLM_SetNumberOfColumns = 3,
ATT610_StartCursorBlink = 12,
DECTCEM_TextCursorEnableMode = 25,
XTERM_EnableDECCOLMSupport = 40,
VT200_MOUSE_MODE = 1000,
BUTTTON_EVENT_MOUSE_MODE = 1002,
ANY_EVENT_MOUSE_MODE = 1003,

View file

@ -58,6 +58,7 @@ public:
virtual bool ForwardTab(const SHORT sNumTabs) = 0; // CHT
virtual bool BackwardsTab(const SHORT sNumTabs) = 0; // CBT
virtual bool TabClear(const SHORT sClearType) = 0; // TBC
virtual bool EnableDECCOLMSupport(const bool fEnabled) = 0; // ?40
virtual bool EnableVT200MouseMode(const bool fEnabled) = 0; // ?1000
virtual bool EnableUTF8ExtendedMouseMode(const bool fEnabled) = 0; // ?1005
virtual bool EnableSGRExtendedMouseMode(const bool fEnabled) = 0; // ?1006

View file

@ -39,6 +39,7 @@ AdaptDispatch::AdaptDispatch(ConGetSet* const pConApi,
AdaptDefaults* const pDefaults) :
_conApi{ THROW_IF_NULL_ALLOC(pConApi) },
_pDefaults{ THROW_IF_NULL_ALLOC(pDefaults) },
_fIsDECCOLMAllowed(false), // by default, DECCOLM is not allowed.
_fChangedBackground(false),
_fChangedForeground(false),
_fChangedMetaAttrs(false),
@ -48,8 +49,6 @@ AdaptDispatch::AdaptDispatch(ConGetSet* const pConApi,
_coordSavedCursor.X = 1;
_coordSavedCursor.Y = 1;
_srScrollMargins = { 0 }; // initially, there are no scroll margins.
_fIsSetColumnsEnabled = false; // by default, DECSCPP is disabled.
// TODO:10086990 - Create a setting to re-enable this.
}
void AdaptDispatch::Print(const wchar_t wchPrintable)
@ -1055,12 +1054,6 @@ bool AdaptDispatch::ScrollDown(_In_ unsigned int const uiDistance)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::SetColumns(_In_ unsigned int const uiColumns)
{
if (!_fIsSetColumnsEnabled)
{
// Only set columns if that option is available. Return true, as this is technically a successful handling.
return true;
}
SHORT sColumns;
bool fSuccess = SUCCEEDED(UIntToShort(uiColumns, &sColumns));
if (fSuccess)
@ -1086,6 +1079,12 @@ bool AdaptDispatch::SetColumns(_In_ unsigned int const uiColumns)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::_DoDECCOLMHelper(_In_ unsigned int const uiColumns)
{
if (!_fIsDECCOLMAllowed)
{
// Only proceed if DECCOLM is allowed. Return true, as this is technically a successful handling.
return true;
}
bool fSuccess = SetColumns(uiColumns);
if (fSuccess)
{
@ -1120,6 +1119,9 @@ bool AdaptDispatch::_PrivateModeParamsHelper(_In_ DispatchTypes::PrivateModePara
case DispatchTypes::PrivateModeParams::DECTCEM_TextCursorEnableMode:
fSuccess = CursorVisibility(fEnable);
break;
case DispatchTypes::PrivateModeParams::XTERM_EnableDECCOLMSupport:
fSuccess = EnableDECCOLMSupport(fEnable);
break;
case DispatchTypes::PrivateModeParams::VT200_MOUSE_MODE:
fSuccess = EnableVT200MouseMode(fEnable);
break;
@ -1679,6 +1681,18 @@ bool AdaptDispatch::_EraseAll()
return !!_conApi->PrivateEraseAll();
}
// Routine Description:
// - Enables or disables support for the DECCOLM escape sequence.
// Arguments:
// - fEnabled - set to true to allow DECCOLM to be used, false to disallow.
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::EnableDECCOLMSupport(const bool fEnabled)
{
_fIsDECCOLMAllowed = fEnabled;
return true;
}
//Routine Description:
// Enable VT200 Mouse Mode - Enables/disables the mouse input handler in default tracking mode.
//Arguments:

View file

@ -85,6 +85,7 @@ namespace Microsoft::Console::VirtualTerminal
bool DesignateCharset(const wchar_t wchCharset) override; // DesignateCharset
bool SoftReset() override; // DECSTR
bool HardReset() override; // RIS
bool EnableDECCOLMSupport(const bool fEnabled) override; // ?40
bool EnableVT200MouseMode(const bool fEnabled) override; // ?1000
bool EnableUTF8ExtendedMouseMode(const bool fEnabled) override; // ?1005
bool EnableSGRExtendedMouseMode(const bool fEnabled) override; // ?1006
@ -149,7 +150,7 @@ namespace Microsoft::Console::VirtualTerminal
COORD _coordSavedCursor;
SMALL_RECT _srScrollMargins;
bool _fIsSetColumnsEnabled;
bool _fIsDECCOLMAllowed;
bool _fChangedForeground;
bool _fChangedBackground;

View file

@ -55,6 +55,7 @@ public:
bool ForwardTab(const SHORT /*sNumTabs*/) override { return false; } // CHT
bool BackwardsTab(const SHORT /*sNumTabs*/) override { return false; } // CBT
bool TabClear(const SHORT /*sClearType*/) override { return false; } // TBC
bool EnableDECCOLMSupport(const bool /*fEnabled*/) override { return false; } // ?40
bool EnableVT200MouseMode(const bool /*fEnabled*/) override { return false; } // ?1000
bool EnableUTF8ExtendedMouseMode(const bool /*fEnabled*/) override { return false; } // ?1005
bool EnableSGRExtendedMouseMode(const bool /*fEnabled*/) override { return false; } // ?1006

View file

@ -641,6 +641,7 @@ public:
_fIsAltBuffer{ false },
_fCursorKeysMode{ false },
_fCursorBlinking{ true },
_fIsDECCOLMAllowed{ false },
_uiWindowWidth{ 80 }
{
memset(_rgOptions, s_uiGraphicsCleared, sizeof(_rgOptions));
@ -807,6 +808,9 @@ public:
case DispatchTypes::PrivateModeParams::DECTCEM_TextCursorEnableMode:
fSuccess = CursorVisibility(fEnable);
break;
case DispatchTypes::PrivateModeParams::XTERM_EnableDECCOLMSupport:
fSuccess = EnableDECCOLMSupport(fEnable);
break;
case DispatchTypes::PrivateModeParams::ASB_AlternateScreenBuffer:
fSuccess = fEnable ? UseAlternateScreenBuffer() : UseMainScreenBuffer();
break;
@ -860,6 +864,12 @@ public:
return true;
}
bool EnableDECCOLMSupport(const bool fEnabled) override
{
_fIsDECCOLMAllowed = fEnabled;
return true;
}
bool UseAlternateScreenBuffer() override
{
_fIsAltBuffer = true;
@ -899,6 +909,7 @@ public:
bool _fIsAltBuffer;
bool _fCursorKeysMode;
bool _fCursorBlinking;
bool _fIsDECCOLMAllowed;
unsigned int _uiWindowWidth;
static const size_t s_cMaxOptions = 16;
@ -1265,6 +1276,24 @@ class StateMachineExternalTest final
pDispatch->ClearState();
}
TEST_METHOD(TestEnableDECCOLMSupport)
{
StatefulDispatch* pDispatch = new StatefulDispatch;
VERIFY_IS_NOT_NULL(pDispatch);
StateMachine mach(new OutputStateMachineEngine(pDispatch));
mach.ProcessString(L"\x1b[?40h");
VERIFY_IS_TRUE(pDispatch->_fIsDECCOLMAllowed);
pDispatch->ClearState();
pDispatch->_fIsDECCOLMAllowed = true;
mach.ProcessString(L"\x1b[?40l");
VERIFY_IS_FALSE(pDispatch->_fIsDECCOLMAllowed);
pDispatch->ClearState();
}
TEST_METHOD(TestErase)
{
BEGIN_TEST_METHOD_PROPERTIES()