Skip to content

Commit

Permalink
Support 8-bit C1 in keyboard and report sequences.
Browse files Browse the repository at this point in the history
  • Loading branch information
j4james committed Sep 19, 2024
1 parent dd829e2 commit a4067fc
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 72 deletions.
136 changes: 72 additions & 64 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1377,8 +1377,7 @@ void AdaptDispatch::RequestChecksumRectangularArea(const VTInt id, const VTInt p
}
}
}
const auto response = wil::str_printf<std::wstring>(L"\033P%d!~%04X\033\\", id, checksum);
_api.ReturnResponse(response);
_ReturnDcsResponse(wil::str_printf<std::wstring>(L"%d!~%04X", id, checksum));
}

// Routine Description:
Expand Down Expand Up @@ -1484,7 +1483,7 @@ void AdaptDispatch::DeviceAttributes()
// 32 = Text macros
// 42 = ISO Latin-2 character set

_api.ReturnResponse(L"\x1b[?61;4;6;7;14;21;22;23;24;28;32;42c");
_ReturnCsiResponse(L"?61;4;6;7;14;21;22;23;24;28;32;42c");
}

// Routine Description:
Expand All @@ -1496,7 +1495,7 @@ void AdaptDispatch::DeviceAttributes()
// - <none>
void AdaptDispatch::SecondaryDeviceAttributes()
{
_api.ReturnResponse(L"\x1b[>0;10;1c");
_ReturnCsiResponse(L">0;10;1c");
}

// Routine Description:
Expand All @@ -1506,7 +1505,7 @@ void AdaptDispatch::SecondaryDeviceAttributes()
// - <none>
void AdaptDispatch::TertiaryDeviceAttributes()
{
_api.ReturnResponse(L"\x1bP!|00000000\x1b\\");
_ReturnDcsResponse(L"!|00000000");
}

// Routine Description:
Expand Down Expand Up @@ -1544,10 +1543,10 @@ void AdaptDispatch::RequestTerminalParameters(const DispatchTypes::ReportingPerm
switch (permission)
{
case DispatchTypes::ReportingPermission::Unsolicited:
_api.ReturnResponse(L"\x1b[2;1;1;128;128;1;0x");
_ReturnCsiResponse(L"2;1;1;128;128;1;0x");
break;
case DispatchTypes::ReportingPermission::Solicited:
_api.ReturnResponse(L"\x1b[3;1;1;128;128;1;0x");
_ReturnCsiResponse(L"3;1;1;128;128;1;0x");
break;
default:
break;
Expand All @@ -1562,7 +1561,7 @@ void AdaptDispatch::RequestTerminalParameters(const DispatchTypes::ReportingPerm
// - <none>
void AdaptDispatch::_DeviceStatusReport(const wchar_t* parameters) const
{
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{}n"), parameters));
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"{}n"), parameters));
}

// Routine Description:
Expand Down Expand Up @@ -1598,14 +1597,12 @@ void AdaptDispatch::_CursorPositionReport(const bool extendedReport)
{
// An extended report also includes the page number.
const auto pageNumber = page.Number();
const auto response = wil::str_printf<std::wstring>(L"\x1b[?%d;%d;%dR", cursorPosition.y, cursorPosition.x, pageNumber);
_api.ReturnResponse(response);
_ReturnCsiResponse(wil::str_printf<std::wstring>(L"?%d;%d;%dR", cursorPosition.y, cursorPosition.x, pageNumber));
}
else
{
// The standard report only returns the cursor position.
const auto response = wil::str_printf<std::wstring>(L"\x1b[%d;%dR", cursorPosition.y, cursorPosition.x);
_api.ReturnResponse(response);
_ReturnCsiResponse(wil::str_printf<std::wstring>(L"%d;%dR", cursorPosition.y, cursorPosition.x));
}
}

