Refactor VT control sequence identification (#7304)

This PR changes the way VT control sequences are identified and
dispatched, to be more efficient and easier to extend. Instead of
parsing the intermediate characters into a vector, and then having to
identify a sequence using both that vector and the final char, we now
use just a single `uint64_t` value as the identifier.

The way the identifier is constructed is by taking the private parameter
prefix, each of the intermediate characters, and then the final
character, and shifting them into a 64-bit integer one byte at a time,
in reverse order. For example, the `DECTLTC` control has a private
parameter prefix of `?`, one intermediate of `'`, and a final character
of `s`. The ASCII values of those characters are `0x3F`, `0x27`, and
`0x73` respectively, and reversing them gets you 0x73273F, so that would
then be the identifier for the control.

The reason for storing them in reverse order, is because sometimes we
need to look at the first intermediate to determine the operation, and
treat the rest of the sequence as a kind of sub-identifier (the
character set designation sequences are one example of this). When in
reverse order, this can easily be achieved by masking off the low byte
to get the first intermediate, and then shifting the value right by 8
bits to get a new identifier with the rest of the sequence.

With 64 bits we have enough space for a private prefix, six
intermediates, and the final char, which is way more than we should ever
need (the _DEC STD 070_ specification recommends supporting at least
three intermediates, but in practice we're unlikely to see more than
two).

With this new way of identifying controls, it should now be possible for
every action code to be unique (for the most part). So I've also used
this PR to clean up the action codes a bit, splitting the codes for the
escape sequences from the control sequences, and sorting them into
alphabetical order (which also does a reasonable job of clustering
associated controls).

## Validation Steps Performed

I think the existing unit tests should be good enough to confirm that
all sequences are still being dispatched correctly. However, I've also
manually tested a number of sequences to make sure they were still
working as expected, in particular those that used intermediates, since
they were the most affected by the dispatch code refactoring.

Since these changes also affected the input state machine, I've done
some manual testing of the conpty keyboard handling (both with and
without the new Win32 input mode enabled) to make sure the keyboard VT
sequences were processed correctly. I've also manually tested the
various VT mouse modes in Vttest to confirm that they were still working
correctly too.

Closes #7276
This commit is contained in:
James Holderness 2020-08-18 19:57:52 +01:00 committed by GitHub
parent 5d082ffe67
commit 7fcff4d33a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 733 additions and 913 deletions

View file

@ -548,6 +548,7 @@ DECSCUSR
DECSED DECSED
DECSEL DECSEL
DECSET DECSET
DECSLPP
DECSLRM DECSLRM
DECSMBV DECSMBV
DECSMKR DECSMKR
@ -2579,6 +2580,7 @@ vstudio
vswhere vswhere
vtapp vtapp
VTE VTE
VTID
vtio vtio
vtmode vtmode
vtpipeterm vtpipeterm

View file

@ -3,6 +3,94 @@
#pragma once #pragma once
namespace Microsoft::Console::VirtualTerminal
{
class VTID
{
public:
template<size_t Length>
constexpr VTID(const char (&s)[Length]) :
_value{ _FromString(s) }
{
}
constexpr VTID(const uint64_t value) :
_value{ value }
{
}
constexpr operator uint64_t() const
{
return _value;
}
constexpr char operator[](const size_t offset) const
{
return SubSequence(offset)._value & 0xFF;
}
constexpr VTID SubSequence(const size_t offset) const
{
return _value >> (CHAR_BIT * offset);
}
private:
template<size_t Length>
static constexpr uint64_t _FromString(const char (&s)[Length])
{
static_assert(Length - 1 <= sizeof(_value));
uint64_t value = 0;
for (auto i = Length - 1; i-- > 0;)
{
value = (value << CHAR_BIT) + gsl::at(s, i);
}
return value;
}
uint64_t _value;
};
class VTIDBuilder
{
public:
void Clear() noexcept
{
_idAccumulator = 0;
_idShift = 0;
}
void AddIntermediate(const wchar_t intermediateChar) noexcept
{
if (_idShift + CHAR_BIT >= sizeof(_idAccumulator) * CHAR_BIT)
{
// If there is not enough space in the accumulator to add
// the intermediate and still have room left for the final,
// then we reset the accumulator to zero. This will result
// in an id with all zero intermediates, which shouldn't
// match anything.
_idAccumulator = 0;
}
else
{
// Otherwise we shift the intermediate so as to add it to the
// accumulator in the next available space, and then increment
// the shift by 8 bits in preparation for the next character.
_idAccumulator += (static_cast<uint64_t>(intermediateChar) << _idShift);
_idShift += CHAR_BIT;
}
}
VTID Finalize(const wchar_t finalChar) noexcept
{
return _idAccumulator + (static_cast<uint64_t>(finalChar) << _idShift);
}
private:
uint64_t _idAccumulator = 0;
size_t _idShift = 0;
};
}
namespace Microsoft::Console::VirtualTerminal::DispatchTypes namespace Microsoft::Console::VirtualTerminal::DispatchTypes
{ {
enum class EraseType : unsigned int enum class EraseType : unsigned int
@ -101,16 +189,16 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
W32IM_Win32InputMode = 9001 W32IM_Win32InputMode = 9001
}; };
namespace CharacterSets enum CharacterSets : uint64_t
{ {
constexpr auto DecSpecialGraphics = std::make_pair(L'0', L'\0'); DecSpecialGraphics = VTID("0"),
constexpr auto ASCII = std::make_pair(L'B', L'\0'); ASCII = VTID("B")
} };
enum CodingSystem : wchar_t enum CodingSystem : uint64_t
{ {
ISO2022 = L'@', ISO2022 = VTID("@"),
UTF8 = L'G' UTF8 = VTID("G")
}; };
enum TabClearType : unsigned short enum TabClearType : unsigned short

View file

@ -98,9 +98,9 @@ public:
virtual bool TertiaryDeviceAttributes() = 0; // DA3 virtual bool TertiaryDeviceAttributes() = 0; // DA3
virtual bool Vt52DeviceAttributes() = 0; // VT52 Identify virtual bool Vt52DeviceAttributes() = 0; // VT52 Identify
virtual bool DesignateCodingSystem(const wchar_t codingSystem) = 0; // DOCS virtual bool DesignateCodingSystem(const VTID codingSystem) = 0; // DOCS
virtual bool Designate94Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) = 0; // SCS virtual bool Designate94Charset(const size_t gsetNumber, const VTID charset) = 0; // SCS
virtual bool Designate96Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) = 0; // SCS virtual bool Designate96Charset(const size_t gsetNumber, const VTID charset) = 0; // SCS
virtual bool LockingShift(const size_t gsetNumber) = 0; // LS0, LS1, LS2, LS3 virtual bool LockingShift(const size_t gsetNumber) = 0; // LS0, LS1, LS2, LS3
virtual bool LockingShiftRight(const size_t gsetNumber) = 0; // LS1R, LS2R, LS3R virtual bool LockingShiftRight(const size_t gsetNumber) = 0; // LS1R, LS2R, LS3R
virtual bool SingleShift(const size_t gsetNumber) = 0; // SS2, SS3 virtual bool SingleShift(const size_t gsetNumber) = 0; // SS2, SS3

View file

