Skip to content

Commit

Permalink
Add support for more DSR queries.
Browse files Browse the repository at this point in the history
  • Loading branch information
j4james committed Jan 3, 2024
1 parent 63c3573 commit eed60e6
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 34 deletions.
17 changes: 12 additions & 5 deletions src/terminal/adapter/DispatchTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,11 +500,18 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes

enum class StatusType : VTInt
{
OS_OperatingStatus = ANSIStandardStatus(5),
CPR_CursorPositionReport = ANSIStandardStatus(6),
ExCPR_ExtendedCursorPositionReport = DECPrivateStatus(6),
MSR_MacroSpaceReport = DECPrivateStatus(62),
MEM_MemoryChecksum = DECPrivateStatus(63),
OperatingStatus = ANSIStandardStatus(5),
CursorPositionReport = ANSIStandardStatus(6),
ExtendedCursorPositionReport = DECPrivateStatus(6),
PrinterStatus = DECPrivateStatus(15),
UserDefinedKeys = DECPrivateStatus(25),
KeyboardStatus = DECPrivateStatus(26),
LocatorStatus = DECPrivateStatus(55),
LocatorIdentity = DECPrivateStatus(56),
MacroSpaceReport = DECPrivateStatus(62),
MemoryChecksum = DECPrivateStatus(63),
DataIntegrity = DECPrivateStatus(75),
MultipleSessionStatus = DECPrivateStatus(85),
};

using ANSIStandardMode = FlaggedEnumValue<0x00000000>;
Expand Down
51 changes: 40 additions & 11 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1473,23 +1473,53 @@ bool AdaptDispatch::SetLineRendition(const LineRendition rendition)
// - True if handled successfully. False otherwise.
bool AdaptDispatch::DeviceStatusReport(const DispatchTypes::StatusType statusType, const VTParameter id)
{
constexpr auto GoodCondition = L"0";
constexpr auto PrinterNotConnected = L"?13";
constexpr auto UserDefinedKeysNotSupported = L"?23";
constexpr auto UnknownPcKeyboard = L"?27;0;0;5";
constexpr auto LocatorNotConnected = L"?53";
constexpr auto UnknownLocatorDevice = L"?57;0";
constexpr auto TerminalReady = L"?70";
constexpr auto MultipleSessionsNotSupported = L"?83";

switch (statusType)
{
case DispatchTypes::StatusType::OS_OperatingStatus:
_OperatingStatus();
case DispatchTypes::StatusType::OperatingStatus:
_DeviceStatusReport(GoodCondition);
return true;
case DispatchTypes::StatusType::CPR_CursorPositionReport:
case DispatchTypes::StatusType::CursorPositionReport:
_CursorPositionReport(false);
return true;
case DispatchTypes::StatusType::ExCPR_ExtendedCursorPositionReport:
case DispatchTypes::StatusType::ExtendedCursorPositionReport:
_CursorPositionReport(true);
return true;
case DispatchTypes::StatusType::MSR_MacroSpaceReport:
case DispatchTypes::StatusType::PrinterStatus:
_DeviceStatusReport(PrinterNotConnected);
return true;
case DispatchTypes::StatusType::UserDefinedKeys:
_DeviceStatusReport(UserDefinedKeysNotSupported);
return true;
case DispatchTypes::StatusType::KeyboardStatus:
_DeviceStatusReport(UnknownPcKeyboard);
return true;
case DispatchTypes::StatusType::LocatorStatus:
_DeviceStatusReport(LocatorNotConnected);
return true;
case DispatchTypes::StatusType::LocatorIdentity:
_DeviceStatusReport(UnknownLocatorDevice);
return true;
case DispatchTypes::StatusType::MacroSpaceReport:
_MacroSpaceReport();
return true;
case DispatchTypes::StatusType::MEM_MemoryChecksum:
case DispatchTypes::StatusType::MemoryChecksum:
_MacroChecksumReport(id);
return true;
case DispatchTypes::StatusType::DataIntegrity:
_DeviceStatusReport(TerminalReady);
return true;
case DispatchTypes::StatusType::MultipleSessionStatus:
_DeviceStatusReport(MultipleSessionsNotSupported);
return true;
default:
return false;
}
Expand Down Expand Up @@ -1609,15 +1639,14 @@ bool AdaptDispatch::RequestTerminalParameters(const DispatchTypes::ReportingPerm
}

// Routine Description:
// - DSR-OS - Reports the operating status back to the input channel
// - DSR - Transmits a device status report with a given parameter string.
// Arguments:
// - <none>
// - parameters - One or more parameter values representing the status
// Return Value:
// - <none>
void AdaptDispatch::_OperatingStatus() const
void AdaptDispatch::_DeviceStatusReport(const std::wstring_view parameters) const
{
// We always report a good operating condition.
_api.ReturnResponse(L"\x1b[0n");
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{}n"), parameters));
}

// Routine Description:
Expand Down
2 changes: 1 addition & 1 deletion src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ namespace Microsoft::Console::VirtualTerminal

void _DoLineFeed(TextBuffer& textBuffer, const bool withReturn, const bool wrapForced);

void _OperatingStatus() const;
void _DeviceStatusReport(const std::wstring_view parameters) const;
void _CursorPositionReport(const bool extendedReport);
void _MacroSpaceReport() const;
void _MacroChecksumReport(const VTParameter id) const;
Expand Down
62 changes: 51 additions & 11 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ class AdapterTest

Log::Comment(L"Test 1: Verify good operating condition.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::OS_OperatingStatus, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::OperatingStatus, {}));

_testGetSet->ValidateInputEvent(L"\x1b[0n");
}
Expand All @@ -1531,7 +1531,7 @@ class AdapterTest
coordCursorExpected.x++;
coordCursorExpected.y++;

VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CPR_CursorPositionReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CursorPositionReport, {}));