Expand All @@ -1619,8 +1616,7 @@ void AdaptDispatch::_MacroSpaceReport() const
{
const auto spaceInBytes = _macroBuffer ? _macroBuffer->GetSpaceAvailable() : MacroBuffer::MAX_SPACE;
// The available space is measured in blocks of 16 bytes, so we need to divide by 16.
const auto response = wil::str_printf<std::wstring>(L"\x1b[%zu*{", spaceInBytes / 16);
_api.ReturnResponse(response);
_ReturnCsiResponse(wil::str_printf<std::wstring>(L"%zu*{", spaceInBytes / 16));
}

// Routine Description:
Expand All @@ -1633,8 +1629,7 @@ void AdaptDispatch::_MacroChecksumReport(const VTParameter id) const
{
const auto requestId = id.value_or(0);
const auto checksum = _macroBuffer ? _macroBuffer->CalculateChecksum() : 0;
const auto response = wil::str_printf<std::wstring>(L"\033P%d!~%04X\033\\", requestId, checksum);
_api.ReturnResponse(response);
_ReturnDcsResponse(wil::str_printf<std::wstring>(L"%d!~%04X", requestId, checksum));
}

// Routine Description:
Expand Down Expand Up @@ -1732,7 +1727,7 @@ void AdaptDispatch::RequestDisplayedExtent()
const auto height = page.Viewport().height();
const auto left = page.XPanOffset() + 1;
const auto top = page.YPanOffset() + 1;
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{};{};{};{};{}\"w"), height, width, left, top, page.Number()));
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"{};{};{};{};{}\"w"), height, width, left, top, page.Number()));
}

// Routine Description:
Expand Down Expand Up @@ -2068,7 +2063,7 @@ void AdaptDispatch::RequestMode(const DispatchTypes::ModeParams param)
prefix = L"?";
}

_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\x1b[{}{};{}$y"), prefix, mode, state));
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"{}{};{}$y"), prefix, mode, state));
}

// - DECKPAM, DECKPNM - Sets the keypad input mode to either Application mode or Numeric mode (true, false respectively)
Expand Down Expand Up @@ -3288,7 +3283,7 @@ void AdaptDispatch::RequestColorTableEntry(const size_t tableIndex)
{
const til::color c{ color };
// Scale values up to match xterm's 16-bit color report format.
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033]4;{};rgb:{:04x}/{:04x}/{:04x}\033\\"), tableIndex, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
_ReturnOscResponse(fmt::format(FMT_COMPILE(L"4;{};rgb:{:04x}/{:04x}/{:04x}"), tableIndex, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
}
}