@ -1670,7 +1670,7 @@ void AdaptDispatch::_InitTabStopsForWidth(const size_t width)
// - codingSystem - The coding system that will be selected. // - codingSystem - The coding system that will be selected.
// Return value: // Return value:
// True if handled successfully. False otherwise. // True if handled successfully. False otherwise.
bool AdaptDispatch::DesignateCodingSystem(const wchar_t codingSystem) bool AdaptDispatch::DesignateCodingSystem(const VTID codingSystem)
{ {
// If we haven't previously saved the initial code page, do so now. // If we haven't previously saved the initial code page, do so now.
// This will be used to restore the code page in response to a reset. // This will be used to restore the code page in response to a reset.
@ -1712,10 +1712,10 @@ bool AdaptDispatch::DesignateCodingSystem(const wchar_t codingSystem)
// If the specified charset is unsupported, we do nothing (remain on the current one) // If the specified charset is unsupported, we do nothing (remain on the current one)
//Arguments: //Arguments:
// - gsetNumber - The G-set into which the charset will be selected. // - gsetNumber - The G-set into which the charset will be selected.
// - charset - The characters indicating the charset that will be used. // - charset - The identifier indicating the charset that will be used.
// Return value: // Return value:
// True if handled successfully. False otherwise. // True if handled successfully. False otherwise.
bool AdaptDispatch::Designate94Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) bool AdaptDispatch::Designate94Charset(const size_t gsetNumber, const VTID charset)
{ {
return _termOutput.Designate94Charset(gsetNumber, charset); return _termOutput.Designate94Charset(gsetNumber, charset);
} }
@ -1727,10 +1727,10 @@ bool AdaptDispatch::Designate94Charset(const size_t gsetNumber, const std::pair<
// If the specified charset is unsupported, we do nothing (remain on the current one) // If the specified charset is unsupported, we do nothing (remain on the current one)
//Arguments: //Arguments:
// - gsetNumber - The G-set into which the charset will be selected. // - gsetNumber - The G-set into which the charset will be selected.
// - charset - The characters indicating the charset that will be used. // - charset - The identifier indicating the charset that will be used.
// Return value: // Return value:
// True if handled successfully. False otherwise. // True if handled successfully. False otherwise.
bool AdaptDispatch::Designate96Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) bool AdaptDispatch::Designate96Charset(const size_t gsetNumber, const VTID charset)
{ {
return _termOutput.Designate96Charset(gsetNumber, charset); return _termOutput.Designate96Charset(gsetNumber, charset);
} }

View file

@ -89,9 +89,9 @@ namespace Microsoft::Console::VirtualTerminal
bool ForwardTab(const size_t numTabs) override; // CHT, HT bool ForwardTab(const size_t numTabs) override; // CHT, HT
bool BackwardsTab(const size_t numTabs) override; // CBT bool BackwardsTab(const size_t numTabs) override; // CBT
bool TabClear(const size_t clearType) override; // TBC bool TabClear(const size_t clearType) override; // TBC
bool DesignateCodingSystem(const wchar_t codingSystem) override; // DOCS bool DesignateCodingSystem(const VTID codingSystem) override; // DOCS
bool Designate94Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) override; // SCS bool Designate94Charset(const size_t gsetNumber, const VTID charset) override; // SCS
bool Designate96Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) override; // SCS bool Designate96Charset(const size_t gsetNumber, const VTID charset) override; // SCS
bool LockingShift(const size_t gsetNumber) override; // LS0, LS1, LS2, LS3 bool LockingShift(const size_t gsetNumber) override; // LS0, LS1, LS2, LS3
bool LockingShiftRight(const size_t gsetNumber) override; // LS1R, LS2R, LS3R bool LockingShiftRight(const size_t gsetNumber) override; // LS1R, LS2R, LS3R
bool SingleShift(const size_t gsetNumber) override; // SS2, SS3 bool SingleShift(const size_t gsetNumber) override; // SS2, SS3

View file

@ -92,9 +92,9 @@ public:
bool TertiaryDeviceAttributes() noexcept override { return false; } // DA3 bool TertiaryDeviceAttributes() noexcept override { return false; } // DA3
bool Vt52DeviceAttributes() noexcept override { return false; } // VT52 Identify bool Vt52DeviceAttributes() noexcept override { return false; } // VT52 Identify
bool DesignateCodingSystem(const wchar_t /*codingSystem*/) noexcept override { return false; } // DOCS bool DesignateCodingSystem(const VTID /*codingSystem*/) noexcept override { return false; } // DOCS
bool Designate94Charset(const size_t /*gsetNumber*/, const std::pair<wchar_t, wchar_t> /*charset*/) noexcept override { return false; } // SCS bool Designate94Charset(const size_t /*gsetNumber*/, const VTID /*charset*/) noexcept override { return false; } // SCS
bool Designate96Charset(const size_t /*gsetNumber*/, const std::pair<wchar_t, wchar_t> /*charset*/) noexcept override { return false; } // SCS bool Designate96Charset(const size_t /*gsetNumber*/, const VTID /*charset*/) noexcept override { return false; } // SCS
bool LockingShift(const size_t /*gsetNumber*/) noexcept override { return false; } // LS0, LS1, LS2, LS3 bool LockingShift(const size_t /*gsetNumber*/) noexcept override { return false; } // LS0, LS1, LS2, LS3
bool LockingShiftRight(const size_t /*gsetNumber*/) noexcept override { return false; } // LS1R, LS2R, LS3R bool LockingShiftRight(const size_t /*gsetNumber*/) noexcept override { return false; } // LS1R, LS2R, LS3R
bool SingleShift(const size_t /*gsetNumber*/) noexcept override { return false; } // SS2, SS3 bool SingleShift(const size_t /*gsetNumber*/) noexcept override { return false; } // SS2, SS3

View file

