Refactor DEC/ANSI modes to avoid duplication when we add SM/RM (#8469)

I was about to add `SetAnsiMode`/`ResetAnsiMode` for `SM` and `RM` when I
realized that we probably don't need yet another enum of mode types, set and
reset functions, and a mode helper for ANSI standard modes when we already have
one for DEC Private modes.

This commit:

1. Changes the enum `PrivateModeParams` to just be `ModeParams`
2. Differentiates ANSI Standard modes (IRM, KAM, SRM, ...) from DEC
   Private modes (DECCOLM, DECCKM, ...) using a flag bit set in the enum
   value.
3. Introduces a helper class for constructing these values much like
   `VTID`. That helper takes a bitmask and applies it to an input to
   produce the final enum value.
4. Dispatches all mode set/reset through a common Set/Reset and
   `_ModeHelper` that uses the existing enum values.

[1] These modes are in separate namespaces with some overlap. We want to
differentiate them at dispatch time to ensure that `\e[2h` and `\e[?2h` are
given different treatment, and ensure that `\e[1000h` doesn't activate xterm
mouse mode.

Fixes #8457.
This commit is contained in:
Dustin L. Howett 2020-12-03 13:51:59 -08:00 committed by GitHub
parent 8d26f45278
commit 104a4e48bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 137 additions and 85 deletions

View file

@ -371,14 +371,14 @@ bool TerminalDispatch::EnableAlternateScroll(const bool enabled) noexcept
return true;
}
bool TerminalDispatch::SetPrivateMode(const DispatchTypes::PrivateModeParams param) noexcept
bool TerminalDispatch::SetMode(const DispatchTypes::ModeParams param) noexcept
{
return _PrivateModeParamsHelper(param, true);
return _ModeParamsHelper(param, true);
}
bool TerminalDispatch::ResetPrivateMode(const DispatchTypes::PrivateModeParams param) noexcept
bool TerminalDispatch::ResetMode(const DispatchTypes::ModeParams param) noexcept
{
return _PrivateModeParamsHelper(param, false);
return _ModeParamsHelper(param, false);
}
// Method Description:
@ -463,43 +463,43 @@ bool TerminalDispatch::DoConEmuAction(const std::wstring_view string) noexcept
// - enable - True for set, false for unset.
// Return Value:
// - True if handled successfully. False otherwise.
bool TerminalDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateModeParams param, const bool enable) noexcept
bool TerminalDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable) noexcept
{
bool success = false;
switch (param)
{
case DispatchTypes::PrivateModeParams::DECCKM_CursorKeysMode:
case DispatchTypes::ModeParams::DECCKM_CursorKeysMode:
// set - Enable Application Mode, reset - Normal mode
success = SetCursorKeysMode(enable);
break;
case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode:
case DispatchTypes::ModeParams::DECSCNM_ScreenMode:
success = SetScreenMode(enable);
break;
case DispatchTypes::PrivateModeParams::VT200_MOUSE_MODE:
case DispatchTypes::ModeParams::VT200_MOUSE_MODE:
success = EnableVT200MouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::BUTTON_EVENT_MOUSE_MODE:
case DispatchTypes::ModeParams::BUTTON_EVENT_MOUSE_MODE:
success = EnableButtonEventMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::ANY_EVENT_MOUSE_MODE:
case DispatchTypes::ModeParams::ANY_EVENT_MOUSE_MODE:
success = EnableAnyEventMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::UTF8_EXTENDED_MODE:
case DispatchTypes::ModeParams::UTF8_EXTENDED_MODE:
success = EnableUTF8ExtendedMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::SGR_EXTENDED_MODE:
case DispatchTypes::ModeParams::SGR_EXTENDED_MODE:
success = EnableSGRExtendedMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::ALTERNATE_SCROLL:
case DispatchTypes::ModeParams::ALTERNATE_SCROLL:
success = EnableAlternateScroll(enable);
break;
case DispatchTypes::PrivateModeParams::DECTCEM_TextCursorEnableMode:
case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode:
success = CursorVisibility(enable);
break;
case DispatchTypes::PrivateModeParams::ATT610_StartCursorBlink:
case DispatchTypes::ModeParams::ATT610_StartCursorBlink:
success = EnableCursorBlinking(enable);
break;
case DispatchTypes::PrivateModeParams::W32IM_Win32InputMode:
case DispatchTypes::ModeParams::W32IM_Win32InputMode:
success = EnableWin32InputMode(enable);
break;
default:

View file

@ -64,8 +64,8 @@ public:
bool EnableAnyEventMouseMode(const bool enabled) noexcept override; // ?1003
bool EnableAlternateScroll(const bool enabled) noexcept override; // ?1007
bool SetPrivateMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams /*param*/) noexcept override; // DECSET
bool ResetPrivateMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams /*param*/) noexcept override; // DECRST
bool SetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECSET
bool ResetMode(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams /*param*/) noexcept override; // DECRST
bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) noexcept override;
bool EndHyperlink() noexcept override;
@ -79,5 +79,5 @@ private:
TextAttribute& attr,
const bool isForeground) noexcept;
bool _PrivateModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::PrivateModeParams param, const bool enable) noexcept;
bool _ModeParamsHelper(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::ModeParams param, const bool enable) noexcept;
};