Expand Down Expand Up @@ -3333,7 +3328,7 @@ void AdaptDispatch::RequestXtermColorResource(const size_t resource)
{
const til::color c{ color };
// Scale values up to match xterm's 16-bit color report format.
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033]{};rgb:{:04x}/{:04x}/{:04x}\033\\"), resource, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
_ReturnOscResponse(fmt::format(FMT_COMPILE(L"{};rgb:{:04x}/{:04x}/{:04x}"), resource, c.r * 0x0101, c.g * 0x0101, c.b * 0x0101));
}
}
}
Expand Down Expand Up @@ -3389,7 +3384,7 @@ void AdaptDispatch::WindowManipulation(const DispatchTypes::WindowManipulationTy

const auto reportSize = [&](const auto size) {
const auto reportType = function - 10;
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033[{};{};{}t"), reportType, size.height, size.width));
_ReturnCsiResponse(fmt::format(FMT_COMPILE(L"{};{};{}t"), reportType, size.height, size.width));
};

switch (function)
Expand Down Expand Up @@ -3856,7 +3851,7 @@ void AdaptDispatch::RequestUserPreferenceCharset()
{
const auto size = _termOutput.GetUserPreferenceCharsetSize();
const auto id = _termOutput.GetUserPreferenceCharsetId();
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033P{}!u{}\033\\"), (size == 96 ? 1 : 0), id));
_ReturnDcsResponse(fmt::format(FMT_COMPILE(L"{}!u{}"), (size == 96 ? 1 : 0), id));
}

// Method Description:
Expand Down Expand Up @@ -3988,9 +3983,9 @@ void AdaptDispatch::_ReportColorTable(const DispatchTypes::ColorModel colorModel
{
using namespace std::string_view_literals;

// A valid response always starts with DCS 2 $ s.
// A valid response always starts with 2 $ s.
fmt::basic_memory_buffer<wchar_t, TextColor::TABLE_SIZE * 18> response;
response.append(L"\033P2$s"sv);
response.append(L"2$s"sv);

const auto modelNumber = static_cast<int>(colorModel);
for (size_t colorNumber = 0; colorNumber < TextColor::TABLE_SIZE; colorNumber++)
Expand All @@ -4015,9 +4010,7 @@ void AdaptDispatch::_ReportColorTable(const DispatchTypes::ColorModel colorModel
}
}

// An ST ends the sequence.
response.append(L"\033\\"sv);
_api.ReturnResponse({ response.data(), response.size() });
_ReturnDcsResponse({ response.data(), response.size() });
}

// Method Description:
Expand Down Expand Up @@ -4120,7 +4113,7 @@ ITermDispatch::StringHandler AdaptDispatch::RequestSetting()
_ReportDECACSetting(VTParameter{ parameter });
break;
default:
_api.ReturnResponse(L"\033P0$r\033\\");
_ReturnDcsResponse(L"0$r");
break;
}
return false;
Expand Down Expand Up @@ -4159,10 +4152,10 @@ void AdaptDispatch::_ReportSGRSetting() const
{
using namespace std::string_view_literals;

// A valid response always starts with DCS 1 $ r.
// A valid response always starts with 1 $ r.
// Then the '0' parameter is to reset the SGR attributes to the defaults.
fmt::basic_memory_buffer<wchar_t, 64> response;
response.append(L"\033P1$r0"sv);
response.append(L"1$r0"sv);

const auto& attr = _pages.ActivePage().Attributes();
const auto ulStyle = attr.GetUnderlineStyle();
Expand Down Expand Up @@ -4214,9 +4207,9 @@ void AdaptDispatch::_ReportSGRSetting() const
addColor(40, attr.GetBackground());
addColor(50, attr.GetUnderlineColor());

// The 'm' indicates this is an SGR response, and ST ends the sequence.
response.append(L"m\033\\"sv);
_api.ReturnResponse({ response.data(), response.size() });
// The 'm' indicates this is an SGR response.
response.append(L"m"sv);
_ReturnDcsResponse({ response.data(), response.size() });
}

// Method Description:
Expand All @@ -4229,10 +4222,9 @@ void AdaptDispatch::_ReportDECSTBMSetting()
{
const auto page = _pages.ActivePage();
const auto [marginTop, marginBottom] = _GetVerticalMargins(page, false);
// A valid response always starts with DCS 1 $ r, the 'r' indicates this
// is a DECSTBM response, and ST ends the sequence.
// A valid response always starts with 1 $ r and the final 'r' indicates this is a DECSTBM response.
// VT origin is at 1,1 so we need to add 1 to these margins.
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033P1$r{};{}r\033\\"), marginTop + 1, marginBottom + 1));
_ReturnDcsResponse(fmt::format(FMT_COMPILE(L"1$r{};{}r"), marginTop + 1, marginBottom + 1));
}

// Method Description:
Expand All @@ -4245,10 +4237,9 @@ void AdaptDispatch::_ReportDECSLRMSetting()
{
const auto pageWidth = _pages.ActivePage().Width();
const auto [marginLeft, marginRight] = _GetHorizontalMargins(pageWidth);
// A valid response always starts with DCS 1 $ r, the 's' indicates this
// is a DECSLRM response, and ST ends the sequence.
// A valid response always starts with 1 $ r and the 's' indicates this is a DECSLRM response.
// VT origin is at 1,1 so we need to add 1 to these margins.
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033P1$r{};{}s\033\\"), marginLeft + 1, marginRight + 1));
_ReturnDcsResponse(fmt::format(FMT_COMPILE(L"1$r{};{}s"), marginLeft + 1, marginRight + 1));
}

// Method Description:
Expand All @@ -4261,26 +4252,26 @@ void AdaptDispatch::_ReportDECSCUSRSetting() const
{
const auto& cursor = _pages.ActivePage().Cursor();
const auto blinking = cursor.IsBlinkingAllowed();
// A valid response always starts with DCS 1 $ r. This is followed by a
// A valid response always starts with 1 $ r. This is followed by a
// number from 1 to 6 representing the cursor style. The ' q' indicates
// this is a DECSCUSR response, and ST ends the sequence.
// this is a DECSCUSR response.
switch (cursor.GetType())
{
case CursorType::FullBox:
_api.ReturnResponse(blinking ? L"\033P1$r1 q\033\\" : L"\033P1$r2 q\033\\");
_ReturnDcsResponse(blinking ? L"1$r1 q" : L"1$r2 q");
break;
case CursorType::Underscore:
_api.ReturnResponse(blinking ? L"\033P1$r3 q\033\\" : L"\033P1$r4 q\033\\");
_ReturnDcsResponse(blinking ? L"1$r3 q" : L"1$r4 q");
break;
case CursorType::VerticalBar:
_api.ReturnResponse(blinking ? L"\033P1$r5 q\033\\" : L"\033P1$r6 q\033\\");
_ReturnDcsResponse(blinking ? L"1$r5 q" : L"1$r6 q");
break;
default:
// If we have a non-standard style, this is likely because it's the
// user's chosen default style, so we report a default value of 0.
// That way, if an application later tries to restore the cursor with
// the returned value, it should be reset to its original state.
_api.ReturnResponse(L"\033P1$r0 q\033\\");
_ReturnDcsResponse(L"1$r0 q");
break;
}
}
Expand All @@ -4294,10 +4285,10 @@ void AdaptDispatch::_ReportDECSCUSRSetting() const
void AdaptDispatch::_ReportDECSCASetting() const
{
const auto isProtected = _pages.ActivePage().Attributes().IsProtected();
// A valid response always starts with DCS 1 $ r. This is followed by '1' if
// the protected attribute is set, or '0' if not. The '"q' indicates this is
// a DECSCA response, and ST ends the sequence.
_api.ReturnResponse(isProtected ? L"\033P1$r1\"q\033\\" : L"\033P1$r0\"q\033\\");
// A valid response always starts with 1 $ r. This is followed by '1' if the
// protected attribute is set, or '0' if not. The '"q' indicates this is a
// DECSCA response.
_ReturnDcsResponse(isProtected ? L"1$r1\"q" : L"1$r0\"q");
}

// Method Description:
Expand All @@ -4309,10 +4300,10 @@ void AdaptDispatch::_ReportDECSCASetting() const
void AdaptDispatch::_ReportDECSACESetting() const
{
const auto rectangularExtent = _modes.test(Mode::RectangularChangeExtent);
// A valid response always starts with DCS 1 $ r. This is followed by '2' if
// A valid response always starts with 1 $ r. This is followed by '2' if
// the extent is rectangular, or '1' if it's a stream. The '*x' indicates
// this is a DECSACE response, and ST ends the sequence.
_api.ReturnResponse(rectangularExtent ? L"\033P1$r2*x\033\\" : L"\033P1$r1*x\033\\");
// this is a DECSACE response.
_ReturnDcsResponse(rectangularExtent ? L"1$r2*x" : L"1$r1*x");
}

// Method Description:
Expand All @@ -4336,12 +4327,11 @@ void AdaptDispatch::_ReportDECACSetting(const VTInt itemNumber) const
bgIndex = _renderSettings.GetColorAliasIndex(ColorAlias::FrameBackground);
break;
default:
_api.ReturnResponse(L"\033P0$r\033\\");
_ReturnDcsResponse(L"0$r");
return;
}
// A valid response always starts with DCS 1 $ r, the ',|' indicates this
// is a DECAC response, and ST ends the sequence.
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"\033P1$r{};{};{},|\033\\"), itemNumber, fgIndex, bgIndex));
// A valid response always starts with 1 $ r and the ',|' indicates this is a DECAC response.
_ReturnDcsResponse(fmt::format(FMT_COMPILE(L"1$r{};{};{},|"), itemNumber, fgIndex, bgIndex));
}