wchar_t pwszBuffer[50];

Expand All @@ -1555,7 +1555,7 @@ class AdapterTest
// Then note that VT is 1,1 based for the top left, so add 1. (The rest of the console uses 0,0 for array index bases.)
coordCursorExpectedFirst += til::point{ 1, 1 };

VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CPR_CursorPositionReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CursorPositionReport, {}));

auto cursorPos = _testGetSet->_textBuffer->GetCursor().GetPosition();
cursorPos.x++;
Expand All @@ -1565,7 +1565,7 @@ class AdapterTest
auto coordCursorExpectedSecond{ coordCursorExpectedFirst };
coordCursorExpectedSecond += til::point{ 1, 1 };

VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CPR_CursorPositionReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::CursorPositionReport, {}));

wchar_t pwszBuffer[50];

Expand Down Expand Up @@ -1594,7 +1594,7 @@ class AdapterTest
// Until we support paging (GH#13892) the reported page number should always be 1.
const auto pageExpected = 1;

VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::ExCPR_ExtendedCursorPositionReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::ExtendedCursorPositionReport, {}));

wchar_t pwszBuffer[50];
swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[?%d;%d;%dR", coordCursorExpected.y, coordCursorExpected.x, pageExpected);
Expand All @@ -1610,7 +1610,7 @@ class AdapterTest

Log::Comment(L"Test 1: Verify maximum space available");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MSR_MacroSpaceReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MacroSpaceReport, {}));

wchar_t pwszBuffer[50];
swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%zu*{", availableSpace);
Expand All @@ -1623,15 +1623,15 @@ class AdapterTest
_stateMachine->ProcessString(L"\033P2;0;0!z12345678\033\\");
_stateMachine->ProcessString(L"\033P3;0;0!z12345678\033\\");
_stateMachine->ProcessString(L"\033P4;0;0!z12345678\033\\");
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MSR_MacroSpaceReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MacroSpaceReport, {}));

swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%zu*{", availableSpace - 2);
_testGetSet->ValidateInputEvent(pwszBuffer);

Log::Comment(L"Test 3: Verify space reset");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->HardReset());
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MSR_MacroSpaceReport, {}));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MacroSpaceReport, {}));

swprintf_s(pwszBuffer, ARRAYSIZE(pwszBuffer), L"\x1b[%zu*{", availableSpace);
_testGetSet->ValidateInputEvent(pwszBuffer);
Expand All @@ -1643,7 +1643,7 @@ class AdapterTest

Log::Comment(L"Test 1: Verify initial checksum is 0");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MEM_MemoryChecksum, 12));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MemoryChecksum, 12));

_testGetSet->ValidateInputEvent(L"\033P12!~0000\033\\");

Expand All @@ -1652,7 +1652,7 @@ class AdapterTest
// Define a couple of text macros
_stateMachine->ProcessString(L"\033P1;0;0!zABCD\033\\");
_stateMachine->ProcessString(L"\033P2;0;0!zabcd\033\\");
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MEM_MemoryChecksum, 34));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MemoryChecksum, 34));

// Checksum is a 16-bit negated sum of the macro buffer characters.
const auto checksum = gsl::narrow_cast<uint16_t>(-('A' + 'B' + 'C' + 'D' + 'a' + 'b' + 'c' + 'd'));
Expand All @@ -1663,11 +1663,51 @@ class AdapterTest
Log::Comment(L"Test 3: Verify checksum resets to 0");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->HardReset());
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MEM_MemoryChecksum, 56));
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MemoryChecksum, 56));

_testGetSet->ValidateInputEvent(L"\033P56!~0000\033\\");
}

TEST_METHOD(DeviceStatus_PrivateStatusTests)
{
Log::Comment(L"Starting test...");

Log::Comment(L"Test 1: Verify printer is not connected.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::PrinterStatus, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?13n");

Log::Comment(L"Test 2: Verify UDKs are not supported.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::UserDefinedKeys, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?23n");

Log::Comment(L"Test 3: Verify PC keyboard with unknown dialect.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::KeyboardStatus, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?27;0;0;5n");

Log::Comment(L"Test 4: Verify locator is not connected.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::LocatorStatus, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?53n");

Log::Comment(L"Test 5: Verify locator type is unknown.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::LocatorIdentity, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?57;0n");

Log::Comment(L"Test 6: Verify terminal is ready.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::DataIntegrity, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?70n");

Log::Comment(L"Test 7: Verify multiple sessions are not supported.");
_testGetSet->PrepData();
VERIFY_IS_TRUE(_pDispatch->DeviceStatusReport(DispatchTypes::StatusType::MultipleSessionStatus, {}));
_testGetSet->ValidateInputEvent(L"\x1b[?83n");
}

TEST_METHOD(DeviceAttributesTests)
{
Log::Comment(L"Starting test...");
Expand Down
Loading

0 comments on commit eed60e6

Please sign in to comment.