View file

@ -188,6 +188,55 @@ namespace Microsoft::Console::VirtualTerminal
private:
gsl::span<const VTParameter> _values;
};
// FlaggedEnumValue is a convenience class that produces enum values (of a specified size)
// with a flag embedded for differentiating different value categories in the same enum.
//
// It is intended to be used via type alias:
// using FirstFlagType = FlaggedEnumValue<uint8_t, 0x10>;
// using SecondFlagType = FlaggedEnumValue<uint8_t, 0x20>;
// enum EnumeratorOfThings : uint8_t {
// ThingOfFirstType = FirstFlagType(1),
// ThingOfSecondType = SecondFlagType(1)
// };
//
// It will produce an error if the provided flag value sets multiple bits.
template<typename T, T Flag>
class FlaggedEnumValue
{
template<T Value>
struct ZeroOrOneBitChecker
{
static_assert(Value == 0 || (((Value - 1) & Value) == 0), "zero or one flags expected");
static constexpr T value = Value;
};
public:
static constexpr T mask{ ZeroOrOneBitChecker<Flag>::value };
constexpr FlaggedEnumValue(const T value) :
_value{ value }
{
}
constexpr FlaggedEnumValue(const VTParameter& value) :
_value{ value }
{
}
template<typename U, std::enable_if_t<sizeof(U) == sizeof(size_t), int> = 0>
constexpr operator U() const noexcept
{
return static_cast<U>(_value | mask);
}
constexpr operator T() const noexcept
{
return _value | mask;
}
private:
T _value;
};
}
namespace Microsoft::Console::VirtualTerminal::DispatchTypes
@ -268,25 +317,28 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
CPR_CursorPositionReport = 6,
};
enum PrivateModeParams : size_t
using ANSIStandardMode = FlaggedEnumValue<size_t, 0x00000000>;
using DECPrivateMode = FlaggedEnumValue<size_t, 0x01000000>;
enum ModeParams : size_t
{
DECCKM_CursorKeysMode = 1,
DECANM_AnsiMode = 2,
DECCOLM_SetNumberOfColumns = 3,
DECSCNM_ScreenMode = 5,
DECOM_OriginMode = 6,
DECAWM_AutoWrapMode = 7,
ATT610_StartCursorBlink = 12,
DECTCEM_TextCursorEnableMode = 25,
XTERM_EnableDECCOLMSupport = 40,
VT200_MOUSE_MODE = 1000,
BUTTON_EVENT_MOUSE_MODE = 1002,
ANY_EVENT_MOUSE_MODE = 1003,
UTF8_EXTENDED_MODE = 1005,
SGR_EXTENDED_MODE = 1006,
ALTERNATE_SCROLL = 1007,
ASB_AlternateScreenBuffer = 1049,
W32IM_Win32InputMode = 9001
DECCKM_CursorKeysMode = DECPrivateMode(1),
DECANM_AnsiMode = DECPrivateMode(2),
DECCOLM_SetNumberOfColumns = DECPrivateMode(3),
DECSCNM_ScreenMode = DECPrivateMode(5),
DECOM_OriginMode = DECPrivateMode(6),
DECAWM_AutoWrapMode = DECPrivateMode(7),
ATT610_StartCursorBlink = DECPrivateMode(12),
DECTCEM_TextCursorEnableMode = DECPrivateMode(25),
XTERM_EnableDECCOLMSupport = DECPrivateMode(40),
VT200_MOUSE_MODE = DECPrivateMode(1000),
BUTTON_EVENT_MOUSE_MODE = DECPrivateMode(1002),
ANY_EVENT_MOUSE_MODE = DECPrivateMode(1003),
UTF8_EXTENDED_MODE = DECPrivateMode(1005),
SGR_EXTENDED_MODE = DECPrivateMode(1006),
ALTERNATE_SCROLL = DECPrivateMode(1007),
ASB_AlternateScreenBuffer = DECPrivateMode(1049),
W32IM_Win32InputMode = DECPrivateMode(9001),
};
enum CharacterSets : uint64_t