// Routine Description:
Expand Down Expand Up @@ -4454,9 +4444,9 @@ void AdaptDispatch::_ReportCursorInformation()
const auto charset2 = _termOutput.GetCharsetId(2);
const auto charset3 = _termOutput.GetCharsetId(3);

// A valid response always starts with DCS 1 $ u and ends with ST.
// A valid response always starts with 1 $ u.
const auto response = fmt::format(
FMT_COMPILE(L"\033P1$u{};{};{};{};{};{};{};{};{};{}{}{}{}\033\\"),
FMT_COMPILE(L"1$u{};{};{};{};{};{};{};{};{};{}{}{}{}"),
cursorPosition.y,
cursorPosition.x,
page.Number(),
Expand All @@ -4470,7 +4460,7 @@ void AdaptDispatch::_ReportCursorInformation()
charset1,
charset2,
charset3);
_api.ReturnResponse({ response.data(), response.size() });
_ReturnDcsResponse({ response.data(), response.size() });
}

// Method Description:
Expand Down Expand Up @@ -4635,9 +4625,9 @@ void AdaptDispatch::_ReportTabStops()

using namespace std::string_view_literals;

// A valid response always starts with DCS 2 $ u.
// A valid response always starts with 2 $ u.
fmt::basic_memory_buffer<wchar_t, 64> response;
response.append(L"\033P2$u"sv);
response.append(L"2$u"sv);

