Preprocess and convert C1 controls to their 7 bit equivalent (#7340)
C1 control characters are now first converted to their 7 bit equivalent. This allows us to unify the logic of C1 and C0 escape handling. This also adds support for SOS/PM/APC string. * Unify the logic for C1 and C0 escape handling by converting C1 to C0 beforehand. This adds support for various C1 characters, including IND(8/4), NEL(8/5), HTS(8/8), RI(8/13), SS2(8/14), SS3(8/15), OSC(9/13), etc. * Add support for SOS/PM/APC escape sequences. Fixes #7032 * Use "Variable Length String" logic to unify the string termination handling of OSC, DCS and SOS/PM/APC. This fixes an issue where OSC action is successfully dispatched even when terminated with non-ST character. Introduced by #6328, the DCS PassThrough is spared from this issue. This PR puts them together and add test cases for them. References: https://vt100.net/docs/vt510-rm/chapter4.html https://vt100.net/emu/dec_ansi_parser Closes #7032 Closes #7317
This commit is contained in:
parent
863e3e5c22
commit
f91b53d5fd
|
@ -20937,6 +20937,8 @@ apay
|
||||||
Apayao
|
Apayao
|
||||||
APB
|
APB
|
||||||
APC
|
APC
|
||||||
|
Apc
|
||||||
|
apc
|
||||||
APDA
|
APDA
|
||||||
APDU
|
APDU
|
||||||
APE
|
APE
|
||||||
|
@ -383889,6 +383891,7 @@ sorus
|
||||||
sorva
|
sorva
|
||||||
sory
|
sory
|
||||||
SOS
|
SOS
|
||||||
|
Sos
|
||||||
sos
|
sos
|
||||||
Sosanna
|
Sosanna
|
||||||
so-seeming
|
so-seeming
|
||||||
|
|
|
@ -68,14 +68,15 @@ static constexpr bool _isC0Code(const wchar_t wch) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character is a C1 CSI (Control Sequence Introducer)
|
// - Determines if a character is a C1 control characters.
|
||||||
// This is a single-character way to start a control sequence, as opposed to "ESC[".
|
// This is a single-character way to start a control sequence, as opposed to using ESC
|
||||||
|
// and their 7-bit equivalent.
|
||||||
//
|
//
|
||||||
// Not all single-byte codepages support C1 control codes--in some, the range that would
|
// Not all single-byte codepages support C1 control codes--in some, the range that would
|
||||||
// be used for C1 codes are instead used for additional graphic characters.
|
// be used for C1 codes are instead used for additional graphic characters.
|
||||||
//
|
//
|
||||||
// However, we do not need to worry about confusion whether a single byte \x9b in a
|
// However, we do not need to worry about confusion whether a single byte, for example,
|
||||||
// single-byte stream represents a C1 CSI or some other glyph, because by the time we
|
// \x9b in a single-byte stream represents a C1 CSI or some other glyph, because by the time we
|
||||||
// get here, everything is Unicode. Knowing whether a single-byte \x9b represents a
|
// get here, everything is Unicode. Knowing whether a single-byte \x9b represents a
|
||||||
// single-character C1 CSI or some other glyph is handled by MultiByteToWideChar before
|
// single-character C1 CSI or some other glyph is handled by MultiByteToWideChar before
|
||||||
// we get here (if the stream was not already UTF-16). For instance, in CP_ACP, if a
|
// we get here (if the stream was not already UTF-16). For instance, in CP_ACP, if a
|
||||||
|
@ -86,24 +87,21 @@ static constexpr bool _isC0Code(const wchar_t wch) noexcept
|
||||||
// - wch - Character to check.
|
// - wch - Character to check.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - True if it is. False if it isn't.
|
// - True if it is. False if it isn't.
|
||||||
static constexpr bool _isC1Csi(const wchar_t wch) noexcept
|
static constexpr bool _isC1ControlCharacter(const wchar_t wch) noexcept
|
||||||
{
|
{
|
||||||
return wch == L'\x9b';
|
return (wch >= L'\x80' && wch <= L'\x9F');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character is a C1 DCS (Device Control Strings)
|
// - Convert a C1 control characters to their 7-bit equivalent.
|
||||||
// This is a single-character way to start a control sequence, as opposed to "ESC P".
|
|
||||||
//
|
|
||||||
// See the comment above _isC1Csi for more information on how this is impacted by codepages.
|
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character to check.
|
// - wch - Character to convert.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - True if it is. False if it isn't.
|
// - The 7-bit equivalent of the 8-bit control characters.
|
||||||
static constexpr bool _isC1Dcs(const wchar_t wch) noexcept
|
static constexpr wchar_t _c1To7Bit(const wchar_t wch) noexcept
|
||||||
{
|
{
|
||||||
return wch == L'\x90';
|
return wch - L'\x40';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
|
@ -144,7 +142,7 @@ static constexpr bool _isEscape(const wchar_t wch) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character is a delimiter between two parameters in a "control sequence".
|
// - Determines if a character is a delimiter between two parameters in an escape sequence.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character to check.
|
// - wch - Character to check.
|
||||||
// Return Value:
|
// Return Value:
|
||||||
|
@ -213,17 +211,6 @@ static constexpr bool _isParameterInvalid(const wchar_t wch) noexcept
|
||||||
return _isCsiInvalid(wch) || _isCsiPrivateMarker(wch);
|
return _isCsiInvalid(wch) || _isCsiPrivateMarker(wch);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Determines if a character is a string terminator.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - Character to check.
|
|
||||||
// Return Value:
|
|
||||||
// - True if it is. False if it isn't.
|
|
||||||
static constexpr bool _isStringTerminator(const wchar_t wch) noexcept
|
|
||||||
{
|
|
||||||
return wch == L'\x9C';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character is a string terminator indicator.
|
// - Determines if a character is a string terminator indicator.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
@ -287,17 +274,6 @@ static constexpr bool _isOscDelimiter(const wchar_t wch) noexcept
|
||||||
return wch == L';'; // 0x3B
|
return wch == L';'; // 0x3B
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Determines if a character should be initiate the end of an OSC sequence.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - Character to check.
|
|
||||||
// Return Value:
|
|
||||||
// - True if it is. False if it isn't.
|
|
||||||
static constexpr bool _isOscTerminationInitiator(const wchar_t wch) noexcept
|
|
||||||
{
|
|
||||||
return wch == AsciiChars::ESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character should be ignored in a operating system control sequence
|
// - Determines if a character should be ignored in a operating system control sequence
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
@ -320,7 +296,7 @@ static constexpr bool _isOscInvalid(const wchar_t wch) noexcept
|
||||||
// - True if it is. False if it isn't.
|
// - True if it is. False if it isn't.
|
||||||
static constexpr bool _isOscTerminator(const wchar_t wch) noexcept
|
static constexpr bool _isOscTerminator(const wchar_t wch) noexcept
|
||||||
{
|
{
|
||||||
return wch == AsciiChars::BEL || _isStringTerminator(wch); // Bell character or C1 terminator
|
return wch == AsciiChars::BEL; // Bell character
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
|
@ -335,17 +311,6 @@ static constexpr bool _isDcsIndicator(const wchar_t wch) noexcept
|
||||||
return wch == L'P'; // 0x50
|
return wch == L'P'; // 0x50
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
|
||||||
// - Determines if a character should initiate the end of a DCS sequence.
|
|
||||||
// Arguments:
|
|
||||||
// - wch - Character to check.
|
|
||||||
// Return Value:
|
|
||||||
// - True if it is. False if it isn't.
|
|
||||||
static constexpr bool _isDcsTerminationInitiator(const wchar_t wch) noexcept
|
|
||||||
{
|
|
||||||
return wch == AsciiChars::ESC;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character is valid for a DCS pass through sequence.
|
// - Determines if a character is valid for a DCS pass through sequence.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
|
@ -358,6 +323,42 @@ static constexpr bool _isDcsPassThroughValid(const wchar_t wch) noexcept
|
||||||
return wch >= AsciiChars::SPC && wch < AsciiChars::DEL;
|
return wch >= AsciiChars::SPC && wch < AsciiChars::DEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Determines if a character is "start of string" beginning
|
||||||
|
// indicator.
|
||||||
|
// Arguments:
|
||||||
|
// - wch - Character to check.
|
||||||
|
// Return Value:
|
||||||
|
// - True if it is. False if it isn't.
|
||||||
|
static constexpr bool _isSosIndicator(const wchar_t wch) noexcept
|
||||||
|
{
|
||||||
|
return wch == L'X'; // 0x58
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Determines if a character is "private message" beginning
|
||||||
|
// indicator.
|
||||||
|
// Arguments:
|
||||||
|
// - wch - Character to check.
|
||||||
|
// Return Value:
|
||||||
|
// - True if it is. False if it isn't.
|
||||||
|
static constexpr bool _isPmIndicator(const wchar_t wch) noexcept
|
||||||
|
{
|
||||||
|
return wch == L'^'; // 0x5E
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Determines if a character is "application program command" beginning
|
||||||
|
// indicator.
|
||||||
|
// Arguments:
|
||||||
|
// - wch - Character to check.
|
||||||
|
// Return Value:
|
||||||
|
// - True if it is. False if it isn't.
|
||||||
|
static constexpr bool _isApcIndicator(const wchar_t wch) noexcept
|
||||||
|
{
|
||||||
|
return wch == L'_'; // 0x5F
|
||||||
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Determines if a character indicates an action that should be taken in the ground state -
|
// - Determines if a character indicates an action that should be taken in the ground state -
|
||||||
// These are C0 characters and the C1 [single-character] CSI.
|
// These are C0 characters and the C1 [single-character] CSI.
|
||||||
|
@ -367,7 +368,7 @@ static constexpr bool _isDcsPassThroughValid(const wchar_t wch) noexcept
|
||||||
// - True if it is. False if it isn't.
|
// - True if it is. False if it isn't.
|
||||||
static constexpr bool _isActionableFromGround(const wchar_t wch) noexcept
|
static constexpr bool _isActionableFromGround(const wchar_t wch) noexcept
|
||||||
{
|
{
|
||||||
return (wch <= AsciiChars::US) || _isC1Csi(wch) || _isC1Dcs(wch) || _isDelete(wch);
|
return (wch <= AsciiChars::US) || _isC1ControlCharacter(wch) || _isDelete(wch);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
@ -926,12 +927,42 @@ void StateMachine::_EnterDcsTermination() noexcept
|
||||||
_trace.TraceStateChange(L"DcsTermination");
|
_trace.TraceStateChange(L"DcsTermination");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Moves the state machine into the SosPmApcString state.
|
||||||
|
// This state is entered:
|
||||||
|
// 1. When the Sos character is seen after an Escape entry
|
||||||
|
// 2. When the Pm character is seen after an Escape entry
|
||||||
|
// 3. When the Apc character is seen after an Escape entry
|
||||||
|
// Arguments:
|
||||||
|
// - <none>
|
||||||
|
// Return Value:
|
||||||
|
// - <none>
|
||||||
|
void StateMachine::_EnterSosPmApcString() noexcept
|
||||||
|
{
|
||||||
|
_state = VTStates::SosPmApcString;
|
||||||
|
_trace.TraceStateChange(L"SosPmApcString");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Moves the state machine into the SosPmApcStringTermination state.
|
||||||
|
// This state is entered:
|
||||||
|
// 1. When an ESC is seen in a SOS/PM/APC string. This escape will be followed by a
|
||||||
|
// '\', as to encode a 0x9C as a 7-bit ASCII char stream.
|
||||||
|
// Arguments:
|
||||||
|
// - <none>
|
||||||
|
// Return Value:
|
||||||
|
// - <none>
|
||||||
|
void StateMachine::_EnterSosPmApcTermination() noexcept
|
||||||
|
{
|
||||||
|
_state = VTStates::SosPmApcTermination;
|
||||||
|
_trace.TraceStateChange(L"SosPmApcStringTermination");
|
||||||
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Processes a character event into an Action that occurs while in the Ground state.
|
// - Processes a character event into an Action that occurs while in the Ground state.
|
||||||
// Events in this state will:
|
// Events in this state will:
|
||||||
// 1. Execute C0 control characters
|
// 1. Execute C0 control characters
|
||||||
// 2. Handle a C1 Control Sequence Introducer
|
// 2. Print all other characters
|
||||||
// 3. Print all other characters
|
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character that triggered the event
|
// - wch - Character that triggered the event
|
||||||
// Return Value:
|
// Return Value:
|
||||||
|
@ -943,14 +974,6 @@ void StateMachine::_EventGround(const wchar_t wch)
|
||||||
{
|
{
|
||||||
_ActionExecute(wch);
|
_ActionExecute(wch);
|
||||||
}
|
}
|
||||||
else if (_isC1Csi(wch) && _isInAnsiMode)
|
|
||||||
{
|
|
||||||
_EnterCsiEntry();
|
|
||||||
}
|
|
||||||
else if (_isC1Dcs(wch) && _isInAnsiMode)
|
|
||||||
{
|
|
||||||
_EnterDcsEntry();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ActionPrint(wch);
|
_ActionPrint(wch);
|
||||||
|
@ -1019,6 +1042,10 @@ void StateMachine::_EventEscape(const wchar_t wch)
|
||||||
{
|
{
|
||||||
_EnterDcsEntry();
|
_EnterDcsEntry();
|
||||||
}
|
}
|
||||||
|
else if (_isSosIndicator(wch) || _isPmIndicator(wch) || _isApcIndicator(wch))
|
||||||
|
{
|
||||||
|
_EnterSosPmApcString();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ActionEscDispatch(wch);
|
_ActionEscDispatch(wch);
|
||||||
|
@ -1299,7 +1326,7 @@ void StateMachine::_EventOscString(const wchar_t wch)
|
||||||
_ActionOscDispatch(wch);
|
_ActionOscDispatch(wch);
|
||||||
_EnterGround();
|
_EnterGround();
|
||||||
}
|
}
|
||||||
else if (_isOscTerminationInitiator(wch))
|
else if (_isEscape(wch))
|
||||||
{
|
{
|
||||||
_EnterOscTermination();
|
_EnterOscTermination();
|
||||||
}
|
}
|
||||||
|
@ -1322,13 +1349,6 @@ void StateMachine::_EventOscString(const wchar_t wch)
|
||||||
// - wch - Character that triggered the event
|
// - wch - Character that triggered the event
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - <none>
|
// - <none>
|
||||||
void StateMachine::_EventOscTermination(const wchar_t wch)
|
|
||||||
{
|
|
||||||
_trace.TraceOnEvent(L"OscTermination");
|
|
||||||
|
|
||||||
_ActionOscDispatch(wch);
|
|
||||||
_EnterGround();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Processes a character event into an Action that occurs while in the Ss3Entry state.
|
// - Processes a character event into an Action that occurs while in the Ss3Entry state.
|
||||||
|
@ -1497,24 +1517,16 @@ void StateMachine::_EventDcsEntry(const wchar_t wch)
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Processes a character event into an Action that occurs while in the DcsIgnore state.
|
// - Processes a character event into an Action that occurs while in the DcsIgnore state.
|
||||||
// Events in this state will:
|
// In this state the entire DCS string is considered invalid and we will ignore everything.
|
||||||
// 1. Enter ground on a String terminator
|
// The termination state is handled outside when an ESC is seen.
|
||||||
// 2. Ignore everything else.
|
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character that triggered the event
|
// - wch - Character that triggered the event
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - <none>
|
// - <none>
|
||||||
void StateMachine::_EventDcsIgnore(const wchar_t wch) noexcept
|
void StateMachine::_EventDcsIgnore() noexcept
|
||||||
{
|
{
|
||||||
_trace.TraceOnEvent(L"DcsIgnore");
|
_trace.TraceOnEvent(L"DcsIgnore");
|
||||||
if (_isStringTerminator(wch))
|
_ActionIgnore();
|
||||||
{
|
|
||||||
_EnterGround();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_ActionIgnore();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
|
@ -1603,10 +1615,9 @@ void StateMachine::_EventDcsParam(const wchar_t wch)
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Processes a character event into an Action that occurs while in the DcsPassThrough state.
|
// - Processes a character event into an Action that occurs while in the DcsPassThrough state.
|
||||||
// Events in this state will:
|
// Events in this state will:
|
||||||
// 1. Enter ground on a String terminator
|
// 1. Pass through if character is valid.
|
||||||
// 2. Pass through if character is valid.
|
// 2. If we see a ESC, enter the DcsTermination state.
|
||||||
// 3. If we see a ESC, enter the DcsTermination state.
|
// 3. Ignore everything else.
|
||||||
// 4. Ignore everything else.
|
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character that triggered the event
|
// - wch - Character that triggered the event
|
||||||
// Return Value:
|
// Return Value:
|
||||||
|
@ -1614,16 +1625,11 @@ void StateMachine::_EventDcsParam(const wchar_t wch)
|
||||||
void StateMachine::_EventDcsPassThrough(const wchar_t wch)
|
void StateMachine::_EventDcsPassThrough(const wchar_t wch)
|
||||||
{
|
{
|
||||||
_trace.TraceOnEvent(L"DcsPassThrough");
|
_trace.TraceOnEvent(L"DcsPassThrough");
|
||||||
if (_isStringTerminator(wch))
|
|
||||||
{
|
|
||||||
// TODO:GH#7316: The Dcs sequence has successfully terminated. This is where we'd be dispatching the DCS command.
|
|
||||||
_EnterGround();
|
|
||||||
}
|
|
||||||
if (_isC0Code(wch) || _isDcsPassThroughValid(wch))
|
if (_isC0Code(wch) || _isDcsPassThroughValid(wch))
|
||||||
{
|
{
|
||||||
_ActionDcsPassThrough(wch);
|
_ActionDcsPassThrough(wch);
|
||||||
}
|
}
|
||||||
else if (_isDcsTerminationInitiator(wch))
|
else if (_isEscape(wch))
|
||||||
{
|
{
|
||||||
_EnterDcsTermination();
|
_EnterDcsTermination();
|
||||||
}
|
}
|
||||||
|
@ -1634,21 +1640,52 @@ void StateMachine::_EventDcsPassThrough(const wchar_t wch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routine Description:
|
// Routine Description:
|
||||||
// - Handle the two-character termination of a DCS sequence.
|
// - Handle SOS/PM/APC string.
|
||||||
// Events in this state will:
|
// Events in this state will:
|
||||||
// 1. Enter ground on a string terminator
|
// 1. If we see a ESC, enter the SosPmApcTermination state.
|
||||||
// 2. Pass on everything else as the start of a regular escape sequence
|
// 2. Ignore everything else.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// - wch - Character that triggered the event
|
// - wch - Character that triggered the event
|
||||||
// Return Value:
|
// Return Value:
|
||||||
// - <none>
|
// - <none>
|
||||||
void StateMachine::_EventDcsTermination(const wchar_t wch)
|
void StateMachine::_EventSosPmApcString(const wchar_t wch) noexcept
|
||||||
{
|
{
|
||||||
_trace.TraceOnEvent(L"DcsTermination");
|
_trace.TraceOnEvent(L"SosPmApcString");
|
||||||
|
if (_isEscape(wch))
|
||||||
|
{
|
||||||
|
_EnterSosPmApcTermination();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_ActionIgnore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Handle "Variable Length String" termination.
|
||||||
|
// Events in this state will:
|
||||||
|
// 1. Trigger the corresponding action and enter ground if we see a string terminator,
|
||||||
|
// 2. Otherwise treat this as a normal escape character event.
|
||||||
|
// Arguments:
|
||||||
|
// - wch - Character that triggered the event
|
||||||
|
// Return Value:
|
||||||
|
// - <none>
|
||||||
|
void StateMachine::_EventVariableLengthStringTermination(const wchar_t wch)
|
||||||
|
{
|
||||||
if (_isStringTerminatorIndicator(wch))
|
if (_isStringTerminatorIndicator(wch))
|
||||||
{
|
{
|
||||||
// TODO: The Dcs sequence has successfully terminated. This is where we'd be dispatching the DCS command.
|
if (_state == VTStates::OscTermination)
|
||||||
|
{
|
||||||
|
_ActionOscDispatch(wch);
|
||||||
|
}
|
||||||
|
else if (_state == VTStates::DcsTermination)
|
||||||
|
{
|
||||||
|
// TODO:GH#7316: The Dcs sequence has successfully terminated. This is where we'd be dispatching the DCS command.
|
||||||
|
}
|
||||||
|
else if (_state == VTStates::SosPmApcTermination)
|
||||||
|
{
|
||||||
|
// We don't support any SOS/PM/APC control string yet.
|
||||||
|
}
|
||||||
_EnterGround();
|
_EnterGround();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1680,12 +1717,40 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
||||||
_ActionExecute(wch);
|
_ActionExecute(wch);
|
||||||
_EnterGround();
|
_EnterGround();
|
||||||
}
|
}
|
||||||
else if (_isEscape(wch) && _state != VTStates::OscString && _state != VTStates::DcsPassThrough)
|
// Preprocess C1 control characters and treat them as ESC + their 7-bit equivalent.
|
||||||
|
else if (_isC1ControlCharacter(wch))
|
||||||
|
{
|
||||||
|
// When we are in "Variable Length String" state, a C1 control character
|
||||||
|
// should effectively acts as an ESC and move us into the corresponding
|
||||||
|
// termination state.
|
||||||
|
if (_IsVariableLengthStringState())
|
||||||
|
{
|
||||||
|
if (_state == VTStates::OscString)
|
||||||
|
{
|
||||||
|
_EnterOscTermination();
|
||||||
|
}
|
||||||
|
else if (_state == VTStates::DcsPassThrough)
|
||||||
|
{
|
||||||
|
_EnterDcsTermination();
|
||||||
|
}
|
||||||
|
else if (_state == VTStates::SosPmApcString)
|
||||||
|
{
|
||||||
|
_EnterSosPmApcTermination();
|
||||||
|
}
|
||||||
|
|
||||||
|
_EventVariableLengthStringTermination(_c1To7Bit(wch));
|
||||||
|
}
|
||||||
|
// Enter Escape state and pass the converted 7-bit character.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_EnterEscape();
|
||||||
|
_EventEscape(_c1To7Bit(wch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't go to escape from the "Variable Length String" state - ESC (and C1 String Terminator)
|
||||||
|
// can be used to terminate variable length control string.
|
||||||
|
else if (_isEscape(wch) && !_IsVariableLengthStringState())
|
||||||
{
|
{
|
||||||
// Don't go to escape from the OSC string state - ESC can be used to
|
|
||||||
// terminate OSC strings.
|
|
||||||
//
|
|
||||||
// Same for DCS pass through state.
|
|
||||||
_EnterEscape();
|
_EnterEscape();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1712,7 +1777,7 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
||||||
case VTStates::OscString:
|
case VTStates::OscString:
|
||||||
return _EventOscString(wch);
|
return _EventOscString(wch);
|
||||||
case VTStates::OscTermination:
|
case VTStates::OscTermination:
|
||||||
return _EventOscTermination(wch);
|
return _EventVariableLengthStringTermination(wch);
|
||||||
case VTStates::Ss3Entry:
|
case VTStates::Ss3Entry:
|
||||||
return _EventSs3Entry(wch);
|
return _EventSs3Entry(wch);
|
||||||
case VTStates::Ss3Param:
|
case VTStates::Ss3Param:
|
||||||
|
@ -1722,7 +1787,7 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
||||||
case VTStates::DcsEntry:
|
case VTStates::DcsEntry:
|
||||||
return _EventDcsEntry(wch);
|
return _EventDcsEntry(wch);
|
||||||
case VTStates::DcsIgnore:
|
case VTStates::DcsIgnore:
|
||||||
return _EventDcsIgnore(wch);
|
return _EventDcsIgnore();
|
||||||
case VTStates::DcsIntermediate:
|
case VTStates::DcsIntermediate:
|
||||||
return _EventDcsIntermediate(wch);
|
return _EventDcsIntermediate(wch);
|
||||||
case VTStates::DcsParam:
|
case VTStates::DcsParam:
|
||||||
|
@ -1730,7 +1795,11 @@ void StateMachine::ProcessCharacter(const wchar_t wch)
|
||||||
case VTStates::DcsPassThrough:
|
case VTStates::DcsPassThrough:
|
||||||
return _EventDcsPassThrough(wch);
|
return _EventDcsPassThrough(wch);
|
||||||
case VTStates::DcsTermination:
|
case VTStates::DcsTermination:
|
||||||
return _EventDcsTermination(wch);
|
return _EventVariableLengthStringTermination(wch);
|
||||||
|
case VTStates::SosPmApcString:
|
||||||
|
return _EventSosPmApcString(wch);
|
||||||
|
case VTStates::SosPmApcTermination:
|
||||||
|
return _EventVariableLengthStringTermination(wch);
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1952,3 +2021,16 @@ void StateMachine::_AccumulateTo(const wchar_t wch, size_t& value) noexcept
|
||||||
value = MAX_PARAMETER_VALUE;
|
value = MAX_PARAMETER_VALUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Routine Description:
|
||||||
|
// - Determines if the engine is in "Variable Length String" state, which is a combination
|
||||||
|
// of all states that are expecting a string that has a undetermined length.
|
||||||
|
//
|
||||||
|
// Arguments:
|
||||||
|
// - <none>
|
||||||
|
// Return Value:
|
||||||
|
// - True if it is. False if it isn't.
|
||||||
|
const bool StateMachine::_IsVariableLengthStringState() const noexcept
|
||||||
|
{
|
||||||
|
return _state == VTStates::OscString || _state == VTStates::DcsPassThrough || _state == VTStates::SosPmApcString;
|
||||||
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||||
void _EnterDcsIntermediate() noexcept;
|
void _EnterDcsIntermediate() noexcept;
|
||||||
void _EnterDcsPassThrough() noexcept;
|
void _EnterDcsPassThrough() noexcept;
|
||||||
void _EnterDcsTermination() noexcept;
|
void _EnterDcsTermination() noexcept;
|
||||||
|
void _EnterSosPmApcString() noexcept;
|
||||||
|
void _EnterSosPmApcTermination() noexcept;
|
||||||
|
|
||||||
void _EventGround(const wchar_t wch);
|
void _EventGround(const wchar_t wch);
|
||||||
void _EventEscape(const wchar_t wch);
|
void _EventEscape(const wchar_t wch);
|
||||||
|
@ -96,18 +98,19 @@ namespace Microsoft::Console::VirtualTerminal
|
||||||
void _EventCsiParam(const wchar_t wch);
|
void _EventCsiParam(const wchar_t wch);
|
||||||
void _EventOscParam(const wchar_t wch) noexcept;
|
void _EventOscParam(const wchar_t wch) noexcept;
|
||||||
void _EventOscString(const wchar_t wch);
|
void _EventOscString(const wchar_t wch);
|
||||||
void _EventOscTermination(const wchar_t wch);
|
|
||||||
void _EventSs3Entry(const wchar_t wch);
|
void _EventSs3Entry(const wchar_t wch);
|
||||||
void _EventSs3Param(const wchar_t wch);
|
void _EventSs3Param(const wchar_t wch);
|
||||||
void _EventVt52Param(const wchar_t wch);
|
void _EventVt52Param(const wchar_t wch);
|
||||||
void _EventDcsEntry(const wchar_t wch);
|
void _EventDcsEntry(const wchar_t wch);
|
||||||
void _EventDcsIgnore(const wchar_t wch) noexcept;
|
void _EventDcsIgnore() noexcept;
|
||||||
void _EventDcsIntermediate(const wchar_t wch);
|
void _EventDcsIntermediate(const wchar_t wch);
|
||||||
void _EventDcsParam(const wchar_t wch);
|
void _EventDcsParam(const wchar_t wch);
|
||||||
void _EventDcsPassThrough(const wchar_t wch);
|
void _EventDcsPassThrough(const wchar_t wch);
|
||||||
void _EventDcsTermination(const wchar_t wch);
|
void _EventSosPmApcString(const wchar_t wch) noexcept;
|
||||||
|
void _EventVariableLengthStringTermination(const wchar_t wch);
|
||||||
|
|
||||||
void _AccumulateTo(const wchar_t wch, size_t& value) noexcept;
|
void _AccumulateTo(const wchar_t wch, size_t& value) noexcept;
|
||||||
|
const bool _IsVariableLengthStringState() const noexcept;
|
||||||
|
|
||||||
enum class VTStates
|
enum class VTStates
|
||||||
{
|
{
|
||||||
|
@ -129,7 +132,9 @@ namespace Microsoft::Console::VirtualTerminal
|
||||||
DcsIntermediate,
|
DcsIntermediate,
|
||||||
DcsParam,
|
DcsParam,
|
||||||
DcsPassThrough,
|
DcsPassThrough,
|
||||||
DcsTermination
|
DcsTermination,
|
||||||
|
SosPmApcString,
|
||||||
|
SosPmApcTermination
|
||||||
};
|
};
|
||||||
|
|
||||||
Microsoft::Console::VirtualTerminal::ParserTracing _trace;
|
Microsoft::Console::VirtualTerminal::ParserTracing _trace;
|
||||||
|
|
|
@ -56,7 +56,7 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
TEST_METHOD(TestEscapePath)
|
TEST_METHOD(TestEscapePath)
|
||||||
{
|
{
|
||||||
BEGIN_TEST_METHOD_PROPERTIES()
|
BEGIN_TEST_METHOD_PROPERTIES()
|
||||||
TEST_METHOD_PROPERTY(L"Data:uiTest", L"{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17}") // one value for each type of state test below.
|
TEST_METHOD_PROPERTY(L"Data:uiTest", L"{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}") // one value for each type of state test below.
|
||||||
END_TEST_METHOD_PROPERTIES()
|
END_TEST_METHOD_PROPERTIES()
|
||||||
|
|
||||||
size_t uiTest;
|
size_t uiTest;
|
||||||
|
@ -66,7 +66,7 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
StateMachine mach(std::move(engine));
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
// The OscString state shouldn't escape out after an ESC.
|
// The OscString state shouldn't escape out after an ESC.
|
||||||
// Same for DcsPassThrough state.
|
// Same for DcsPassThrough and SosPmApcString state.
|
||||||
bool shouldEscapeOut = true;
|
bool shouldEscapeOut = true;
|
||||||
|
|
||||||
switch (uiTest)
|
switch (uiTest)
|
||||||
|
@ -181,6 +181,19 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
mach._state = StateMachine::VTStates::DcsTermination;
|
mach._state = StateMachine::VTStates::DcsTermination;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 18:
|
||||||
|
{
|
||||||
|
Log::Comment(L"Escape from SosPmApcString");
|
||||||
|
shouldEscapeOut = false;
|
||||||
|
mach._state = StateMachine::VTStates::SosPmApcString;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 19:
|
||||||
|
{
|
||||||
|
Log::Comment(L"Escape from SosPmApcTermination");
|
||||||
|
mach._state = StateMachine::VTStates::SosPmApcTermination;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mach.ProcessCharacter(AsciiChars::ESC);
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
@ -405,6 +418,19 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestC1Osc)
|
||||||
|
{
|
||||||
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||||
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
mach.ProcessCharacter(L'\x9d');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam);
|
||||||
|
mach.ProcessCharacter(AsciiChars::BEL);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_METHOD(TestOscStringSimple)
|
TEST_METHOD(TestOscStringSimple)
|
||||||
{
|
{
|
||||||
auto dispatch = std::make_unique<DummyDispatch>();
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
@ -596,6 +622,35 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestOscStringInvalidTermination)
|
||||||
|
{
|
||||||
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||||
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L']');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam);
|
||||||
|
mach.ProcessCharacter(L'1');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam);
|
||||||
|
mach.ProcessCharacter(L';');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString);
|
||||||
|
mach.ProcessCharacter(L's');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscTermination);
|
||||||
|
mach.ProcessCharacter(L'['); // This is not a string terminator.
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiEntry);
|
||||||
|
mach.ProcessCharacter(L'4');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiParam);
|
||||||
|
mach.ProcessCharacter(L';');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiParam);
|
||||||
|
mach.ProcessCharacter(L'm');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_METHOD(TestDcsEntry)
|
TEST_METHOD(TestDcsEntry)
|
||||||
{
|
{
|
||||||
auto dispatch = std::make_unique<DummyDispatch>();
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
@ -752,6 +807,143 @@ class Microsoft::Console::VirtualTerminal::OutputEngineTest final
|
||||||
mach.ProcessCharacter(L'\\');
|
mach.ProcessCharacter(L'\\');
|
||||||
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestDcsInvalidTermination)
|
||||||
|
{
|
||||||
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||||
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'P');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsEntry);
|
||||||
|
mach.ProcessCharacter(L'q');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsPassThrough);
|
||||||
|
mach.ProcessCharacter(L'#');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsPassThrough);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsTermination);
|
||||||
|
mach.ProcessCharacter(L'['); // This is not a string terminator.
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiEntry);
|
||||||
|
mach.ProcessCharacter(L'4');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiParam);
|
||||||
|
mach.ProcessCharacter(L';');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::CsiParam);
|
||||||
|
mach.ProcessCharacter(L'm');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestSosPmApcString)
|
||||||
|
{
|
||||||
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||||
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'X');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'1');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'2');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcTermination);
|
||||||
|
mach.ProcessCharacter(L'\\');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'^');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'3');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'4');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcTermination);
|
||||||
|
mach.ProcessCharacter(L'\\');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'_');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'5');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'6');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcTermination);
|
||||||
|
mach.ProcessCharacter(L'\\');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_METHOD(TestC1StringTerminator)
|
||||||
|
{
|
||||||
|
auto dispatch = std::make_unique<DummyDispatch>();
|
||||||
|
auto engine = std::make_unique<OutputStateMachineEngine>(std::move(dispatch));
|
||||||
|
StateMachine mach(std::move(engine));
|
||||||
|
|
||||||
|
// C1 ST should terminate OSC string.
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L']');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam);
|
||||||
|
mach.ProcessCharacter(L'1');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscParam);
|
||||||
|
mach.ProcessCharacter(L';');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString);
|
||||||
|
mach.ProcessCharacter(L's');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::OscString);
|
||||||
|
mach.ProcessCharacter(L'\x9c');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
// C1 ST should terminate DCS passthrough string.
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'P');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsEntry);
|
||||||
|
mach.ProcessCharacter(L'q');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsPassThrough);
|
||||||
|
mach.ProcessCharacter(L'#');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::DcsPassThrough);
|
||||||
|
mach.ProcessCharacter(L'1');
|
||||||
|
mach.ProcessCharacter(L'\x9c');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
// C1 ST should terminate SOS/PM/APC string.
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'X');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'1');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'\x9c');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'^');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'2');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'\x9c');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
|
||||||
|
mach.ProcessCharacter(AsciiChars::ESC);
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Escape);
|
||||||
|
mach.ProcessCharacter(L'_');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'3');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::SosPmApcString);
|
||||||
|
mach.ProcessCharacter(L'\x9c');
|
||||||
|
VERIFY_ARE_EQUAL(mach._state, StateMachine::VTStates::Ground);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class StatefulDispatch final : public TermDispatch
|
class StatefulDispatch final : public TermDispatch
|
||||||
|
|
Loading…
Reference in a new issue