@ -17,107 +17,89 @@ TerminalOutput::TerminalOutput() noexcept
_gsetTranslationTables.at(3) = Latin1; _gsetTranslationTables.at(3) = Latin1;
} }
bool TerminalOutput::Designate94Charset(size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) bool TerminalOutput::Designate94Charset(size_t gsetNumber, const VTID charset)
{ {
switch (charset.first) switch (charset)
{ {
case L'B': // US ASCII case VTID("B"): // US ASCII
case L'1': // Alternate Character ROM case VTID("1"): // Alternate Character ROM
return _SetTranslationTable(gsetNumber, Ascii); return _SetTranslationTable(gsetNumber, Ascii);
case L'0': // DEC Special Graphics case VTID("0"): // DEC Special Graphics
case L'2': // Alternate Character ROM Special Graphics case VTID("2"): // Alternate Character ROM Special Graphics
return _SetTranslationTable(gsetNumber, DecSpecialGraphics); return _SetTranslationTable(gsetNumber, DecSpecialGraphics);
case L'<': // DEC Supplemental case VTID("<"): // DEC Supplemental
return _SetTranslationTable(gsetNumber, DecSupplemental); return _SetTranslationTable(gsetNumber, DecSupplemental);
case L'A': // British NRCS case VTID("A"): // British NRCS
return _SetTranslationTable(gsetNumber, BritishNrcs); return _SetTranslationTable(gsetNumber, BritishNrcs);
case L'4': // Dutch NRCS case VTID("4"): // Dutch NRCS
return _SetTranslationTable(gsetNumber, DutchNrcs); return _SetTranslationTable(gsetNumber, DutchNrcs);
case L'5': // Finnish NRCS case VTID("5"): // Finnish NRCS
case L'C': // (fallback) case VTID("C"): // (fallback)
return _SetTranslationTable(gsetNumber, FinnishNrcs); return _SetTranslationTable(gsetNumber, FinnishNrcs);
case L'R': // French NRCS case VTID("R"): // French NRCS
return _SetTranslationTable(gsetNumber, FrenchNrcs); return _SetTranslationTable(gsetNumber, FrenchNrcs);
case L'f': // French NRCS (ISO update) case VTID("f"): // French NRCS (ISO update)
return _SetTranslationTable(gsetNumber, FrenchNrcsIso); return _SetTranslationTable(gsetNumber, FrenchNrcsIso);
case L'9': // French Canadian NRCS case VTID("9"): // French Canadian NRCS
case L'Q': // (fallback) case VTID("Q"): // (fallback)
return _SetTranslationTable(gsetNumber, FrenchCanadianNrcs); return _SetTranslationTable(gsetNumber, FrenchCanadianNrcs);
case L'K': // German NRCS case VTID("K"): // German NRCS
return _SetTranslationTable(gsetNumber, GermanNrcs); return _SetTranslationTable(gsetNumber, GermanNrcs);
case L'Y': // Italian NRCS case VTID("Y"): // Italian NRCS
return _SetTranslationTable(gsetNumber, ItalianNrcs); return _SetTranslationTable(gsetNumber, ItalianNrcs);
case L'6': // Norwegian/Danish NRCS case VTID("6"): // Norwegian/Danish NRCS
case L'E': // (fallback) case VTID("E"): // (fallback)
return _SetTranslationTable(gsetNumber, NorwegianDanishNrcs); return _SetTranslationTable(gsetNumber, NorwegianDanishNrcs);
case L'`': // Norwegian/Danish NRCS (ISO standard) case VTID("`"): // Norwegian/Danish NRCS (ISO standard)
return _SetTranslationTable(gsetNumber, NorwegianDanishNrcsIso); return _SetTranslationTable(gsetNumber, NorwegianDanishNrcsIso);
case L'Z': // Spanish NRCS case VTID("Z"): // Spanish NRCS
return _SetTranslationTable(gsetNumber, SpanishNrcs); return _SetTranslationTable(gsetNumber, SpanishNrcs);
case L'7': // Swedish NRCS case VTID("7"): // Swedish NRCS
case L'H': // (fallback) case VTID("H"): // (fallback)
return _SetTranslationTable(gsetNumber, SwedishNrcs); return _SetTranslationTable(gsetNumber, SwedishNrcs);
case L'=': // Swiss NRCS case VTID("="): // Swiss NRCS
return _SetTranslationTable(gsetNumber, SwissNrcs); return _SetTranslationTable(gsetNumber, SwissNrcs);
case L'&': case VTID("&4"): // DEC Cyrillic
switch (charset.second) return _SetTranslationTable(gsetNumber, DecCyrillic);
{ case VTID("&5"): // Russian NRCS
case L'4': // DEC Cyrillic return _SetTranslationTable(gsetNumber, RussianNrcs);
return _SetTranslationTable(gsetNumber, DecCyrillic); case VTID("\"?"): // DEC Greek
case L'5': // Russian NRCS return _SetTranslationTable(gsetNumber, DecGreek);
return _SetTranslationTable(gsetNumber, RussianNrcs); case VTID("\">"): // Greek NRCS
default: return _SetTranslationTable(gsetNumber, GreekNrcs);
return false; case VTID("\"4"): // DEC Hebrew
} return _SetTranslationTable(gsetNumber, DecHebrew);
case L'"': case VTID("%="): // Hebrew NRCS
switch (charset.second) return _SetTranslationTable(gsetNumber, HebrewNrcs);
{ case VTID("%0"): // DEC Turkish
case L'?': // DEC Greek return _SetTranslationTable(gsetNumber, DecTurkish);
return _SetTranslationTable(gsetNumber, DecGreek); case VTID("%2"): // Turkish NRCS
case L'>': // Greek NRCS return _SetTranslationTable(gsetNumber, TurkishNrcs);
return _SetTranslationTable(gsetNumber, GreekNrcs); case VTID("%5"): // DEC Supplemental
case L'4': // DEC Hebrew return _SetTranslationTable(gsetNumber, DecSupplemental);
return _SetTranslationTable(gsetNumber, DecHebrew); case VTID("%6"): // Portuguese NRCS
default: return _SetTranslationTable(gsetNumber, PortugueseNrcs);
return false;
}
case L'%':
switch (charset.second)
{
case L'=': // Hebrew NRCS
return _SetTranslationTable(gsetNumber, HebrewNrcs);
case L'0': // DEC Turkish
return _SetTranslationTable(gsetNumber, DecTurkish);
case L'2': // Turkish NRCS
return _SetTranslationTable(gsetNumber, TurkishNrcs);
case L'5': // DEC Supplemental
return _SetTranslationTable(gsetNumber, DecSupplemental);
case L'6': // Portuguese NRCS
return _SetTranslationTable(gsetNumber, PortugueseNrcs);
default:
return false;
}
default: default:
return false; return false;
} }
} }
bool TerminalOutput::Designate96Charset(size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset) bool TerminalOutput::Designate96Charset(size_t gsetNumber, const VTID charset)
{ {
switch (charset.first) switch (charset)
{ {
case L'A': // ISO Latin-1 Supplemental case VTID("A"): // ISO Latin-1 Supplemental
case L'<': // (UPSS when assigned to Latin-1) case VTID("<"): // (UPSS when assigned to Latin-1)
return _SetTranslationTable(gsetNumber, Latin1); return _SetTranslationTable(gsetNumber, Latin1);
case L'B': // ISO Latin-2 Supplemental case VTID("B"): // ISO Latin-2 Supplemental
return _SetTranslationTable(gsetNumber, Latin2); return _SetTranslationTable(gsetNumber, Latin2);
case L'L': // ISO Latin-Cyrillic Supplemental case VTID("L"): // ISO Latin-Cyrillic Supplemental
return _SetTranslationTable(gsetNumber, LatinCyrillic); return _SetTranslationTable(gsetNumber, LatinCyrillic);
case L'F': // ISO Latin-Greek Supplemental case VTID("F"): // ISO Latin-Greek Supplemental
return _SetTranslationTable(gsetNumber, LatinGreek); return _SetTranslationTable(gsetNumber, LatinGreek);
case L'H': // ISO Latin-Hebrew Supplemental case VTID("H"): // ISO Latin-Hebrew Supplemental
return _SetTranslationTable(gsetNumber, LatinHebrew); return _SetTranslationTable(gsetNumber, LatinHebrew);
case L'M': // ISO Latin-5 Supplemental case VTID("M"): // ISO Latin-5 Supplemental
return _SetTranslationTable(gsetNumber, Latin5); return _SetTranslationTable(gsetNumber, Latin5);
default: default:
return false; return false;

View file

@ -26,8 +26,8 @@ namespace Microsoft::Console::VirtualTerminal
TerminalOutput() noexcept; TerminalOutput() noexcept;
wchar_t TranslateKey(const wchar_t wch) const noexcept; wchar_t TranslateKey(const wchar_t wch) const noexcept;
bool Designate94Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset); bool Designate94Charset(const size_t gsetNumber, const VTID charset);
bool Designate96Charset(const size_t gsetNumber, const std::pair<wchar_t, wchar_t> charset); bool Designate96Charset(const size_t gsetNumber, const VTID charset);
bool LockingShift(const size_t gsetNumber); bool LockingShift(const size_t gsetNumber);
bool LockingShiftRight(const size_t gsetNumber); bool LockingShiftRight(const size_t gsetNumber);
bool SingleShift(const size_t gsetNumber); bool SingleShift(const size_t gsetNumber);

View file

@ -12,6 +12,9 @@ Abstract:
the existing VT parsing. the existing VT parsing.
*/ */
#pragma once #pragma once
#include "../adapter/DispatchTypes.hpp"
namespace Microsoft::Console::VirtualTerminal namespace Microsoft::Console::VirtualTerminal
{ {
class IStateMachineEngine class IStateMachineEngine
@ -30,14 +33,9 @@ namespace Microsoft::Console::VirtualTerminal
virtual bool ActionPassThroughString(const std::wstring_view string) = 0; virtual bool ActionPassThroughString(const std::wstring_view string) = 0;
virtual bool ActionEscDispatch(const wchar_t wch, virtual bool ActionEscDispatch(const VTID id) = 0;
const gsl::span<const wchar_t> intermediates) = 0; virtual bool ActionVt52EscDispatch(const VTID id, const gsl::span<const size_t> parameters) = 0;
virtual bool ActionVt52EscDispatch(const wchar_t wch, virtual bool ActionCsiDispatch(const VTID id, const gsl::span<const size_t> parameters) = 0;
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) = 0;
virtual bool ActionCsiDispatch(const wchar_t wch,
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) = 0;
virtual bool ActionClear() = 0; virtual bool ActionClear() = 0;

View file

@ -34,9 +34,9 @@ static constexpr std::array<CsiToVkey, 10> s_csiMap = {
CsiToVkey{ CsiActionCodes::CSI_F4, VK_F4 } CsiToVkey{ CsiActionCodes::CSI_F4, VK_F4 }
}; };
static bool operator==(const CsiToVkey& pair, const CsiActionCodes code) noexcept static bool operator==(const CsiToVkey& pair, const VTID id) noexcept
{ {
return pair.action == code; return pair.action == id;
} }
struct GenericToVkey struct GenericToVkey
@ -298,12 +298,10 @@ bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view st
// a simple escape sequence. These sequences traditionally start with ESC // a simple escape sequence. These sequences traditionally start with ESC
// and a simple letter. No complicated parameters. // and a simple letter. No complicated parameters.
// Arguments: // Arguments:
// - wch - Character to dispatch. // - id - Identifier of the escape sequence to dispatch.
// - intermediates - Intermediate characters in the sequence
// Return Value: // Return Value:
// - true iff we successfully dispatched the sequence. // - true iff we successfully dispatched the sequence.
bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch, bool InputStateMachineEngine::ActionEscDispatch(const VTID id)
const gsl::span<const wchar_t> /*intermediates*/)
{ {
if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue) if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue)
{ {
@ -312,6 +310,9 @@ bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch,
bool success = false; bool success = false;
// There are no intermediates, so the id is effectively the final char.
const wchar_t wch = gsl::narrow_cast<wchar_t>(id);
// 0x7f is DEL, which we treat effectively the same as a ctrl character. // 0x7f is DEL, which we treat effectively the same as a ctrl character.
if (wch == 0x7f) if (wch == 0x7f)
{ {
@ -339,14 +340,11 @@ bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch,
// a VT52 escape sequence. These sequences start with ESC and a single letter, // a VT52 escape sequence. These sequences start with ESC and a single letter,
// sometimes followed by parameters. // sometimes followed by parameters.
// Arguments: // Arguments:
// - wch - Character to dispatch. // - id - Identifier of the VT52 sequence to dispatch.
// - intermediates - Intermediate characters in the sequence.
// - parameters - Set of parameters collected while parsing the sequence. // - parameters - Set of parameters collected while parsing the sequence.
// Return Value: // Return Value:
// - true iff we successfully dispatched the sequence. // - true iff we successfully dispatched the sequence.
bool InputStateMachineEngine::ActionVt52EscDispatch(const wchar_t /*wch*/, bool InputStateMachineEngine::ActionVt52EscDispatch(const VTID /*id*/, const gsl::span<const size_t> /*parameters*/) noexcept
const gsl::span<const wchar_t> /*intermediates*/,
const gsl::span<const size_t> /*parameters*/) noexcept
{ {
// VT52 escape sequences are not used in the input state machine. // VT52 escape sequences are not used in the input state machine.
return false; return false;
@ -357,17 +355,12 @@ bool InputStateMachineEngine::ActionVt52EscDispatch(const wchar_t /*wch*/,
// a control sequence. These sequences perform various API-type commands // a control sequence. These sequences perform various API-type commands
// that can include many parameters. // that can include many parameters.
// Arguments: // Arguments:
// - wch - Character to dispatch. // - id - Identifier of the control sequence to dispatch.
// - intermediates - Intermediate characters in the sequence
// - parameters - set of numeric parameters collected while parsing the sequence. // - parameters - set of numeric parameters collected while parsing the sequence.
// Return Value: // Return Value:
// - true iff we successfully dispatched the sequence. // - true iff we successfully dispatched the sequence.
bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const gsl::span<const size_t> parameters)
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters)
{ {
const auto actionCode = static_cast<CsiActionCodes>(wch);
// GH#4999 - If the client was in VT input mode, but we received a // GH#4999 - If the client was in VT input mode, but we received a
// win32-input-mode sequence, then _don't_ passthrough the sequence to the // win32-input-mode sequence, then _don't_ passthrough the sequence to the
// client. It's impossibly unlikely that the client actually wanted // client. It's impossibly unlikely that the client actually wanted
@ -376,7 +369,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
// client reads it. // client reads it.
if (_pDispatch->IsVtInputEnabled() && if (_pDispatch->IsVtInputEnabled() &&
_pfnFlushToInputQueue && _pfnFlushToInputQueue &&
actionCode != CsiActionCodes::Win32KeyboardInput) id != CsiActionCodes::Win32KeyboardInput)
{ {
return _pfnFlushToInputQueue(); return _pfnFlushToInputQueue();
} }
@ -392,32 +385,22 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
const auto remainingArgs = parameters.size() > 1 ? parameters.subspan(1) : gsl::span<const size_t>{}; const auto remainingArgs = parameters.size() > 1 ? parameters.subspan(1) : gsl::span<const size_t>{};
bool success = false; bool success = false;
// Handle intermediate characters, if any switch (id)
if (!intermediates.empty())
{ {
switch (static_cast<CsiIntermediateCodes>(til::at(intermediates, 0))) case CsiActionCodes::MouseDown:
{ case CsiActionCodes::MouseUp:
case CsiIntermediateCodes::MOUSE_SGR: {
{ DWORD buttonState = 0;
DWORD buttonState = 0; DWORD eventFlags = 0;
DWORD eventFlags = 0; modifierState = _GetSGRMouseModifierState(parameters);
modifierState = _GetSGRMouseModifierState(parameters); success = _GetSGRXYPosition(parameters, row, col);
success = _GetSGRXYPosition(parameters, row, col);
// we need _UpdateSGRMouseButtonState() on the left side here because we _always_ should be updating our state // we need _UpdateSGRMouseButtonState() on the left side here because we _always_ should be updating our state
// even if we failed to parse a portion of this sequence. // even if we failed to parse a portion of this sequence.
success = _UpdateSGRMouseButtonState(wch, parameters, buttonState, eventFlags) && success; success = _UpdateSGRMouseButtonState(id, parameters, buttonState, eventFlags) && success;
success = success && _WriteMouseEvent(col, row, buttonState, modifierState, eventFlags); success = success && _WriteMouseEvent(col, row, buttonState, modifierState, eventFlags);
break;
}
default:
success = false;
break;
}
return success; return success;
} }
switch (actionCode)
{
case CsiActionCodes::Generic: case CsiActionCodes::Generic:
modifierState = _GetGenericKeysModifierState(parameters); modifierState = _GetGenericKeysModifierState(parameters);
success = _GetGenericVkey(parameters, vkey); success = _GetGenericVkey(parameters, vkey);
@ -443,8 +426,8 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
case CsiActionCodes::CSI_F1: case CsiActionCodes::CSI_F1:
case CsiActionCodes::CSI_F2: case CsiActionCodes::CSI_F2:
case CsiActionCodes::CSI_F4: case CsiActionCodes::CSI_F4:
success = _GetCursorKeysVkey(wch, vkey); success = _GetCursorKeysVkey(id, vkey);
modifierState = _GetCursorKeysModifierState(parameters, static_cast<CsiActionCodes>(wch)); modifierState = _GetCursorKeysModifierState(parameters, id);
break; break;
case CsiActionCodes::CursorBackTab: case CsiActionCodes::CursorBackTab:
modifierState = SHIFT_PRESSED; modifierState = SHIFT_PRESSED;
@ -464,7 +447,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch,
if (success) if (success)
{ {
switch (static_cast<CsiActionCodes>(wch)) switch (id)
{ {
// case CsiActionCodes::DSR_DeviceStatusReportResponse: // case CsiActionCodes::DSR_DeviceStatusReportResponse:
case CsiActionCodes::CSI_F3: case CsiActionCodes::CSI_F3:
@ -799,10 +782,10 @@ bool InputStateMachineEngine::_WriteMouseEvent(const size_t column, const size_t
// sequence. This is for Arrow keys, Home, End, etc. // sequence. This is for Arrow keys, Home, End, etc.
// Arguments: // Arguments:
// - parameters - the set of parameters to get the modifier state from. // - parameters - the set of parameters to get the modifier state from.
// - actionCode - the actionCode for the sequence we're operating on. // - id - the identifier for the sequence we're operating on.
// Return Value: // Return Value:
// - the INPUT_RECORD compatible modifier state. // - the INPUT_RECORD compatible modifier state.
DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span<const size_t> parameters, const CsiActionCodes actionCode) noexcept DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span<const size_t> parameters, const VTID id) noexcept
{ {
DWORD modifiers = 0; DWORD modifiers = 0;
if (_IsModified(parameters.size()) && parameters.size() >= 2) if (_IsModified(parameters.size()) && parameters.size() >= 2)
@ -816,7 +799,7 @@ DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span<const
// of the keypad; and the divide (/) and ENTER keys in the keypad. // of the keypad; and the divide (/) and ENTER keys in the keypad.
// This snippet detects the direction keys + HOME + END // This snippet detects the direction keys + HOME + END
// actionCode should be one of the above, so just make sure it's not a CSI_F# code // actionCode should be one of the above, so just make sure it's not a CSI_F# code
if (actionCode < CsiActionCodes::CSI_F1 || actionCode > CsiActionCodes::CSI_F4) if (id < CsiActionCodes::CSI_F1 || id > CsiActionCodes::CSI_F4)
{ {
WI_SetFlag(modifiers, ENHANCED_KEY); WI_SetFlag(modifiers, ENHANCED_KEY);
} }
@ -917,13 +900,13 @@ DWORD InputStateMachineEngine::_GetModifier(const size_t modifierParam) noexcept
// - Here, we refer to and maintain the global state of our mouse. // - Here, we refer to and maintain the global state of our mouse.
// - Mouse wheel events are added at the end to keep them out of the global state // - Mouse wheel events are added at the end to keep them out of the global state
// Arguments: // Arguments:
// - wch: the wchar_t representing whether the button was pressed or released // - id: the sequence identifier representing whether the button was pressed or released
// - parameters: the wchar_t to get the mapped vkey of. Represents the direction of the button (down vs up) // - parameters: the wchar_t to get the mapped vkey of. Represents the direction of the button (down vs up)
// - buttonState: Receives the button state for the record // - buttonState: Receives the button state for the record
// - eventFlags: Receives the special mouse events for the record // - eventFlags: Receives the special mouse events for the record
// Return Value: // Return Value:
// true iff we were able to synthesize buttonState // true iff we were able to synthesize buttonState
bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const wchar_t wch, bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id,
const gsl::span<const size_t> parameters, const gsl::span<const size_t> parameters,
DWORD& buttonState, DWORD& buttonState,
DWORD& eventFlags) noexcept DWORD& eventFlags) noexcept
@ -987,7 +970,7 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const wchar_t wch,
// Step 2: Decide whether to set or clear that button's bit // Step 2: Decide whether to set or clear that button's bit
// NOTE: WI_SetFlag/WI_ClearFlag can't be used here because buttonFlag would have to be a compile-time constant // NOTE: WI_SetFlag/WI_ClearFlag can't be used here because buttonFlag would have to be a compile-time constant
switch (static_cast<CsiActionCodes>(wch)) switch (id)
{ {
case CsiActionCodes::MouseDown: case CsiActionCodes::MouseDown:
// set flag // set flag
@ -1049,15 +1032,15 @@ bool InputStateMachineEngine::_GetGenericVkey(const gsl::span<const size_t> para
// Method Description: // Method Description:
// - Gets the Vkey from the CSI codes table associated with a particular character. // - Gets the Vkey from the CSI codes table associated with a particular character.
// Arguments: // Arguments:
// - wch: the wchar_t to get the mapped vkey of. // - id: the sequence identifier to get the mapped vkey of.
// - vkey: Receives the vkey // - vkey: Receives the vkey
// Return Value: // Return Value:
// true iff we found the key // true iff we found the key
bool InputStateMachineEngine::_GetCursorKeysVkey(const wchar_t wch, short& vkey) const bool InputStateMachineEngine::_GetCursorKeysVkey(const VTID id, short& vkey) const
{ {
vkey = 0; vkey = 0;
const auto mapping = std::find(s_csiMap.cbegin(), s_csiMap.cend(), (CsiActionCodes)wch); const auto mapping = std::find(s_csiMap.cbegin(), s_csiMap.cend(), id);
if (mapping != s_csiMap.end()) if (mapping != s_csiMap.end())
{ {
vkey = mapping->vkey; vkey = mapping->vkey;

View file

@ -50,30 +50,25 @@ namespace Microsoft::Console::VirtualTerminal
// CAPSLOCK_ON 0x0080 // CAPSLOCK_ON 0x0080
// ENHANCED_KEY 0x0100 // ENHANCED_KEY 0x0100
enum CsiIntermediateCodes : wchar_t enum CsiActionCodes : uint64_t
{ {
MOUSE_SGR = L'<', ArrowUp = VTID("A"),
}; ArrowDown = VTID("B"),
ArrowRight = VTID("C"),
enum class CsiActionCodes : wchar_t ArrowLeft = VTID("D"),
{ Home = VTID("H"),
ArrowUp = L'A', End = VTID("F"),
ArrowDown = L'B', MouseDown = VTID("<M"),
ArrowRight = L'C', MouseUp = VTID("<m"),
ArrowLeft = L'D', Generic = VTID("~"), // Used for a whole bunch of possible keys
Home = L'H', CSI_F1 = VTID("P"),
End = L'F', CSI_F2 = VTID("Q"),
MouseDown = L'M', CSI_F3 = VTID("R"), // Both F3 and DSR are on R.
MouseUp = L'm', // DSR_DeviceStatusReportResponse = VTID("R"),
Generic = L'~', // Used for a whole bunch of possible keys CSI_F4 = VTID("S"),
CSI_F1 = L'P', DTTERM_WindowManipulation = VTID("t"),
CSI_F2 = L'Q', CursorBackTab = VTID("Z"),
CSI_F3 = L'R', // Both F3 and DSR are on R. Win32KeyboardInput = VTID("_")
// DSR_DeviceStatusReportResponse = L'R',
CSI_F4 = L'S',
DTTERM_WindowManipulation = L't',
CursorBackTab = L'Z',
Win32KeyboardInput = L'_'
}; };
enum CsiMouseButtonCodes : unsigned short enum CsiMouseButtonCodes : unsigned short
@ -145,16 +140,11 @@ namespace Microsoft::Console::VirtualTerminal
bool ActionPassThroughString(const std::wstring_view string) override; bool ActionPassThroughString(const std::wstring_view string) override;
bool ActionEscDispatch(const wchar_t wch, bool ActionEscDispatch(const VTID id) override;
const gsl::span<const wchar_t> intermediates) override;
bool ActionVt52EscDispatch(const wchar_t wch, bool ActionVt52EscDispatch(const VTID id, const gsl::span<const size_t> parameters) noexcept override;
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) noexcept override;
bool ActionCsiDispatch(const wchar_t wch, bool ActionCsiDispatch(const VTID id, const gsl::span<const size_t> parameters) override;
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) override;
bool ActionClear() noexcept override; bool ActionClear() noexcept override;
@ -180,7 +170,7 @@ namespace Microsoft::Console::VirtualTerminal
bool _lookingForDSR; bool _lookingForDSR;
DWORD _mouseButtonState = 0; DWORD _mouseButtonState = 0;
DWORD _GetCursorKeysModifierState(const gsl::span<const size_t> parameters, const CsiActionCodes actionCode) noexcept; DWORD _GetCursorKeysModifierState(const gsl::span<const size_t> parameters, const VTID id) noexcept;
DWORD _GetGenericKeysModifierState(const gsl::span<const size_t> parameters) noexcept; DWORD _GetGenericKeysModifierState(const gsl::span<const size_t> parameters) noexcept;
DWORD _GetSGRMouseModifierState(const gsl::span<const size_t> parameters) noexcept; DWORD _GetSGRMouseModifierState(const gsl::span<const size_t> parameters) noexcept;
bool _GenerateKeyFromChar(const wchar_t wch, short& vkey, DWORD& modifierState) noexcept; bool _GenerateKeyFromChar(const wchar_t wch, short& vkey, DWORD& modifierState) noexcept;
@ -188,13 +178,13 @@ namespace Microsoft::Console::VirtualTerminal
bool _IsModified(const size_t paramCount) noexcept; bool _IsModified(const size_t paramCount) noexcept;
DWORD _GetModifier(const size_t parameter) noexcept; DWORD _GetModifier(const size_t parameter) noexcept;
bool _UpdateSGRMouseButtonState(const wchar_t wch, bool _UpdateSGRMouseButtonState(const VTID id,
const gsl::span<const size_t> parameters, const gsl::span<const size_t> parameters,
DWORD& buttonState, DWORD& buttonState,
DWORD& eventFlags) noexcept; DWORD& eventFlags) noexcept;
bool _GetGenericVkey(const gsl::span<const size_t> parameters, bool _GetGenericVkey(const gsl::span<const size_t> parameters,
short& vkey) const; short& vkey) const;
bool _GetCursorKeysVkey(const wchar_t wch, short& vkey) const; bool _GetCursorKeysVkey(const VTID id, short& vkey) const;
bool _GetSs3KeysVkey(const wchar_t wch, short& vkey) const; bool _GetSs3KeysVkey(const wchar_t wch, short& vkey) const;
bool _WriteSingleKey(const short vkey, const DWORD modifierState); bool _WriteSingleKey(const short vkey, const DWORD modifierState);

File diff suppressed because it is too large Load diff

View file

@ -33,16 +33,11 @@ namespace Microsoft::Console::VirtualTerminal
bool ActionPassThroughString(const std::wstring_view string) override; bool ActionPassThroughString(const std::wstring_view string) override;
bool ActionEscDispatch(const wchar_t wch, bool ActionEscDispatch(const VTID id) override;
const gsl::span<const wchar_t> intermediates) override;
bool ActionVt52EscDispatch(const wchar_t wch, bool ActionVt52EscDispatch(const VTID id, const gsl::span<const size_t> parameters) override;
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) override;
bool ActionCsiDispatch(const wchar_t wch, bool ActionCsiDispatch(const VTID id, const gsl::span<const size_t> parameters) override;
const gsl::span<const wchar_t> intermediates,
const gsl::span<const size_t> parameters) override;
bool ActionClear() noexcept override; bool ActionClear() noexcept override;
@ -73,94 +68,89 @@ namespace Microsoft::Console::VirtualTerminal
wchar_t _lastPrintedChar; wchar_t _lastPrintedChar;
std::vector<DispatchTypes::GraphicsOptions> _graphicsOptions; std::vector<DispatchTypes::GraphicsOptions> _graphicsOptions;
bool _IntermediateScsDispatch(const wchar_t wch, enum EscActionCodes : uint64_t
const gsl::span<const wchar_t> intermediates);
bool _IntermediateQuestionMarkDispatch(const wchar_t wchAction,
const gsl::span<const size_t> parameters);
bool _IntermediateGreaterThanOrEqualDispatch(const wchar_t wch,
const wchar_t intermediate,
const gsl::span<const size_t> parameters);
bool _IntermediateExclamationDispatch(const wchar_t wch);
bool _IntermediateSpaceDispatch(const wchar_t wchAction,
const gsl::span<const size_t> parameters);
enum VTActionCodes : wchar_t
{ {
CUU_CursorUp = L'A', DECSC_CursorSave = VTID("7"),
CUD_CursorDown = L'B', DECRC_CursorRestore = VTID("8"),
CUF_CursorForward = L'C', DECKPAM_KeypadApplicationMode = VTID("="),
CUB_CursorBackward = L'D', DECKPNM_KeypadNumericMode = VTID(">"),
CNL_CursorNextLine = L'E', IND_Index = VTID("D"),
CPL_CursorPrevLine = L'F', NEL_NextLine = VTID("E"),
CHA_CursorHorizontalAbsolute = L'G', HTS_HorizontalTabSet = VTID("H"),
CUP_CursorPosition = L'H', RI_ReverseLineFeed = VTID("M"),
ED_EraseDisplay = L'J', SS2_SingleShift = VTID("N"),
EL_EraseLine = L'K', SS3_SingleShift = VTID("O"),
SU_ScrollUp = L'S', ST_StringTerminator = VTID("\\"),
SD_ScrollDown = L'T', RIS_ResetToInitialState = VTID("c"),
ICH_InsertCharacter = L'@', LS2_LockingShift = VTID("n"),
DCH_DeleteCharacter = L'P', LS3_LockingShift = VTID("o"),
SGR_SetGraphicsRendition = L'm', LS1R_LockingShift = VTID("~"),
DECSC_CursorSave = L'7', LS2R_LockingShift = VTID("}"),
DECRC_CursorRestore = L'8', LS3R_LockingShift = VTID("|"),
DECSET_PrivateModeSet = L'h', DECALN_ScreenAlignmentPattern = VTID("#8")
DECRST_PrivateModeReset = L'l',
ANSISYSSC_CursorSave = L's', // NOTE: Overlaps with DECLRMM/DECSLRM. Fix when/if implemented.
ANSISYSRC_CursorRestore = L'u', // NOTE: Overlaps with DECSMBV. Fix when/if implemented.
DECKPAM_KeypadApplicationMode = L'=',
DECKPNM_KeypadNumericMode = L'>',
DSR_DeviceStatusReport = L'n',
DA_DeviceAttributes = L'c',
DECSCPP_SetColumnsPerPage = L'|',
IL_InsertLine = L'L',
DL_DeleteLine = L'M', // Yes, this is the same as RI, however, RI is not preceded by a CSI, and DL is.
HPA_HorizontalPositionAbsolute = L'`',
VPA_VerticalLinePositionAbsolute = L'd',
HPR_HorizontalPositionRelative = L'a',
VPR_VerticalPositionRelative = L'e',
DECSTBM_SetScrollingRegion = L'r',
NEL_NextLine = L'E', // Not a CSI, so doesn't overlap with CNL
IND_Index = L'D', // Not a CSI, so doesn't overlap with CUB
RI_ReverseLineFeed = L'M',
HTS_HorizontalTabSet = L'H', // Not a CSI, so doesn't overlap with CUP
CHT_CursorForwardTab = L'I',
CBT_CursorBackTab = L'Z',
TBC_TabClear = L'g',
ECH_EraseCharacters = L'X',
HVP_HorizontalVerticalPosition = L'f',
DECSTR_SoftReset = L'p',
RIS_ResetToInitialState = L'c', // DA is prefaced by CSI, RIS by ESC
// 'q' is overloaded - no postfix is DECLL, ' ' postfix is DECSCUSR, and '"' is DECSCA
DECSCUSR_SetCursorStyle = L'q', // I believe we'll only ever implement DECSCUSR
DTTERM_WindowManipulation = L't',
REP_RepeatCharacter = L'b',
SS2_SingleShift = L'N',
SS3_SingleShift = L'O',
LS2_LockingShift = L'n',
LS3_LockingShift = L'o',
LS1R_LockingShift = L'~',
LS2R_LockingShift = L'}',
LS3R_LockingShift = L'|',
DECALN_ScreenAlignmentPattern = L'8'
}; };
enum Vt52ActionCodes : wchar_t enum CsiActionCodes : uint64_t
{ {
CursorUp = L'A', ICH_InsertCharacter = VTID("@"),
CursorDown = L'B', CUU_CursorUp = VTID("A"),
CursorRight = L'C', CUD_CursorDown = VTID("B"),
CursorLeft = L'D', CUF_CursorForward = VTID("C"),
EnterGraphicsMode = L'F', CUB_CursorBackward = VTID("D"),
ExitGraphicsMode = L'G', CNL_CursorNextLine = VTID("E"),
CursorToHome = L'H', CPL_CursorPrevLine = VTID("F"),
ReverseLineFeed = L'I', CHA_CursorHorizontalAbsolute = VTID("G"),
EraseToEndOfScreen = L'J', CUP_CursorPosition = VTID("H"),
EraseToEndOfLine = L'K', CHT_CursorForwardTab = VTID("I"),
DirectCursorAddress = L'Y', ED_EraseDisplay = VTID("J"),
Identify = L'Z', EL_EraseLine = VTID("K"),
EnterAlternateKeypadMode = L'=', IL_InsertLine = VTID("L"),
ExitAlternateKeypadMode = L'>', DL_DeleteLine = VTID("M"),
ExitVt52Mode = L'<' DCH_DeleteCharacter = VTID("P"),
SU_ScrollUp = VTID("S"),
SD_ScrollDown = VTID("T"),
ECH_EraseCharacters = VTID("X"),
CBT_CursorBackTab = VTID("Z"),
HPA_HorizontalPositionAbsolute = VTID("`"),
HPR_HorizontalPositionRelative = VTID("a"),
REP_RepeatCharacter = VTID("b"),
DA_DeviceAttributes = VTID("c"),
DA2_SecondaryDeviceAttributes = VTID(">c"),
DA3_TertiaryDeviceAttributes = VTID("=c"),
VPA_VerticalLinePositionAbsolute = VTID("d"),
VPR_VerticalPositionRelative = VTID("e"),
HVP_HorizontalVerticalPosition = VTID("f"),
TBC_TabClear = VTID("g"),
DECSET_PrivateModeSet = VTID("?h"),
DECRST_PrivateModeReset = VTID("?l"),
SGR_SetGraphicsRendition = VTID("m"),
DSR_DeviceStatusReport = VTID("n"),
DECSTBM_SetScrollingRegion = VTID("r"),
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"),
DECSCUSR_SetCursorStyle = VTID(" q"),
DECSTR_SoftReset = VTID("!p"),
DECSCPP_SetColumnsPerPage = VTID("$|")
};
enum Vt52ActionCodes : uint64_t
{
CursorUp = VTID("A"),
CursorDown = VTID("B"),
CursorRight = VTID("C"),
CursorLeft = VTID("D"),
EnterGraphicsMode = VTID("F"),
ExitGraphicsMode = VTID("G"),
CursorToHome = VTID("H"),
ReverseLineFeed = VTID("I"),
EraseToEndOfScreen = VTID("J"),
EraseToEndOfLine = VTID("K"),
DirectCursorAddress = VTID("Y"),
Identify = VTID("Z"),
EnterAlternateKeypadMode = VTID("="),
ExitAlternateKeypadMode = VTID(">"),
ExitVt52Mode = VTID("<")
}; };
enum OscActionCodes : unsigned int enum OscActionCodes : unsigned int

View file

@ -15,7 +15,6 @@ StateMachine::StateMachine(std::unique_ptr<IStateMachineEngine> engine) :
_state(VTStates::Ground), _state(VTStates::Ground),
_trace(Microsoft::Console::VirtualTerminal::ParserTracing()), _trace(Microsoft::Console::VirtualTerminal::ParserTracing()),
_isInAnsiMode(true), _isInAnsiMode(true),
_intermediates{},
_parameters{}, _parameters{},
_oscString{}, _oscString{},
_cachedSequence{ std::nullopt }, _cachedSequence{ std::nullopt },
@ -422,7 +421,7 @@ void StateMachine::_ActionEscDispatch(const wchar_t wch)
{ {
_trace.TraceOnAction(L"EscDispatch"); _trace.TraceOnAction(L"EscDispatch");
const bool success = _engine->ActionEscDispatch(wch, { _intermediates.data(), _intermediates.size() }); const bool success = _engine->ActionEscDispatch(_identifier.Finalize(wch));
// Trace the result. // Trace the result.
_trace.DispatchSequenceTrace(success); _trace.DispatchSequenceTrace(success);
@ -445,8 +444,7 @@ void StateMachine::_ActionVt52EscDispatch(const wchar_t wch)
{ {
_trace.TraceOnAction(L"Vt52EscDispatch"); _trace.TraceOnAction(L"Vt52EscDispatch");
const bool success = _engine->ActionVt52EscDispatch(wch, const bool success = _engine->ActionVt52EscDispatch(_identifier.Finalize(wch),
{ _intermediates.data(), _intermediates.size() },
{ _parameters.data(), _parameters.size() }); { _parameters.data(), _parameters.size() });
// Trace the result. // Trace the result.
@ -470,8 +468,7 @@ void StateMachine::_ActionCsiDispatch(const wchar_t wch)
{ {
_trace.TraceOnAction(L"CsiDispatch"); _trace.TraceOnAction(L"CsiDispatch");
const bool success = _engine->ActionCsiDispatch(wch, const bool success = _engine->ActionCsiDispatch(_identifier.Finalize(wch),
{ _intermediates.data(), _intermediates.size() },
{ _parameters.data(), _parameters.size() }); { _parameters.data(), _parameters.size() });
// Trace the result. // Trace the result.
@ -490,12 +487,12 @@ void StateMachine::_ActionCsiDispatch(const wchar_t wch)
// - wch - Character to dispatch. // - wch - Character to dispatch.
// Return Value: // Return Value:
// - <none> // - <none>
void StateMachine::_ActionCollect(const wchar_t wch) void StateMachine::_ActionCollect(const wchar_t wch) noexcept
{ {
_trace.TraceOnAction(L"Collect"); _trace.TraceOnAction(L"Collect");
// store collect data // store collect data
_intermediates.push_back(wch); _identifier.AddIntermediate(wch);
} }
// Routine Description: // Routine Description:
@ -541,7 +538,7 @@ void StateMachine::_ActionClear()
_trace.TraceOnAction(L"Clear"); _trace.TraceOnAction(L"Clear");
// clear all internal stored state. // clear all internal stored state.
_intermediates.clear(); _identifier.Clear();
_parameters.clear(); _parameters.clear();

View file

@ -55,7 +55,7 @@ namespace Microsoft::Console::VirtualTerminal
void _ActionPrint(const wchar_t wch); void _ActionPrint(const wchar_t wch);
void _ActionEscDispatch(const wchar_t wch); void _ActionEscDispatch(const wchar_t wch);
void _ActionVt52EscDispatch(const wchar_t wch); void _ActionVt52EscDispatch(const wchar_t wch);
void _ActionCollect(const wchar_t wch); void _ActionCollect(const wchar_t wch) noexcept;
void _ActionParam(const wchar_t wch); void _ActionParam(const wchar_t wch);
void _ActionCsiDispatch(const wchar_t wch); void _ActionCsiDispatch(const wchar_t wch);
void _ActionOscParam(const wchar_t wch) noexcept; void _ActionOscParam(const wchar_t wch) noexcept;
@ -142,7 +142,7 @@ namespace Microsoft::Console::VirtualTerminal
std::wstring_view _run; std::wstring_view _run;
std::vector<wchar_t> _intermediates; VTIDBuilder _identifier;
std::vector<size_t> _parameters; std::vector<size_t> _parameters;
std::wstring _oscString; std::wstring _oscString;

View file

@ -222,7 +222,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest
std::wstring GenerateSgrMouseSequence(const CsiMouseButtonCodes button, std::wstring GenerateSgrMouseSequence(const CsiMouseButtonCodes button,
const unsigned short modifiers, const unsigned short modifiers,
const COORD position, const COORD position,
const CsiActionCodes direction); const VTID direction);
// SGR_PARAMS serves as test input // SGR_PARAMS serves as test input
// - the state of the buttons (constructed via InputStateMachineEngine::CsiActionMouseCodes) // - the state of the buttons (constructed via InputStateMachineEngine::CsiActionMouseCodes)
@ -1089,7 +1089,7 @@ void InputEngineTest::AltBackspaceEnterTest()
std::wstring InputEngineTest::GenerateSgrMouseSequence(const CsiMouseButtonCodes button, std::wstring InputEngineTest::GenerateSgrMouseSequence(const CsiMouseButtonCodes button,
const unsigned short modifiers, const unsigned short modifiers,
const COORD position, const COORD position,
const CsiActionCodes direction) const VTID direction)
{ {
// we first need to convert "button" and "modifiers" into an 8 bit sequence // we first need to convert "button" and "modifiers" into an 8 bit sequence
unsigned int actionCode = 0; unsigned int actionCode = 0;
@ -1102,7 +1102,11 @@ std::wstring InputEngineTest::GenerateSgrMouseSequence(const CsiMouseButtonCodes
// modifiers represents the middle 4 bits // modifiers represents the middle 4 bits
actionCode |= modifiers; actionCode |= modifiers;
return wil::str_printf_failfast<std::wstring>(L"\x1b[<%d;%d;%d%c", static_cast<int>(actionCode), position.X, position.Y, direction); // mouse sequence identifiers consist of a private parameter prefix and a final character
const wchar_t prefixChar = direction[0];
const wchar_t finalChar = direction[1];
return wil::str_printf_failfast<std::wstring>(L"\x1b[%c%d;%d;%d%c", prefixChar, static_cast<int>(actionCode), position.X, position.Y, finalChar);
} }
void InputEngineTest::VerifySGRMouseData(const std::vector<std::tuple<SGR_PARAMS, MOUSE_EVENT_PARAMS>> testData) void InputEngineTest::VerifySGRMouseData(const std::vector<std::tuple<SGR_PARAMS, MOUSE_EVENT_PARAMS>> testData)

View file

@ -50,12 +50,9 @@ public:
return true; return true;
}; };
bool ActionEscDispatch(const wchar_t /* wch */, bool ActionEscDispatch(const VTID /* id */) override { return true; };
const gsl::span<const wchar_t> /* intermediates */) override { return true; };
bool ActionVt52EscDispatch(const wchar_t /*wch*/, bool ActionVt52EscDispatch(const VTID /*id*/, const gsl::span<const size_t> /*parameters*/) override { return true; };
const gsl::span<const wchar_t> /*intermediates*/,
const gsl::span<const size_t> /*parameters*/) override { return true; };
bool ActionClear() override { return true; }; bool ActionClear() override { return true; };
@ -82,9 +79,7 @@ public:
bool DispatchIntermediatesFromEscape() const override { return false; }; bool DispatchIntermediatesFromEscape() const override { return false; };
// ActionCsiDispatch is the only method that's actually implemented. // ActionCsiDispatch is the only method that's actually implemented.
bool ActionCsiDispatch(const wchar_t /*wch*/, bool ActionCsiDispatch(const VTID /*id*/, const gsl::span<const size_t> parameters) override
const gsl::span<const wchar_t> /*intermediates*/,
const gsl::span<const size_t> parameters) override
{ {
// If flush to terminal is registered for a test, then use it. // If flush to terminal is registered for a test, then use it.
if (pfnFlushToTerminal) if (pfnFlushToTerminal)