auto need_separator = false;
for (auto column = 0; column < width; column++)
Expand All @@ -4650,9 +4640,7 @@ void AdaptDispatch::_ReportTabStops()
}
}

// An ST ends the sequence.
response.append(L"\033\\"sv);
_api.ReturnResponse({ response.data(), response.size() });
_ReturnDcsResponse({ response.data(), response.size() });
}

// Method Description:
Expand Down Expand Up @@ -4697,6 +4685,26 @@ ITermDispatch::StringHandler AdaptDispatch::_RestoreTabStops()
};
}

void AdaptDispatch::_ReturnCsiResponse(const std::wstring_view response) const
{
const auto csi = _terminalInput.GetInputMode(TerminalInput::Mode::SendC1) ? L"\x9B" : L"\x1B[";
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"{}{}"), csi, response));
}

void AdaptDispatch::_ReturnDcsResponse(const std::wstring_view response) const
{
const auto dcs = _terminalInput.GetInputMode(TerminalInput::Mode::SendC1) ? L"\x90" : L"\x1BP";
const auto st = _terminalInput.GetInputMode(TerminalInput::Mode::SendC1) ? L"\x9C" : L"\x1B\\";
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"{}{}{}"), dcs, response, st));
}

void AdaptDispatch::_ReturnOscResponse(const std::wstring_view response) const
{
const auto osc = _terminalInput.GetInputMode(TerminalInput::Mode::SendC1) ? L"\x9D" : L"\x1B]";
const auto st = _terminalInput.GetInputMode(TerminalInput::Mode::SendC1) ? L"\x9C" : L"\x1B\\";
_api.ReturnResponse(fmt::format(FMT_COMPILE(L"{}{}{}"), osc, response, st));
}

// Routine Description:
// - DECPS - Plays a sequence of musical notes.
// Arguments:
Expand Down
4 changes: 4 additions & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ namespace Microsoft::Console::VirtualTerminal
void _ReportTabStops();
StringHandler _RestoreTabStops();

void _ReturnCsiResponse(const std::wstring_view response) const;
void _ReturnDcsResponse(const std::wstring_view response) const;
void _ReturnOscResponse(const std::wstring_view response) const;

std::vector<uint8_t> _tabStopColumns;
bool _initDefaultTabStops = true;

Expand Down
Loading

0 comments on commit a4067fc

Please sign in to comment.