View file

@ -88,9 +88,9 @@ public:
virtual bool SetGraphicsRendition(const VTParameters options) = 0; // SGR
virtual bool SetPrivateMode(const DispatchTypes::PrivateModeParams param) = 0; // DECSET
virtual bool SetMode(const DispatchTypes::ModeParams param) = 0; // DECSET
virtual bool ResetPrivateMode(const DispatchTypes::PrivateModeParams param) = 0; // DECRST
virtual bool ResetMode(const DispatchTypes::ModeParams param) = 0; // DECRST
virtual bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) = 0; // DSR, DSR-OS, DSR-CPR
virtual bool DeviceAttributes() = 0; // DA1

View file

@ -1044,62 +1044,62 @@ bool AdaptDispatch::_DoDECCOLMHelper(const size_t columns)
// - enable - True for set, false for unset.
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateModeParams param, const bool enable)
bool AdaptDispatch::_ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable)
{
bool success = false;
switch (param)
{
case DispatchTypes::PrivateModeParams::DECCKM_CursorKeysMode:
case DispatchTypes::ModeParams::DECCKM_CursorKeysMode:
// set - Enable Application Mode, reset - Normal mode
success = SetCursorKeysMode(enable);
break;
case DispatchTypes::PrivateModeParams::DECANM_AnsiMode:
case DispatchTypes::ModeParams::DECANM_AnsiMode:
success = SetAnsiMode(enable);
break;
case DispatchTypes::PrivateModeParams::DECCOLM_SetNumberOfColumns:
case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns:
success = _DoDECCOLMHelper(enable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns);
break;
case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode:
case DispatchTypes::ModeParams::DECSCNM_ScreenMode:
success = SetScreenMode(enable);
break;
case DispatchTypes::PrivateModeParams::DECOM_OriginMode:
case DispatchTypes::ModeParams::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);
break;
case DispatchTypes::PrivateModeParams::DECAWM_AutoWrapMode:
case DispatchTypes::ModeParams::DECAWM_AutoWrapMode:
success = SetAutoWrapMode(enable);
break;
case DispatchTypes::PrivateModeParams::ATT610_StartCursorBlink:
case DispatchTypes::ModeParams::ATT610_StartCursorBlink:
success = EnableCursorBlinking(enable);
break;
case DispatchTypes::PrivateModeParams::DECTCEM_TextCursorEnableMode:
case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode:
success = CursorVisibility(enable);
break;
case DispatchTypes::PrivateModeParams::XTERM_EnableDECCOLMSupport:
case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport:
success = EnableDECCOLMSupport(enable);
break;
case DispatchTypes::PrivateModeParams::VT200_MOUSE_MODE:
case DispatchTypes::ModeParams::VT200_MOUSE_MODE:
success = EnableVT200MouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::BUTTON_EVENT_MOUSE_MODE:
case DispatchTypes::ModeParams::BUTTON_EVENT_MOUSE_MODE:
success = EnableButtonEventMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::ANY_EVENT_MOUSE_MODE:
case DispatchTypes::ModeParams::ANY_EVENT_MOUSE_MODE:
success = EnableAnyEventMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::UTF8_EXTENDED_MODE:
case DispatchTypes::ModeParams::UTF8_EXTENDED_MODE:
success = EnableUTF8ExtendedMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::SGR_EXTENDED_MODE:
case DispatchTypes::ModeParams::SGR_EXTENDED_MODE:
success = EnableSGRExtendedMouseMode(enable);
break;
case DispatchTypes::PrivateModeParams::ALTERNATE_SCROLL:
case DispatchTypes::ModeParams::ALTERNATE_SCROLL:
success = EnableAlternateScroll(enable);
break;
case DispatchTypes::PrivateModeParams::ASB_AlternateScreenBuffer:
case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer:
success = enable ? UseAlternateScreenBuffer() : UseMainScreenBuffer();
break;
case DispatchTypes::PrivateModeParams::W32IM_Win32InputMode:
case DispatchTypes::ModeParams::W32IM_Win32InputMode:
success = EnableWin32InputMode(enable);
break;
default:
@ -1116,9 +1116,9 @@ bool AdaptDispatch::_PrivateModeParamsHelper(const DispatchTypes::PrivateModePar
// - param - mode parameter to set
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::SetPrivateMode(const DispatchTypes::PrivateModeParams param)
bool AdaptDispatch::SetMode(const DispatchTypes::ModeParams param)
{
return _PrivateModeParamsHelper(param, true);
return _ModeParamsHelper(param, true);
}
// Routine Description:
@ -1127,9 +1127,9 @@ bool AdaptDispatch::SetPrivateMode(const DispatchTypes::PrivateModeParams param)
// - param - mode parameter to reset
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::ResetPrivateMode(const DispatchTypes::PrivateModeParams param)
bool AdaptDispatch::ResetMode(const DispatchTypes::ModeParams param)
{
return _PrivateModeParamsHelper(param, false);
return _ModeParamsHelper(param, false);
}
// - DECKPAM, DECKPNM - Sets the keypad input mode to either Application mode or Numeric mode (true, false respectively)

View file

@ -67,8 +67,8 @@ namespace Microsoft::Console::VirtualTerminal
bool InsertLine(const size_t distance) override; // IL
bool DeleteLine(const size_t distance) override; // DL
bool SetColumns(const size_t columns) override; // DECCOLM
bool SetPrivateMode(const DispatchTypes::PrivateModeParams param) override; // DECSET
bool ResetPrivateMode(const DispatchTypes::PrivateModeParams param) override; // DECRST
bool SetMode(const DispatchTypes::ModeParams param) override; // DECSET
bool ResetMode(const DispatchTypes::ModeParams param) override; // DECRST
bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM
bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM
bool EnableWin32InputMode(const bool win32InputMode) override; // win32-input-mode
@ -166,7 +166,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _CursorPositionReport() const;
bool _WriteResponse(const std::wstring_view reply) const;
bool _PrivateModeParamsHelper(const DispatchTypes::PrivateModeParams param, const bool enable);
bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable);
bool _DoDECCOLMHelper(const size_t columns);
bool _ClearSingleTabStop();

View file

@ -82,9 +82,9 @@ public:
bool SetGraphicsRendition(const VTParameters /*options*/) noexcept override { return false; } // SGR
bool SetPrivateMode(const DispatchTypes::PrivateModeParams /*param*/) noexcept override { return false; } // DECSET
bool SetMode(const DispatchTypes::ModeParams /*param*/) noexcept override { return false; } // DECSET
bool ResetPrivateMode(const DispatchTypes::PrivateModeParams /*param*/) noexcept override { return false; } // DECRST
bool ResetMode(const DispatchTypes::ModeParams /*param*/) noexcept override { return false; } // DECRST
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType /*statusType*/) noexcept override { return false; } // DSR, DSR-OS, DSR-CPR
bool DeviceAttributes() noexcept override { return false; } // DA1

