Add support for the DECREQTPARM report (#7939)
This PR adds support for the `DECREQTPARM` (Request Terminal Parameters) escape sequence, which was originally used on the VT100 terminal to report the serial communication parameters. Modern terminal emulators simply hardcode the reported values for backward compatibility. The `DECREQTPARM` sequence has one parameter, which was originally used to tell the terminal whether it was permitted to send unsolicited reports or not. However, since we have no reason to send an unsolicited report, we don't need to keep track of that state, but the permission parameter does still determine the value of the first parameter in the response. The response parameters are as follows: | Parameter | Value | Meaning | | ---------------- | ------ | ------------------------ | | response type | 2 or 3 | unsolicited or solicited | | parity | 1 | no parity | | data bits | 1 | 8 bits per character | | transmit speed | 128 | 38400 baud | | receive speed | 128 | 38400 baud | | clock multiplier | 1 | | | flags | 0 | | There is some variation in the baud rate reported by modern terminal emulators, and 9600 baud seems to be a little more common than 38400 baud, but I thought the higher speed was probably more appropriate, especially since that's also the value reported by XTerm. ## Validation Steps Performed I've added a couple of adapter and output engine tests to verify that the sequence is dispatched correctly, and the expected responses are generated. I've also manually tested in Vttest and confirmed that we now pass the `DECREQTPARM` test in the _Test of terminal reports_. Closes #7852
This commit is contained in:
parent
9d911c01fb
commit
30e363e7ac
|
@ -537,6 +537,7 @@ DECOM
|
|||
deconstructed
|
||||
DECPCTERM
|
||||
DECRC
|
||||
DECREQTPARM
|
||||
DECRLM
|
||||
DECRQM
|
||||
DECRST
|
||||
|
|
|
@ -325,6 +325,12 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
|
|||
SteadyBar = 6
|
||||
};
|
||||
|
||||
enum class ReportingPermission : size_t
|
||||
{
|
||||
Unsolicited = 0,
|
||||
Solicited = 1
|
||||
};
|
||||
|
||||
enum class LineFeedType : unsigned int
|
||||
{
|
||||
WithReturn,
|
||||
|
|
|
@ -97,6 +97,7 @@ public:
|
|||
virtual bool SecondaryDeviceAttributes() = 0; // DA2
|
||||
virtual bool TertiaryDeviceAttributes() = 0; // DA3
|
||||
virtual bool Vt52DeviceAttributes() = 0; // VT52 Identify
|
||||
virtual bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) = 0; // DECREQTPARM
|
||||
|
||||
virtual bool DesignateCodingSystem(const VTID codingSystem) = 0; // DOCS
|
||||
virtual bool Designate94Charset(const size_t gsetNumber, const VTID charset) = 0; // SCS
|
||||
|
|
|
@ -773,6 +773,40 @@ bool AdaptDispatch::Vt52DeviceAttributes()
|
|||
return _WriteResponse(L"\x1b/Z");
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - DECREQTPARM - This sequence was originally used on the VT100 terminal to
|
||||
// report the serial communication parameters (baud rate, data bits, parity,
|
||||
// etc.). On modern terminal emulators, the response is simply hardcoded.
|
||||
// Arguments:
|
||||
// - permission - This would originally have determined whether the terminal
|
||||
// was allowed to send unsolicited reports or not.
|
||||
// Return Value:
|
||||
// - True if handled successfully. False otherwise.
|
||||
bool AdaptDispatch::RequestTerminalParameters(const DispatchTypes::ReportingPermission permission)
|
||||
{
|
||||
// We don't care whether unsolicited reports are allowed or not, but the
|
||||
// requested permission does determine the value of the first response
|
||||
// parameter. The remaining parameters are just hardcoded to indicate a
|
||||
// 38400 baud connection, which matches the XTerm response. The full
|
||||
// parameter sequence is as follows:
|
||||
// - response type: 2 or 3 (unsolicited or solicited)
|
||||
// - parity: 1 (no parity)
|
||||
// - data bits: 1 (8 bits per character)
|
||||
// - transmit speed: 128 (38400 baud)
|
||||
// - receive speed: 128 (38400 baud)
|
||||
// - clock multiplier: 1
|
||||
// - flags: 0
|
||||
switch (permission)
|
||||
{
|
||||
case DispatchTypes::ReportingPermission::Unsolicited:
|
||||
return _WriteResponse(L"\x1b[2;1;1;128;128;1;0x");
|
||||
case DispatchTypes::ReportingPermission::Solicited:
|
||||
return _WriteResponse(L"\x1b[3;1;1;128;128;1;0x");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - DSR-OS - Reports the operating status back to the input channel
|
||||
// Arguments:
|
||||
|
|
|
@ -61,6 +61,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
bool SecondaryDeviceAttributes() override; // DA2
|
||||
bool TertiaryDeviceAttributes() override; // DA3
|
||||
bool Vt52DeviceAttributes() override; // VT52 Identify
|
||||
bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) override; // DECREQTPARM
|
||||
bool ScrollUp(const size_t distance) override; // SU
|
||||
bool ScrollDown(const size_t distance) override; // SD
|
||||
bool InsertLine(const size_t distance) override; // IL
|
||||
|
|
|
@ -91,6 +91,7 @@ public:
|
|||
bool SecondaryDeviceAttributes() noexcept override { return false; } // DA2
|
||||
bool TertiaryDeviceAttributes() noexcept override { return false; } // DA3
|
||||
bool Vt52DeviceAttributes() noexcept override { return false; } // VT52 Identify
|
||||
bool RequestTerminalParameters(const DispatchTypes::ReportingPermission /*permission*/) noexcept override { return false; } // DECREQTPARM
|
||||
|
||||
bool DesignateCodingSystem(const VTID /*codingSystem*/) noexcept override { return false; } // DOCS
|
||||
bool Designate94Charset(const size_t /*gsetNumber*/, const VTID /*charset*/) noexcept override { return false; } // SCS
|
||||
|
|
|
@ -1823,6 +1823,30 @@ public:
|
|||
VERIFY_IS_FALSE(_pDispatch.get()->TertiaryDeviceAttributes());
|
||||
}
|
||||
|
||||
TEST_METHOD(RequestTerminalParametersTests)
|
||||
{
|
||||
Log::Comment(L"Starting test...");
|
||||
|
||||
Log::Comment(L"Test 1: Verify response for unsolicited permission.");
|
||||
_testGetSet->PrepData();
|
||||
VERIFY_IS_TRUE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited));
|
||||
_testGetSet->ValidateInputEvent(L"\x1b[2;1;1;128;128;1;0x");
|
||||
|
||||
Log::Comment(L"Test 2: Verify response for solicited permission.");
|
||||
_testGetSet->PrepData();
|
||||
VERIFY_IS_TRUE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Solicited));
|
||||
_testGetSet->ValidateInputEvent(L"\x1b[3;1;1;128;128;1;0x");
|
||||
|
||||
Log::Comment(L"Test 3: Verify failure with invalid parameter.");
|
||||
_testGetSet->PrepData();
|
||||
VERIFY_IS_FALSE(_pDispatch.get()->RequestTerminalParameters((DispatchTypes::ReportingPermission)2));
|
||||
|
||||
Log::Comment(L"Test 4: Verify failure when WriteConsoleInput doesn't work.");
|
||||
_testGetSet->PrepData();
|
||||
_testGetSet->_privateWriteConsoleInputWResult = FALSE;
|
||||
VERIFY_IS_FALSE(_pDispatch.get()->RequestTerminalParameters(DispatchTypes::ReportingPermission::Unsolicited));
|
||||
}
|
||||
|
||||
TEST_METHOD(CursorKeysModeTest)
|
||||
{
|
||||
Log::Comment(L"Starting test...");
|
||||
|
|
|
@ -509,6 +509,10 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, const VTParamete
|
|||
success = parameters.at(0).value_or(0) == 0 && _dispatch->TertiaryDeviceAttributes();
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::DA3);
|
||||
break;
|
||||
case CsiActionCodes::DECREQTPARM_RequestTerminalParameters:
|
||||
success = _dispatch->RequestTerminalParameters(parameters.at(0));
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::DECREQTPARM);
|
||||
break;
|
||||
case CsiActionCodes::SU_ScrollUp:
|
||||
success = _dispatch->ScrollUp(parameters.at(0));
|
||||
TermTelemetry::Instance().Log(TermTelemetry::Codes::SU);
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
ANSISYSSC_CursorSave = VTID("s"), // NOTE: Overlaps with DECLRMM/DECSLRM. Fix when/if implemented.
|
||||
DTTERM_WindowManipulation = VTID("t"), // NOTE: Overlaps with DECSLPP. Fix when/if implemented.
|
||||
ANSISYSRC_CursorRestore = VTID("u"),
|
||||
DECREQTPARM_RequestTerminalParameters = VTID("x"),
|
||||
DECSCUSR_SetCursorStyle = VTID(" q"),
|
||||
DECSTR_SoftReset = VTID("!p"),
|
||||
DECSCPP_SetColumnsPerPage = VTID("$|")
|
||||
|
|
|
@ -227,6 +227,7 @@ void TermTelemetry::WriteFinalTraceLog() const
|
|||
TraceLoggingUInt32(_uiTimesUsed[DA], "DA"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[DA2], "DA2"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[DA3], "DA3"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[DECREQTPARM], "DECREQTPARM"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[VPA], "VPA"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[HPR], "HPR"),
|
||||
TraceLoggingUInt32(_uiTimesUsed[VPR], "VPR"),
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace Microsoft::Console::VirtualTerminal
|
|||
DA,
|
||||
DA2,
|
||||
DA3,
|
||||
DECREQTPARM,
|
||||
VPA,
|
||||
HPR,
|
||||
VPR,
|
||||
|
|
|
@ -1026,6 +1026,8 @@ public:
|
|||
_secondaryDeviceAttributes{ false },
|
||||
_tertiaryDeviceAttributes{ false },
|
||||
_vt52DeviceAttributes{ false },
|
||||
_requestTerminalParameters{ false },
|
||||
_reportingPermission{ (DispatchTypes::ReportingPermission)-1 },
|
||||
_isAltBuffer{ false },
|
||||
_cursorKeysMode{ false },
|
||||
_cursorBlinking{ true },
|
||||
|
@ -1237,6 +1239,14 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) noexcept override
|
||||
{
|
||||
_requestTerminalParameters = true;
|
||||
_reportingPermission = permission;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _PrivateModeParamsHelper(_In_ DispatchTypes::PrivateModeParams const param, const bool fEnable)
|
||||
{
|
||||
bool fSuccess = false;
|
||||
|
@ -1476,6 +1486,8 @@ public:
|
|||
bool _secondaryDeviceAttributes;
|
||||
bool _tertiaryDeviceAttributes;
|
||||
bool _vt52DeviceAttributes;
|
||||
bool _requestTerminalParameters;
|
||||
DispatchTypes::ReportingPermission _reportingPermission;
|
||||
bool _isAltBuffer;
|
||||
bool _cursorKeysMode;
|
||||
bool _cursorBlinking;
|
||||
|
@ -2434,6 +2446,44 @@ class StateMachineExternalTest final
|
|||
pDispatch->ClearState();
|
||||
}
|
||||
|
||||
TEST_METHOD(TestRequestTerminalParameters)
|
||||
{
|
||||
auto dispatch = std::make_unique<StatefulDispatch>();
|
||||
auto pDispatch = dispatch.get();
|
||||
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||
StateMachine mach(std::move(engine));
|
||||
|
||||
Log::Comment(L"Test 1: Check default case, no params.");
|
||||
mach.ProcessCharacter(AsciiChars::ESC);
|
||||
mach.ProcessCharacter(L'[');
|
||||
mach.ProcessCharacter(L'x');
|
||||
|
||||
VERIFY_IS_TRUE(pDispatch->_requestTerminalParameters);
|
||||
VERIFY_ARE_EQUAL(DispatchTypes::ReportingPermission::Unsolicited, pDispatch->_reportingPermission);
|
||||
|
||||
pDispatch->ClearState();
|
||||
|
||||
Log::Comment(L"Test 2: Check unsolicited permission, 0 param.");
|
||||
mach.ProcessCharacter(AsciiChars::ESC);
|
||||
mach.ProcessCharacter(L'[');
|
||||
mach.ProcessCharacter(L'0');
|
||||
mach.ProcessCharacter(L'x');
|
||||
|
||||
VERIFY_IS_TRUE(pDispatch->_requestTerminalParameters);
|
||||
VERIFY_ARE_EQUAL(DispatchTypes::ReportingPermission::Unsolicited, pDispatch->_reportingPermission);
|
||||
|
||||
Log::Comment(L"Test 3: Check solicited permission, 1 param.");
|
||||
mach.ProcessCharacter(AsciiChars::ESC);
|
||||
mach.ProcessCharacter(L'[');
|
||||
mach.ProcessCharacter(L'1');
|
||||
mach.ProcessCharacter(L'x');
|
||||
|
||||
VERIFY_IS_TRUE(pDispatch->_requestTerminalParameters);
|
||||
VERIFY_ARE_EQUAL(DispatchTypes::ReportingPermission::Solicited, pDispatch->_reportingPermission);
|
||||
|
||||
pDispatch->ClearState();
|
||||
}
|
||||
|
||||
TEST_METHOD(TestStrings)
|
||||
{
|
||||
auto dispatch = std::make_unique<StatefulDispatch>();
|
||||
|
|
Loading…
Reference in a new issue