Add support for the DSR-OS operating status report (#5300)

This adds support for the VT escape sequence that requests the
terminal's operating status. There is no attempt to actually verify the
status of the app, though. We always return a response indicating a good
operating condition (the same as most terminal emulators).

## PR Checklist
* [x] CLA signed.
* [x] Tests added/passed

## Detailed Description of the Pull Request / Additional comments

This required an update to the `OutputStateMachineEngine` to accept the
`DSR-OS` type, since it only dispatches types that it recognises (I
think that's unnecessary, but that's an issue for another day).

The actual processing of the request is handled in the `AdaptDispatch`
class, where it simply responds with a hard coded sequence (`CSI 0 n`),
indicating a good operating condition.

## Validation Steps Performed

I've added unit tests to confirm that the request is dispatched
correctly, and the appropriate response is returned. I've also manually
confirmed that the test of the _Device Status Report_ in _Vttest_ is now
succeeding.

Closes #5052
This commit is contained in:
James Holderness 2020-04-09 21:11:37 +01:00 committed by GitHub
parent 7936605cc9
commit 2d09dfd48b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 5 deletions

View file

@ -75,6 +75,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
enum class AnsiStatusType : unsigned int
{
OS_OperatingStatus = 5,
CPR_CursorPositionReport = 6,
};

View file

@ -90,7 +90,7 @@ public:
virtual bool ResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> params) = 0; // DECRST
virtual bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) = 0; // DSR, DSR-CPR
virtual bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) = 0; // DSR, DSR-OS, DSR-CPR
virtual bool DeviceAttributes() = 0; // DA1
virtual bool DesignateCharset(const wchar_t wchCharset) = 0; // SCS

View file

@ -685,6 +685,9 @@ bool AdaptDispatch::DeviceStatusReport(const DispatchTypes::AnsiStatusType statu
switch (statusType)
{
case DispatchTypes::AnsiStatusType::OS_OperatingStatus:
success = _OperatingStatus();
break;
case DispatchTypes::AnsiStatusType::CPR_CursorPositionReport:
success = _CursorPositionReport();
break;
@ -706,6 +709,18 @@ bool AdaptDispatch::DeviceAttributes()
return _WriteResponse(L"\x1b[?1;0c");
}
// Routine Description:
// - DSR-OS - Reports the operating status back to the input channel
// Arguments:
// - <none>
// Return Value:
// - True if handled successfully. False otherwise.
bool AdaptDispatch::_OperatingStatus() const
{
// We always report a good operating condition.
return _WriteResponse(L"\x1b[0n");
}
// Routine Description:
// - DSR-CPR - Reports the current cursor position within the viewport back to the input channel
// Arguments:

View file

@ -56,7 +56,7 @@ namespace Microsoft::Console::VirtualTerminal
bool InsertCharacter(const size_t count) override; // ICH
bool DeleteCharacter(const size_t count) override; // DCH
bool SetGraphicsRendition(const std::basic_string_view<DispatchTypes::GraphicsOptions> options) override; // SGR
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) override; // DSR, DSR-CPR
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) override; // DSR, DSR-OS, DSR-CPR
bool DeviceAttributes() override; // DA1
bool ScrollUp(const size_t distance) override; // SU
bool ScrollDown(const size_t distance) override; // SD
@ -145,6 +145,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _DoSetTopBottomScrollingMargins(const size_t topMargin,
const size_t bottomMargin);
bool _OperatingStatus() const;
bool _CursorPositionReport() const;
bool _WriteResponse(const std::wstring_view reply) const;

View file

@ -84,7 +84,7 @@ public:
bool ResetPrivateModes(const std::basic_string_view<DispatchTypes::PrivateModeParams> /*params*/) noexcept override { return false; } // DECRST
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType /*statusType*/) noexcept override { return false; } // DSR, DSR-CPR
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType /*statusType*/) noexcept override { return false; } // DSR, DSR-OS, DSR-CPR
bool DeviceAttributes() noexcept override { return false; } // DA1
bool DesignateCharset(const wchar_t /*wchCharset*/) noexcept override { return false; } // SCS

View file

@ -1778,6 +1778,17 @@ public:
VERIFY_IS_FALSE(_pDispatch.get()->DeviceStatusReport((DispatchTypes::AnsiStatusType)-1));
}
TEST_METHOD(DeviceStatus_OperatingStatusTests)
{
Log::Comment(L"Starting test...");
Log::Comment(L"Test 1: Verify good operating condition.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch.get()->DeviceStatusReport(DispatchTypes::AnsiStatusType::OS_OperatingStatus));
_testGetSet->ValidateInputEvent(L"\x1b[0n");
}
TEST_METHOD(DeviceStatus_CursorPositionReportTests)
{
Log::Comment(L"Starting test...");

View file

@ -1113,6 +1113,10 @@ bool OutputStateMachineEngine::_GetDeviceStatusOperation(const std::basic_string
switch (param)
{
// This looks kinda silly, but I want the parser to reject (success = false) any status types we haven't put here.
case (unsigned short)DispatchTypes::AnsiStatusType::OS_OperatingStatus:
statusType = DispatchTypes::AnsiStatusType::OS_OperatingStatus;
success = true;
break;
case (unsigned short)DispatchTypes::AnsiStatusType::CPR_CursorPositionReport:
statusType = DispatchTypes::AnsiStatusType::CPR_CursorPositionReport;
success = true;

View file

@ -1745,7 +1745,18 @@ class StateMachineExternalTest final
pDispatch->ClearState();
Log::Comment(L"Test 2: Check CSR (cursor position command) case 6. Should succeed.");
Log::Comment(L"Test 2: Check OS (operating status) case 5. Should succeed.");
mach.ProcessCharacter(AsciiChars::ESC);
mach.ProcessCharacter(L'[');
mach.ProcessCharacter(L'5');
mach.ProcessCharacter(L'n');
VERIFY_IS_TRUE(pDispatch->_deviceStatusReport);
VERIFY_ARE_EQUAL(DispatchTypes::AnsiStatusType::OS_OperatingStatus, pDispatch->_statusReportType);
pDispatch->ClearState();
Log::Comment(L"Test 3: Check CPR (cursor position report) case 6. Should succeed.");
mach.ProcessCharacter(AsciiChars::ESC);
mach.ProcessCharacter(L'[');
mach.ProcessCharacter(L'6');
@ -1756,7 +1767,7 @@ class StateMachineExternalTest final
pDispatch->ClearState();
Log::Comment(L"Test 3: Check unimplemented case 1. Should fail.");
Log::Comment(L"Test 4: Check unimplemented case 1. Should fail.");
mach.ProcessCharacter(AsciiChars::ESC);
mach.ProcessCharacter(L'[');
mach.ProcessCharacter(L'1');