View file

@ -378,7 +378,7 @@ bool OutputStateMachineEngine::ActionVt52EscDispatch(const VTID id, const VTPara
success = _dispatch->SetKeypadMode(false);
break;
case Vt52ActionCodes::ExitVt52Mode:
success = _dispatch->SetPrivateMode(DispatchTypes::PrivateModeParams::DECANM_AnsiMode);
success = _dispatch->SetMode(DispatchTypes::ModeParams::DECANM_AnsiMode);
break;
default:
// If no functions to call, overall dispatch was a failure.
@ -478,14 +478,14 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
break;
case CsiActionCodes::DECSET_PrivateModeSet:
success = parameters.for_each([&](const auto mode) {
return _dispatch->SetPrivateMode(mode);
return _dispatch->SetMode(DispatchTypes::DECPrivateMode(mode));
});
//TODO: MSFT:6367459 Add specific logging for each of the DECSET/DECRST codes
TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSET);
break;
case CsiActionCodes::DECRST_PrivateModeReset:
success = parameters.for_each([&](const auto mode) {
return _dispatch->ResetPrivateMode(mode);
return _dispatch->ResetMode(DispatchTypes::DECPrivateMode(mode));
});
TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRST);
break;

View file

@ -1247,44 +1247,44 @@ public:
return true;
}
bool _PrivateModeParamsHelper(_In_ DispatchTypes::PrivateModeParams const param, const bool fEnable)
bool _ModeParamsHelper(_In_ DispatchTypes::ModeParams const param, const bool fEnable)
{
bool fSuccess = false;
switch (param)
{
case DispatchTypes::PrivateModeParams::DECCKM_CursorKeysMode:
case DispatchTypes::ModeParams::DECCKM_CursorKeysMode:
// set - Enable Application Mode, reset - Numeric/normal mode
fSuccess = SetVirtualTerminalInputMode(fEnable);
break;
case DispatchTypes::PrivateModeParams::DECANM_AnsiMode:
case DispatchTypes::ModeParams::DECANM_AnsiMode:
fSuccess = SetAnsiMode(fEnable);
break;
case DispatchTypes::PrivateModeParams::DECCOLM_SetNumberOfColumns:
case DispatchTypes::ModeParams::DECCOLM_SetNumberOfColumns:
fSuccess = SetColumns(static_cast<size_t>(fEnable ? DispatchTypes::s_sDECCOLMSetColumns : DispatchTypes::s_sDECCOLMResetColumns));
break;
case DispatchTypes::PrivateModeParams::DECSCNM_ScreenMode:
case DispatchTypes::ModeParams::DECSCNM_ScreenMode:
fSuccess = SetScreenMode(fEnable);
break;
case DispatchTypes::PrivateModeParams::DECOM_OriginMode:
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::PrivateModeParams::DECAWM_AutoWrapMode:
case DispatchTypes::ModeParams::DECAWM_AutoWrapMode:
fSuccess = SetAutoWrapMode(fEnable);
break;
case DispatchTypes::PrivateModeParams::ATT610_StartCursorBlink:
case DispatchTypes::ModeParams::ATT610_StartCursorBlink:
fSuccess = EnableCursorBlinking(fEnable);
break;
case DispatchTypes::PrivateModeParams::DECTCEM_TextCursorEnableMode:
case DispatchTypes::ModeParams::DECTCEM_TextCursorEnableMode:
fSuccess = CursorVisibility(fEnable);
break;
case DispatchTypes::PrivateModeParams::XTERM_EnableDECCOLMSupport:
case DispatchTypes::ModeParams::XTERM_EnableDECCOLMSupport:
fSuccess = EnableDECCOLMSupport(fEnable);
break;
case DispatchTypes::PrivateModeParams::ASB_AlternateScreenBuffer:
case DispatchTypes::ModeParams::ASB_AlternateScreenBuffer:
fSuccess = fEnable ? UseAlternateScreenBuffer() : UseMainScreenBuffer();
break;
case DispatchTypes::PrivateModeParams::W32IM_Win32InputMode:
case DispatchTypes::ModeParams::W32IM_Win32InputMode:
fSuccess = EnableWin32InputMode(fEnable);
break;
default:
@ -1295,14 +1295,14 @@ public:
return fSuccess;
}
bool SetPrivateMode(const DispatchTypes::PrivateModeParams param) noexcept override
bool SetMode(const DispatchTypes::ModeParams param) noexcept override
{
return _PrivateModeParamsHelper(param, true);
return _ModeParamsHelper(param, true);
}
bool ResetPrivateMode(const DispatchTypes::PrivateModeParams param) noexcept override
bool ResetMode(const DispatchTypes::ModeParams param) noexcept override
{
return _PrivateModeParamsHelper(param, false);
return _ModeParamsHelper(param, false);
}
bool SetColumns(_In_ size_t const uiColumns) noexcept override