From c60de41888f13c818bc048af2f16e19eb1afab5b Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 30 Jul 2020 18:01:15 +0100 Subject: [PATCH 01/11] Split the Escape Sequence action codes from the Control Sequence codes and reorder alphabetically. --- .../parser/OutputStateMachineEngine.cpp | 182 +++++++++--------- .../parser/OutputStateMachineEngine.hpp | 85 ++++---- 2 files changed, 135 insertions(+), 132 deletions(-) diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 4d5b4d0c8ac..4257b129391 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -193,67 +193,67 @@ bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch, { switch (wch) { - case VTActionCodes::DECSC_CursorSave: + case EscActionCodes::DECSC_CursorSave: success = _dispatch->CursorSaveState(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSC); break; - case VTActionCodes::DECRC_CursorRestore: + case EscActionCodes::DECRC_CursorRestore: success = _dispatch->CursorRestoreState(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRC); break; - case VTActionCodes::DECKPAM_KeypadApplicationMode: + case EscActionCodes::DECKPAM_KeypadApplicationMode: success = _dispatch->SetKeypadMode(true); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPAM); break; - case VTActionCodes::DECKPNM_KeypadNumericMode: + case EscActionCodes::DECKPNM_KeypadNumericMode: success = _dispatch->SetKeypadMode(false); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPNM); break; - case VTActionCodes::NEL_NextLine: + case EscActionCodes::NEL_NextLine: success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithReturn); TermTelemetry::Instance().Log(TermTelemetry::Codes::NEL); break; - case VTActionCodes::IND_Index: + case EscActionCodes::IND_Index: success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithoutReturn); TermTelemetry::Instance().Log(TermTelemetry::Codes::IND); break; - case VTActionCodes::RI_ReverseLineFeed: + case EscActionCodes::RI_ReverseLineFeed: success = _dispatch->ReverseLineFeed(); TermTelemetry::Instance().Log(TermTelemetry::Codes::RI); break; - case VTActionCodes::HTS_HorizontalTabSet: + case EscActionCodes::HTS_HorizontalTabSet: success = _dispatch->HorizontalTabSet(); TermTelemetry::Instance().Log(TermTelemetry::Codes::HTS); break; - case VTActionCodes::RIS_ResetToInitialState: + case EscActionCodes::RIS_ResetToInitialState: success = _dispatch->HardReset(); TermTelemetry::Instance().Log(TermTelemetry::Codes::RIS); break; - case VTActionCodes::SS2_SingleShift: + case EscActionCodes::SS2_SingleShift: success = _dispatch->SingleShift(2); TermTelemetry::Instance().Log(TermTelemetry::Codes::SS2); break; - case VTActionCodes::SS3_SingleShift: + case EscActionCodes::SS3_SingleShift: success = _dispatch->SingleShift(3); TermTelemetry::Instance().Log(TermTelemetry::Codes::SS3); break; - case VTActionCodes::LS2_LockingShift: + case EscActionCodes::LS2_LockingShift: success = _dispatch->LockingShift(2); TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2); break; - case VTActionCodes::LS3_LockingShift: + case EscActionCodes::LS3_LockingShift: success = _dispatch->LockingShift(3); TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3); break; - case VTActionCodes::LS1R_LockingShift: + case EscActionCodes::LS1R_LockingShift: success = _dispatch->LockingShiftRight(1); TermTelemetry::Instance().Log(TermTelemetry::Codes::LS1R); break; - case VTActionCodes::LS2R_LockingShift: + case EscActionCodes::LS2R_LockingShift: success = _dispatch->LockingShiftRight(2); TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2R); break; - case VTActionCodes::LS3R_LockingShift: + case EscActionCodes::LS3R_LockingShift: success = _dispatch->LockingShiftRight(3); TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3R); break; @@ -274,7 +274,7 @@ bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch, case L'#': switch (wch) { - case VTActionCodes::DECALN_ScreenAlignmentPattern: + case EscActionCodes::DECALN_ScreenAlignmentPattern: success = _dispatch->ScreenAlignmentPattern(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECALN); break; @@ -477,65 +477,65 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, // fill params switch (wch) { - case VTActionCodes::CUU_CursorUp: - case VTActionCodes::CUD_CursorDown: - case VTActionCodes::CUF_CursorForward: - case VTActionCodes::CUB_CursorBackward: - case VTActionCodes::CNL_CursorNextLine: - case VTActionCodes::CPL_CursorPrevLine: - case VTActionCodes::CHA_CursorHorizontalAbsolute: - case VTActionCodes::HPA_HorizontalPositionAbsolute: - case VTActionCodes::VPA_VerticalLinePositionAbsolute: - case VTActionCodes::HPR_HorizontalPositionRelative: - case VTActionCodes::VPR_VerticalPositionRelative: - case VTActionCodes::ICH_InsertCharacter: - case VTActionCodes::DCH_DeleteCharacter: - case VTActionCodes::ECH_EraseCharacters: + case CsiActionCodes::CUU_CursorUp: + case CsiActionCodes::CUD_CursorDown: + case CsiActionCodes::CUF_CursorForward: + case CsiActionCodes::CUB_CursorBackward: + case CsiActionCodes::CNL_CursorNextLine: + case CsiActionCodes::CPL_CursorPrevLine: + case CsiActionCodes::CHA_CursorHorizontalAbsolute: + case CsiActionCodes::HPA_HorizontalPositionAbsolute: + case CsiActionCodes::VPA_VerticalLinePositionAbsolute: + case CsiActionCodes::HPR_HorizontalPositionRelative: + case CsiActionCodes::VPR_VerticalPositionRelative: + case CsiActionCodes::ICH_InsertCharacter: + case CsiActionCodes::DCH_DeleteCharacter: + case CsiActionCodes::ECH_EraseCharacters: success = _GetCursorDistance(parameters, distance); break; - case VTActionCodes::HVP_HorizontalVerticalPosition: - case VTActionCodes::CUP_CursorPosition: + case CsiActionCodes::HVP_HorizontalVerticalPosition: + case CsiActionCodes::CUP_CursorPosition: success = _GetXYPosition(parameters, line, column); break; - case VTActionCodes::DECSTBM_SetScrollingRegion: + case CsiActionCodes::DECSTBM_SetScrollingRegion: success = _GetTopBottomMargins(parameters, topMargin, bottomMargin); break; - case VTActionCodes::ED_EraseDisplay: - case VTActionCodes::EL_EraseLine: + case CsiActionCodes::ED_EraseDisplay: + case CsiActionCodes::EL_EraseLine: success = _GetEraseOperation(parameters, eraseType); break; - case VTActionCodes::SGR_SetGraphicsRendition: + case CsiActionCodes::SGR_SetGraphicsRendition: success = _GetGraphicsOptions(parameters, _graphicsOptions); break; - case VTActionCodes::DSR_DeviceStatusReport: + case CsiActionCodes::DSR_DeviceStatusReport: success = _GetDeviceStatusOperation(parameters, deviceStatusType); break; - case VTActionCodes::DA_DeviceAttributes: + case CsiActionCodes::DA_DeviceAttributes: success = _VerifyDeviceAttributesParams(parameters); break; - case VTActionCodes::SU_ScrollUp: - case VTActionCodes::SD_ScrollDown: + case CsiActionCodes::SU_ScrollUp: + case CsiActionCodes::SD_ScrollDown: success = _GetScrollDistance(parameters, distance); break; - case VTActionCodes::ANSISYSSC_CursorSave: - case VTActionCodes::ANSISYSRC_CursorRestore: + case CsiActionCodes::ANSISYSSC_CursorSave: + case CsiActionCodes::ANSISYSRC_CursorRestore: success = _VerifyHasNoParameters(parameters); break; - case VTActionCodes::IL_InsertLine: - case VTActionCodes::DL_DeleteLine: + case CsiActionCodes::IL_InsertLine: + case CsiActionCodes::DL_DeleteLine: success = _GetScrollDistance(parameters, distance); break; - case VTActionCodes::CHT_CursorForwardTab: - case VTActionCodes::CBT_CursorBackTab: + case CsiActionCodes::CHT_CursorForwardTab: + case CsiActionCodes::CBT_CursorBackTab: success = _GetTabDistance(parameters, numTabs); break; - case VTActionCodes::TBC_TabClear: + case CsiActionCodes::TBC_TabClear: success = _GetTabClearType(parameters, clearType); break; - case VTActionCodes::DTTERM_WindowManipulation: + case CsiActionCodes::DTTERM_WindowManipulation: success = _GetWindowManipulationType(parameters, function); break; - case VTActionCodes::REP_RepeatCharacter: + case CsiActionCodes::REP_RepeatCharacter: success = _GetRepeatCount(parameters, repeatCount); break; default: @@ -549,130 +549,130 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, { switch (wch) { - case VTActionCodes::CUU_CursorUp: + case CsiActionCodes::CUU_CursorUp: success = _dispatch->CursorUp(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CUU); break; - case VTActionCodes::CUD_CursorDown: + case CsiActionCodes::CUD_CursorDown: success = _dispatch->CursorDown(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CUD); break; - case VTActionCodes::CUF_CursorForward: + case CsiActionCodes::CUF_CursorForward: success = _dispatch->CursorForward(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CUF); break; - case VTActionCodes::CUB_CursorBackward: + case CsiActionCodes::CUB_CursorBackward: success = _dispatch->CursorBackward(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CUB); break; - case VTActionCodes::CNL_CursorNextLine: + case CsiActionCodes::CNL_CursorNextLine: success = _dispatch->CursorNextLine(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CNL); break; - case VTActionCodes::CPL_CursorPrevLine: + case CsiActionCodes::CPL_CursorPrevLine: success = _dispatch->CursorPrevLine(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CPL); break; - case VTActionCodes::CHA_CursorHorizontalAbsolute: - case VTActionCodes::HPA_HorizontalPositionAbsolute: + case CsiActionCodes::CHA_CursorHorizontalAbsolute: + case CsiActionCodes::HPA_HorizontalPositionAbsolute: success = _dispatch->CursorHorizontalPositionAbsolute(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::CHA); break; - case VTActionCodes::VPA_VerticalLinePositionAbsolute: + case CsiActionCodes::VPA_VerticalLinePositionAbsolute: success = _dispatch->VerticalLinePositionAbsolute(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::VPA); break; - case VTActionCodes::HPR_HorizontalPositionRelative: + case CsiActionCodes::HPR_HorizontalPositionRelative: success = _dispatch->HorizontalPositionRelative(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::HPR); break; - case VTActionCodes::VPR_VerticalPositionRelative: + case CsiActionCodes::VPR_VerticalPositionRelative: success = _dispatch->VerticalPositionRelative(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::VPR); break; - case VTActionCodes::CUP_CursorPosition: - case VTActionCodes::HVP_HorizontalVerticalPosition: + case CsiActionCodes::CUP_CursorPosition: + case CsiActionCodes::HVP_HorizontalVerticalPosition: success = _dispatch->CursorPosition(line, column); TermTelemetry::Instance().Log(TermTelemetry::Codes::CUP); break; - case VTActionCodes::DECSTBM_SetScrollingRegion: + case CsiActionCodes::DECSTBM_SetScrollingRegion: success = _dispatch->SetTopBottomScrollingMargins(topMargin, bottomMargin); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTBM); break; - case VTActionCodes::ICH_InsertCharacter: + case CsiActionCodes::ICH_InsertCharacter: success = _dispatch->InsertCharacter(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::ICH); break; - case VTActionCodes::DCH_DeleteCharacter: + case CsiActionCodes::DCH_DeleteCharacter: success = _dispatch->DeleteCharacter(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::DCH); break; - case VTActionCodes::ED_EraseDisplay: + case CsiActionCodes::ED_EraseDisplay: success = _dispatch->EraseInDisplay(eraseType); TermTelemetry::Instance().Log(TermTelemetry::Codes::ED); break; - case VTActionCodes::EL_EraseLine: + case CsiActionCodes::EL_EraseLine: success = _dispatch->EraseInLine(eraseType); TermTelemetry::Instance().Log(TermTelemetry::Codes::EL); break; - case VTActionCodes::SGR_SetGraphicsRendition: + case CsiActionCodes::SGR_SetGraphicsRendition: success = _dispatch->SetGraphicsRendition({ _graphicsOptions.data(), _graphicsOptions.size() }); TermTelemetry::Instance().Log(TermTelemetry::Codes::SGR); break; - case VTActionCodes::DSR_DeviceStatusReport: + case CsiActionCodes::DSR_DeviceStatusReport: success = _dispatch->DeviceStatusReport(deviceStatusType); TermTelemetry::Instance().Log(TermTelemetry::Codes::DSR); break; - case VTActionCodes::DA_DeviceAttributes: + case CsiActionCodes::DA_DeviceAttributes: success = _dispatch->DeviceAttributes(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DA); break; - case VTActionCodes::SU_ScrollUp: + case CsiActionCodes::SU_ScrollUp: success = _dispatch->ScrollUp(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::SU); break; - case VTActionCodes::SD_ScrollDown: + case CsiActionCodes::SD_ScrollDown: success = _dispatch->ScrollDown(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::SD); break; - case VTActionCodes::ANSISYSSC_CursorSave: + case CsiActionCodes::ANSISYSSC_CursorSave: success = _dispatch->CursorSaveState(); TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSSC); break; - case VTActionCodes::ANSISYSRC_CursorRestore: + case CsiActionCodes::ANSISYSRC_CursorRestore: success = _dispatch->CursorRestoreState(); TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSRC); break; - case VTActionCodes::IL_InsertLine: + case CsiActionCodes::IL_InsertLine: success = _dispatch->InsertLine(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::IL); break; - case VTActionCodes::DL_DeleteLine: + case CsiActionCodes::DL_DeleteLine: success = _dispatch->DeleteLine(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::DL); break; - case VTActionCodes::CHT_CursorForwardTab: + case CsiActionCodes::CHT_CursorForwardTab: success = _dispatch->ForwardTab(numTabs); TermTelemetry::Instance().Log(TermTelemetry::Codes::CHT); break; - case VTActionCodes::CBT_CursorBackTab: + case CsiActionCodes::CBT_CursorBackTab: success = _dispatch->BackwardsTab(numTabs); TermTelemetry::Instance().Log(TermTelemetry::Codes::CBT); break; - case VTActionCodes::TBC_TabClear: + case CsiActionCodes::TBC_TabClear: success = _dispatch->TabClear(clearType); TermTelemetry::Instance().Log(TermTelemetry::Codes::TBC); break; - case VTActionCodes::ECH_EraseCharacters: + case CsiActionCodes::ECH_EraseCharacters: success = _dispatch->EraseCharacters(distance); TermTelemetry::Instance().Log(TermTelemetry::Codes::ECH); break; - case VTActionCodes::DTTERM_WindowManipulation: + case CsiActionCodes::DTTERM_WindowManipulation: success = _dispatch->WindowManipulation(static_cast(function), remainingParams); TermTelemetry::Instance().Log(TermTelemetry::Codes::DTTERM_WM); break; - case VTActionCodes::REP_RepeatCharacter: + case CsiActionCodes::REP_RepeatCharacter: // Handled w/o the dispatch. This function is unique in that way // If this were in the ITerminalDispatch, then each // implementation would effectively be the same, calling only @@ -745,8 +745,8 @@ bool OutputStateMachineEngine::_IntermediateQuestionMarkDispatch(const wchar_t w // Ensure that there was the right number of params switch (wchAction) { - case VTActionCodes::DECSET_PrivateModeSet: - case VTActionCodes::DECRST_PrivateModeReset: + case CsiActionCodes::DECSET_PrivateModeSet: + case CsiActionCodes::DECRST_PrivateModeReset: success = _GetPrivateModeParams(parameters, privateModeParams); break; @@ -759,12 +759,12 @@ bool OutputStateMachineEngine::_IntermediateQuestionMarkDispatch(const wchar_t w { switch (wchAction) { - case VTActionCodes::DECSET_PrivateModeSet: + case CsiActionCodes::DECSET_PrivateModeSet: success = _dispatch->SetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); //TODO: MSFT:6367459 Add specific logging for each of the DECSET/DECRST codes TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSET); break; - case VTActionCodes::DECRST_PrivateModeReset: + case CsiActionCodes::DECRST_PrivateModeReset: success = _dispatch->ResetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRST); break; @@ -793,7 +793,7 @@ bool OutputStateMachineEngine::_IntermediateGreaterThanOrEqualDispatch(const wch switch (wch) { - case VTActionCodes::DA_DeviceAttributes: + case CsiActionCodes::DA_DeviceAttributes: if (_VerifyDeviceAttributesParams(parameters)) { switch (intermediate) @@ -826,7 +826,7 @@ bool OutputStateMachineEngine::_IntermediateExclamationDispatch(const wchar_t wc switch (wchAction) { - case VTActionCodes::DECSTR_SoftReset: + case CsiActionCodes::DECSTR_SoftReset: success = _dispatch->SoftReset(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTR); break; @@ -854,7 +854,7 @@ bool OutputStateMachineEngine::_IntermediateSpaceDispatch(const wchar_t wchActio // Parse params switch (wchAction) { - case VTActionCodes::DECSCUSR_SetCursorStyle: + case CsiActionCodes::DECSCUSR_SetCursorStyle: success = _GetCursorStyle(parameters, cursorStyle); break; default: @@ -868,7 +868,7 @@ bool OutputStateMachineEngine::_IntermediateSpaceDispatch(const wchar_t wchActio { switch (wchAction) { - case VTActionCodes::DECSCUSR_SetCursorStyle: + case CsiActionCodes::DECSCUSR_SetCursorStyle: success = _dispatch->SetCursorStyle(cursorStyle); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSCUSR); break; diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index bce98255e0e..d9478325484 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -84,8 +84,30 @@ namespace Microsoft::Console::VirtualTerminal bool _IntermediateSpaceDispatch(const wchar_t wchAction, const gsl::span parameters); - enum VTActionCodes : wchar_t + enum EscActionCodes : wchar_t { + DECSC_CursorSave = L'7', + DECRC_CursorRestore = L'8', + DECKPAM_KeypadApplicationMode = L'=', + DECKPNM_KeypadNumericMode = L'>', + IND_Index = L'D', + NEL_NextLine = L'E', + HTS_HorizontalTabSet = L'H', + RI_ReverseLineFeed = L'M', + SS2_SingleShift = L'N', + SS3_SingleShift = L'O', + RIS_ResetToInitialState = L'c', + LS2_LockingShift = L'n', + LS3_LockingShift = L'o', + LS1R_LockingShift = L'~', + LS2R_LockingShift = L'}', + LS3R_LockingShift = L'|', + DECALN_ScreenAlignmentPattern = L'8' // With # intermediate + }; + + enum CsiActionCodes : wchar_t + { + ICH_InsertCharacter = L'@', CUU_CursorUp = L'A', CUD_CursorDown = L'B', CUF_CursorForward = L'C', @@ -94,54 +116,35 @@ namespace Microsoft::Console::VirtualTerminal CPL_CursorPrevLine = L'F', CHA_CursorHorizontalAbsolute = L'G', CUP_CursorPosition = L'H', + CHT_CursorForwardTab = L'I', ED_EraseDisplay = L'J', EL_EraseLine = L'K', + IL_InsertLine = L'L', + DL_DeleteLine = L'M', + DCH_DeleteCharacter = L'P', SU_ScrollUp = L'S', SD_ScrollDown = L'T', - ICH_InsertCharacter = L'@', - DCH_DeleteCharacter = L'P', - SGR_SetGraphicsRendition = L'm', - DECSC_CursorSave = L'7', - DECRC_CursorRestore = L'8', - DECSET_PrivateModeSet = L'h', - 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. + ECH_EraseCharacters = L'X', + CBT_CursorBackTab = L'Z', HPA_HorizontalPositionAbsolute = L'`', - VPA_VerticalLinePositionAbsolute = L'd', HPR_HorizontalPositionRelative = L'a', + REP_RepeatCharacter = L'b', + DA_DeviceAttributes = L'c', + VPA_VerticalLinePositionAbsolute = L'd', 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' + TBC_TabClear = L'g', + DECSET_PrivateModeSet = L'h', + DECRST_PrivateModeReset = L'l', + SGR_SetGraphicsRendition = L'm', + DSR_DeviceStatusReport = L'n', + DECSTBM_SetScrollingRegion = L'r', + ANSISYSSC_CursorSave = L's', // NOTE: Overlaps with DECLRMM/DECSLRM. Fix when/if implemented. + DTTERM_WindowManipulation = L't', // NOTE: Overlaps with DECSLPP. Fix when/if implemented. + ANSISYSRC_CursorRestore = L'u', // NOTE: Overlaps with DECSMBV. Fix when/if implemented. + DECSCUSR_SetCursorStyle = L'q', // With SP intermediate + DECSTR_SoftReset = L'p', // With ! intermediate + DECSCPP_SetColumnsPerPage = L'|' // With $ intermediate }; enum Vt52ActionCodes : wchar_t From 6a97e81fee616f6d926532f380f1aefb86213813 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 13:18:37 +0100 Subject: [PATCH 02/11] Add VTID type to represent the intermediate/final identification characters of an escape sequence. --- src/terminal/adapter/DispatchTypes.hpp | 48 ++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 9de95ca1527..30f1a547d58 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -3,6 +3,54 @@ #pragma once +namespace Microsoft::Console::VirtualTerminal +{ + class VTID + { + public: + template + 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 + 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; + }; +} + namespace Microsoft::Console::VirtualTerminal::DispatchTypes { enum class EraseType : unsigned int From 31e83ddd3b2c6fe2fc6bac0e56a02060eca3a81e Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 13:21:32 +0100 Subject: [PATCH 03/11] Use the VTID type in place of a wchar_t pair to represent the VT character sets identifiers. --- src/terminal/adapter/DispatchTypes.hpp | 8 +- src/terminal/adapter/ITermDispatch.hpp | 4 +- src/terminal/adapter/adaptDispatch.cpp | 8 +- src/terminal/adapter/adaptDispatch.hpp | 4 +- src/terminal/adapter/termDispatch.hpp | 4 +- src/terminal/adapter/terminalOutput.cpp | 121 ++++++++---------- src/terminal/adapter/terminalOutput.hpp | 4 +- .../parser/OutputStateMachineEngine.cpp | 2 +- 8 files changed, 70 insertions(+), 85 deletions(-) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 30f1a547d58..96932000361 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -149,11 +149,11 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes W32IM_Win32InputMode = 9001 }; - namespace CharacterSets + enum CharacterSets : uint64_t { - constexpr auto DecSpecialGraphics = std::make_pair(L'0', L'\0'); - constexpr auto ASCII = std::make_pair(L'B', L'\0'); - } + DecSpecialGraphics = VTID("0"), + ASCII = VTID("B") + }; enum CodingSystem : wchar_t { diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 93f1dffeccd..932a0b07090 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -99,8 +99,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool Vt52DeviceAttributes() = 0; // VT52 Identify virtual bool DesignateCodingSystem(const wchar_t codingSystem) = 0; // DOCS - virtual bool Designate94Charset(const size_t gsetNumber, const std::pair charset) = 0; // SCS - virtual bool Designate96Charset(const size_t gsetNumber, const std::pair charset) = 0; // SCS + virtual bool Designate94Charset(const size_t gsetNumber, const VTID 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 LockingShiftRight(const size_t gsetNumber) = 0; // LS1R, LS2R, LS3R virtual bool SingleShift(const size_t gsetNumber) = 0; // SS2, SS3 diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 7578e4fbc9a..1199382b9c2 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1706,10 +1706,10 @@ bool AdaptDispatch::DesignateCodingSystem(const wchar_t codingSystem) // If the specified charset is unsupported, we do nothing (remain on the current one) //Arguments: // - 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: // True if handled successfully. False otherwise. -bool AdaptDispatch::Designate94Charset(const size_t gsetNumber, const std::pair charset) +bool AdaptDispatch::Designate94Charset(const size_t gsetNumber, const VTID charset) { return _termOutput.Designate94Charset(gsetNumber, charset); } @@ -1721,10 +1721,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) //Arguments: // - 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: // True if handled successfully. False otherwise. -bool AdaptDispatch::Designate96Charset(const size_t gsetNumber, const std::pair charset) +bool AdaptDispatch::Designate96Charset(const size_t gsetNumber, const VTID charset) { return _termOutput.Designate96Charset(gsetNumber, charset); } diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 12a0c902104..167124cb42c 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -90,8 +90,8 @@ namespace Microsoft::Console::VirtualTerminal bool BackwardsTab(const size_t numTabs) override; // CBT bool TabClear(const size_t clearType) override; // TBC bool DesignateCodingSystem(const wchar_t codingSystem) override; // DOCS - bool Designate94Charset(const size_t gsetNumber, const std::pair charset) override; // SCS - bool Designate96Charset(const size_t gsetNumber, const std::pair charset) override; // SCS + bool Designate94Charset(const size_t gsetNumber, const VTID 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 LockingShiftRight(const size_t gsetNumber) override; // LS1R, LS2R, LS3R bool SingleShift(const size_t gsetNumber) override; // SS2, SS3 diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index e13579c688e..53362edcbcc 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -93,8 +93,8 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool Vt52DeviceAttributes() noexcept override { return false; } // VT52 Identify bool DesignateCodingSystem(const wchar_t /*codingSystem*/) noexcept override { return false; } // DOCS - bool Designate94Charset(const size_t /*gsetNumber*/, const std::pair /*charset*/) noexcept override { return false; } // SCS - bool Designate96Charset(const size_t /*gsetNumber*/, const std::pair /*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 VTID /*charset*/) noexcept override { return false; } // SCS 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 SingleShift(const size_t /*gsetNumber*/) noexcept override { return false; } // SS2, SS3 diff --git a/src/terminal/adapter/terminalOutput.cpp b/src/terminal/adapter/terminalOutput.cpp index a7b7c58aee7..14de84e1393 100644 --- a/src/terminal/adapter/terminalOutput.cpp +++ b/src/terminal/adapter/terminalOutput.cpp @@ -17,104 +17,89 @@ TerminalOutput::TerminalOutput() noexcept _gsetTranslationTables.at(3) = Latin1; } -bool TerminalOutput::Designate94Charset(size_t gsetNumber, const std::pair charset) +bool TerminalOutput::Designate94Charset(size_t gsetNumber, const VTID charset) { - switch (charset.first) + switch (charset) { - case L'B': // US ASCII - case L'1': // Alternate Character ROM + case VTID("B"): // US ASCII + case VTID("1"): // Alternate Character ROM return _SetTranslationTable(gsetNumber, Ascii); - case L'0': // DEC Special Graphics - case L'2': // Alternate Character ROM Special Graphics + case VTID("0"): // DEC Special Graphics + case VTID("2"): // Alternate Character ROM Special Graphics return _SetTranslationTable(gsetNumber, DecSpecialGraphics); - case L'<': // DEC Supplemental + case VTID("<"): // DEC Supplemental return _SetTranslationTable(gsetNumber, DecSupplemental); - case L'A': // British NRCS + case VTID("A"): // British NRCS return _SetTranslationTable(gsetNumber, BritishNrcs); - case L'4': // Dutch NRCS + case VTID("4"): // Dutch NRCS return _SetTranslationTable(gsetNumber, DutchNrcs); - case L'5': // Finnish NRCS - case L'C': // (fallback) + case VTID("5"): // Finnish NRCS + case VTID("C"): // (fallback) return _SetTranslationTable(gsetNumber, FinnishNrcs); - case L'R': // French NRCS + case VTID("R"): // French NRCS return _SetTranslationTable(gsetNumber, FrenchNrcs); - case L'f': // French NRCS (ISO update) + case VTID("f"): // French NRCS (ISO update) return _SetTranslationTable(gsetNumber, FrenchNrcsIso); - case L'9': // French Canadian NRCS - case L'Q': // (fallback) + case VTID("9"): // French Canadian NRCS + case VTID("Q"): // (fallback) return _SetTranslationTable(gsetNumber, FrenchCanadianNrcs); - case L'K': // German NRCS + case VTID("K"): // German NRCS return _SetTranslationTable(gsetNumber, GermanNrcs); - case L'Y': // Italian NRCS + case VTID("Y"): // Italian NRCS return _SetTranslationTable(gsetNumber, ItalianNrcs); - case L'6': // Norwegian/Danish NRCS - case L'E': // (fallback) + case VTID("6"): // Norwegian/Danish NRCS + case VTID("E"): // (fallback) return _SetTranslationTable(gsetNumber, NorwegianDanishNrcs); - case L'`': // Norwegian/Danish NRCS (ISO standard) + case VTID("`"): // Norwegian/Danish NRCS (ISO standard) return _SetTranslationTable(gsetNumber, NorwegianDanishNrcsIso); - case L'Z': // Spanish NRCS + case VTID("Z"): // Spanish NRCS return _SetTranslationTable(gsetNumber, SpanishNrcs); - case L'7': // Swedish NRCS - case L'H': // (fallback) + case VTID("7"): // Swedish NRCS + case VTID("H"): // (fallback) return _SetTranslationTable(gsetNumber, SwedishNrcs); - case L'=': // Swiss NRCS + case VTID("="): // Swiss NRCS return _SetTranslationTable(gsetNumber, SwissNrcs); - case L'&': - switch (charset.second) - { - case L'4': // DEC Cyrillic - return _SetTranslationTable(gsetNumber, DecCyrillic); - case L'5': // Russian NRCS - return _SetTranslationTable(gsetNumber, RussianNrcs); - } - return false; - case L'"': - switch (charset.second) - { - case L'?': // DEC Greek - return _SetTranslationTable(gsetNumber, DecGreek); - case L'>': // Greek NRCS - return _SetTranslationTable(gsetNumber, GreekNrcs); - case L'4': // DEC Hebrew - return _SetTranslationTable(gsetNumber, DecHebrew); - } - 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); - } - return false; + case VTID("&4"): // DEC Cyrillic + return _SetTranslationTable(gsetNumber, DecCyrillic); + case VTID("&5"): // Russian NRCS + return _SetTranslationTable(gsetNumber, RussianNrcs); + case VTID("\"?"): // DEC Greek + return _SetTranslationTable(gsetNumber, DecGreek); + case VTID("\">"): // Greek NRCS + return _SetTranslationTable(gsetNumber, GreekNrcs); + case VTID("\"4"): // DEC Hebrew + return _SetTranslationTable(gsetNumber, DecHebrew); + case VTID("%="): // Hebrew NRCS + return _SetTranslationTable(gsetNumber, HebrewNrcs); + case VTID("%0"): // DEC Turkish + return _SetTranslationTable(gsetNumber, DecTurkish); + case VTID("%2"): // Turkish NRCS + return _SetTranslationTable(gsetNumber, TurkishNrcs); + case VTID("%5"): // DEC Supplemental + return _SetTranslationTable(gsetNumber, DecSupplemental); + case VTID("%6"): // Portuguese NRCS + return _SetTranslationTable(gsetNumber, PortugueseNrcs); default: return false; } } -bool TerminalOutput::Designate96Charset(size_t gsetNumber, const std::pair charset) +bool TerminalOutput::Designate96Charset(size_t gsetNumber, const VTID charset) { - switch (charset.first) + switch (charset) { - case L'A': // ISO Latin-1 Supplemental - case L'<': // (UPSS when assigned to Latin-1) + case VTID("A"): // ISO Latin-1 Supplemental + case VTID("<"): // (UPSS when assigned to Latin-1) return _SetTranslationTable(gsetNumber, Latin1); - case L'B': // ISO Latin-2 Supplemental + case VTID("B"): // ISO Latin-2 Supplemental return _SetTranslationTable(gsetNumber, Latin2); - case L'L': // ISO Latin-Cyrillic Supplemental + case VTID("L"): // ISO Latin-Cyrillic Supplemental return _SetTranslationTable(gsetNumber, LatinCyrillic); - case L'F': // ISO Latin-Greek Supplemental + case VTID("F"): // ISO Latin-Greek Supplemental return _SetTranslationTable(gsetNumber, LatinGreek); - case L'H': // ISO Latin-Hebrew Supplemental + case VTID("H"): // ISO Latin-Hebrew Supplemental return _SetTranslationTable(gsetNumber, LatinHebrew); - case L'M': // ISO Latin-5 Supplemental + case VTID("M"): // ISO Latin-5 Supplemental return _SetTranslationTable(gsetNumber, Latin5); default: return false; diff --git a/src/terminal/adapter/terminalOutput.hpp b/src/terminal/adapter/terminalOutput.hpp index a26f6d85a61..e82e067a7c2 100644 --- a/src/terminal/adapter/terminalOutput.hpp +++ b/src/terminal/adapter/terminalOutput.hpp @@ -26,8 +26,8 @@ namespace Microsoft::Console::VirtualTerminal TerminalOutput() noexcept; wchar_t TranslateKey(const wchar_t wch) const noexcept; - bool Designate94Charset(const size_t gsetNumber, const std::pair charset); - bool Designate96Charset(const size_t gsetNumber, const std::pair charset); + bool Designate94Charset(const size_t gsetNumber, const VTID charset); + bool Designate96Charset(const size_t gsetNumber, const VTID charset); bool LockingShift(const size_t gsetNumber); bool LockingShiftRight(const size_t gsetNumber); bool SingleShift(const size_t gsetNumber); diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 4257b129391..d0b6e2f4d5f 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -403,7 +403,7 @@ bool OutputStateMachineEngine::_IntermediateScsDispatch(const wchar_t wch, // If we have more than one intermediate, the second intermediate forms part of // the charset identifier. Otherwise it's identified by just the final character. - const auto charset = intermediates.size() > 1 ? std::make_pair(til::at(intermediates, 1), wch) : std::make_pair(wch, L'\0'); + const VTID charset = intermediates.size() > 1 ? (wch << 8) + til::at(intermediates, 1) : wch; switch (til::at(intermediates, 0)) { From 97250a059d73bf857f9c835d1f81f1f3d7fe0d30 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 15:49:44 +0100 Subject: [PATCH 04/11] Use the VTID type for the DesignateCodingSystem identifier. --- src/terminal/adapter/DispatchTypes.hpp | 6 +++--- src/terminal/adapter/ITermDispatch.hpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 2 +- src/terminal/adapter/adaptDispatch.hpp | 2 +- src/terminal/adapter/termDispatch.hpp | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 96932000361..109ec0f1d48 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -155,10 +155,10 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes ASCII = VTID("B") }; - enum CodingSystem : wchar_t + enum CodingSystem : uint64_t { - ISO2022 = L'@', - UTF8 = L'G' + ISO2022 = VTID("@"), + UTF8 = VTID("G") }; enum TabClearType : unsigned short diff --git a/src/terminal/adapter/ITermDispatch.hpp b/src/terminal/adapter/ITermDispatch.hpp index 932a0b07090..b90705ac4a9 100644 --- a/src/terminal/adapter/ITermDispatch.hpp +++ b/src/terminal/adapter/ITermDispatch.hpp @@ -98,7 +98,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch virtual bool TertiaryDeviceAttributes() = 0; // DA3 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 VTID 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 diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 1199382b9c2..390eec2b74f 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1667,7 +1667,7 @@ void AdaptDispatch::_InitTabStopsForWidth(const size_t width) // - codingSystem - The coding system that will be selected. // Return value: // 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. // This will be used to restore the code page in response to a reset. diff --git a/src/terminal/adapter/adaptDispatch.hpp b/src/terminal/adapter/adaptDispatch.hpp index 167124cb42c..2e5f27a7823 100644 --- a/src/terminal/adapter/adaptDispatch.hpp +++ b/src/terminal/adapter/adaptDispatch.hpp @@ -89,7 +89,7 @@ namespace Microsoft::Console::VirtualTerminal bool ForwardTab(const size_t numTabs) override; // CHT, HT bool BackwardsTab(const size_t numTabs) override; // CBT 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 VTID 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 diff --git a/src/terminal/adapter/termDispatch.hpp b/src/terminal/adapter/termDispatch.hpp index 53362edcbcc..47ecd990784 100644 --- a/src/terminal/adapter/termDispatch.hpp +++ b/src/terminal/adapter/termDispatch.hpp @@ -92,7 +92,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons bool TertiaryDeviceAttributes() noexcept override { return false; } // DA3 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 VTID /*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 From b06e2b4104365e9878c6bd5de63ed7f962090800 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 19:13:14 +0100 Subject: [PATCH 05/11] Add a builder class for constructing a VTID one character at a time. --- src/terminal/adapter/DispatchTypes.hpp | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/terminal/adapter/DispatchTypes.hpp b/src/terminal/adapter/DispatchTypes.hpp index 109ec0f1d48..1454ed279e4 100644 --- a/src/terminal/adapter/DispatchTypes.hpp +++ b/src/terminal/adapter/DispatchTypes.hpp @@ -49,6 +49,46 @@ namespace Microsoft::Console::VirtualTerminal 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(intermediateChar) << _idShift); + _idShift += CHAR_BIT; + } + } + + VTID Finalize(const wchar_t finalChar) noexcept + { + return _idAccumulator + (static_cast(finalChar) << _idShift); + } + + private: + uint64_t _idAccumulator = 0; + size_t _idShift = 0; + }; } namespace Microsoft::Console::VirtualTerminal::DispatchTypes From fcfb41f641453a4691a57cd9f157358d29a09aa4 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 19:24:14 +0100 Subject: [PATCH 06/11] Use a VTID instead of separate intermediate and final parameters in the escape dispatchers. --- src/terminal/parser/IStateMachineEngine.hpp | 6 +- .../parser/InputStateMachineEngine.cpp | 9 +- .../parser/InputStateMachineEngine.hpp | 3 +- .../parser/OutputStateMachineEngine.cpp | 243 +++++++----------- .../parser/OutputStateMachineEngine.hpp | 41 ++- src/terminal/parser/stateMachine.cpp | 7 +- .../parser/ut_parser/StateMachineTest.cpp | 3 +- 7 files changed, 135 insertions(+), 177 deletions(-) diff --git a/src/terminal/parser/IStateMachineEngine.hpp b/src/terminal/parser/IStateMachineEngine.hpp index c1a668fb72c..02a27bfe07e 100644 --- a/src/terminal/parser/IStateMachineEngine.hpp +++ b/src/terminal/parser/IStateMachineEngine.hpp @@ -12,6 +12,9 @@ Module Name: the existing VT parsing. */ #pragma once + +#include "../adapter/DispatchTypes.hpp" + namespace Microsoft::Console::VirtualTerminal { class IStateMachineEngine @@ -30,8 +33,7 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ActionPassThroughString(const std::wstring_view string) = 0; - virtual bool ActionEscDispatch(const wchar_t wch, - const gsl::span intermediates) = 0; + virtual bool ActionEscDispatch(const VTID id) = 0; virtual bool ActionVt52EscDispatch(const wchar_t wch, const gsl::span intermediates, const gsl::span parameters) = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 59f34e20ab9..146e2057d65 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -298,12 +298,10 @@ bool InputStateMachineEngine::ActionPassThroughString(const std::wstring_view st // a simple escape sequence. These sequences traditionally start with ESC // and a simple letter. No complicated parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence +// - id - Identifier of the escape sequence to dispatch. // Return Value: // - true iff we successfully dispatched the sequence. -bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch, - const gsl::span /*intermediates*/) +bool InputStateMachineEngine::ActionEscDispatch(const VTID id) { if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue) { @@ -312,6 +310,9 @@ bool InputStateMachineEngine::ActionEscDispatch(const wchar_t wch, bool success = false; + // There are no intermediates, so the id is effectively the final char. + const wchar_t wch = gsl::narrow_cast(id); + // 0x7f is DEL, which we treat effectively the same as a ctrl character. if (wch == 0x7f) { diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 14f6cedc188..824d6f8929f 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -145,8 +145,7 @@ namespace Microsoft::Console::VirtualTerminal bool ActionPassThroughString(const std::wstring_view string) override; - bool ActionEscDispatch(const wchar_t wch, - const gsl::span intermediates) override; + bool ActionEscDispatch(const VTID id) override; bool ActionVt52EscDispatch(const wchar_t wch, const gsl::span intermediates, diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index d0b6e2f4d5f..d29d36e4d36 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -179,83 +179,119 @@ bool OutputStateMachineEngine::ActionPassThroughString(const std::wstring_view s // a simple escape sequence. These sequences traditionally start with ESC // and a simple letter. No complicated parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence +// - id - Identifier of the escape sequence to dispatch. // Return Value: // - true iff we successfully dispatched the sequence. -bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch, - const gsl::span intermediates) +bool OutputStateMachineEngine::ActionEscDispatch(const VTID id) { bool success = false; - // no intermediates. - if (intermediates.empty()) + switch (id) { - switch (wch) + case EscActionCodes::DECSC_CursorSave: + success = _dispatch->CursorSaveState(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSC); + break; + case EscActionCodes::DECRC_CursorRestore: + success = _dispatch->CursorRestoreState(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRC); + break; + case EscActionCodes::DECKPAM_KeypadApplicationMode: + success = _dispatch->SetKeypadMode(true); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPAM); + break; + case EscActionCodes::DECKPNM_KeypadNumericMode: + success = _dispatch->SetKeypadMode(false); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPNM); + break; + case EscActionCodes::NEL_NextLine: + success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithReturn); + TermTelemetry::Instance().Log(TermTelemetry::Codes::NEL); + break; + case EscActionCodes::IND_Index: + success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithoutReturn); + TermTelemetry::Instance().Log(TermTelemetry::Codes::IND); + break; + case EscActionCodes::RI_ReverseLineFeed: + success = _dispatch->ReverseLineFeed(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::RI); + break; + case EscActionCodes::HTS_HorizontalTabSet: + success = _dispatch->HorizontalTabSet(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::HTS); + break; + case EscActionCodes::RIS_ResetToInitialState: + success = _dispatch->HardReset(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::RIS); + break; + case EscActionCodes::SS2_SingleShift: + success = _dispatch->SingleShift(2); + TermTelemetry::Instance().Log(TermTelemetry::Codes::SS2); + break; + case EscActionCodes::SS3_SingleShift: + success = _dispatch->SingleShift(3); + TermTelemetry::Instance().Log(TermTelemetry::Codes::SS3); + break; + case EscActionCodes::LS2_LockingShift: + success = _dispatch->LockingShift(2); + TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2); + break; + case EscActionCodes::LS3_LockingShift: + success = _dispatch->LockingShift(3); + TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3); + break; + case EscActionCodes::LS1R_LockingShift: + success = _dispatch->LockingShiftRight(1); + TermTelemetry::Instance().Log(TermTelemetry::Codes::LS1R); + break; + case EscActionCodes::LS2R_LockingShift: + success = _dispatch->LockingShiftRight(2); + TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2R); + break; + case EscActionCodes::LS3R_LockingShift: + success = _dispatch->LockingShiftRight(3); + TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3R); + break; + case EscActionCodes::DECALN_ScreenAlignmentPattern: + success = _dispatch->ScreenAlignmentPattern(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECALN); + break; + default: + const auto commandChar = id[0]; + const auto commandParameter = id.SubSequence(1); + switch (commandChar) { - case EscActionCodes::DECSC_CursorSave: - success = _dispatch->CursorSaveState(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSC); - break; - case EscActionCodes::DECRC_CursorRestore: - success = _dispatch->CursorRestoreState(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRC); - break; - case EscActionCodes::DECKPAM_KeypadApplicationMode: - success = _dispatch->SetKeypadMode(true); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPAM); - break; - case EscActionCodes::DECKPNM_KeypadNumericMode: - success = _dispatch->SetKeypadMode(false); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECKPNM); - break; - case EscActionCodes::NEL_NextLine: - success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithReturn); - TermTelemetry::Instance().Log(TermTelemetry::Codes::NEL); - break; - case EscActionCodes::IND_Index: - success = _dispatch->LineFeed(DispatchTypes::LineFeedType::WithoutReturn); - TermTelemetry::Instance().Log(TermTelemetry::Codes::IND); - break; - case EscActionCodes::RI_ReverseLineFeed: - success = _dispatch->ReverseLineFeed(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::RI); - break; - case EscActionCodes::HTS_HorizontalTabSet: - success = _dispatch->HorizontalTabSet(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::HTS); - break; - case EscActionCodes::RIS_ResetToInitialState: - success = _dispatch->HardReset(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::RIS); + case '%': + success = _dispatch->DesignateCodingSystem(commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DOCS); break; - case EscActionCodes::SS2_SingleShift: - success = _dispatch->SingleShift(2); - TermTelemetry::Instance().Log(TermTelemetry::Codes::SS2); + case '(': + success = _dispatch->Designate94Charset(0, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG0); break; - case EscActionCodes::SS3_SingleShift: - success = _dispatch->SingleShift(3); - TermTelemetry::Instance().Log(TermTelemetry::Codes::SS3); + case ')': + success = _dispatch->Designate94Charset(1, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG1); break; - case EscActionCodes::LS2_LockingShift: - success = _dispatch->LockingShift(2); - TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2); + case '*': + success = _dispatch->Designate94Charset(2, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG2); break; - case EscActionCodes::LS3_LockingShift: - success = _dispatch->LockingShift(3); - TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3); + case '+': + success = _dispatch->Designate94Charset(3, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG3); break; - case EscActionCodes::LS1R_LockingShift: - success = _dispatch->LockingShiftRight(1); - TermTelemetry::Instance().Log(TermTelemetry::Codes::LS1R); + case '-': + success = _dispatch->Designate96Charset(1, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG1); break; - case EscActionCodes::LS2R_LockingShift: - success = _dispatch->LockingShiftRight(2); - TermTelemetry::Instance().Log(TermTelemetry::Codes::LS2R); + case '.': + success = _dispatch->Designate96Charset(2, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG2); break; - case EscActionCodes::LS3R_LockingShift: - success = _dispatch->LockingShiftRight(3); - TermTelemetry::Instance().Log(TermTelemetry::Codes::LS3R); + case '/': + success = _dispatch->Designate96Charset(3, commandParameter); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG3); break; default: // If no functions to call, overall dispatch was a failure. @@ -263,36 +299,6 @@ bool OutputStateMachineEngine::ActionEscDispatch(const wchar_t wch, break; } } - else if (intermediates.size() == 1) - { - switch (til::at(intermediates, 0)) - { - case L'%': - success = _dispatch->DesignateCodingSystem(wch); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DOCS); - break; - case L'#': - switch (wch) - { - case EscActionCodes::DECALN_ScreenAlignmentPattern: - success = _dispatch->ScreenAlignmentPattern(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECALN); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } - break; - default: - success = _IntermediateScsDispatch(wch, intermediates); - break; - } - } - else if (intermediates.size() == 2) - { - success = _IntermediateScsDispatch(wch, intermediates); - } // If we were unable to process the string, and there's a TTY attached to us, // trigger the state machine to flush the string to the terminal. @@ -389,57 +395,6 @@ bool OutputStateMachineEngine::ActionVt52EscDispatch(const wchar_t wch, return success; } -// Routine Description: -// - Handles SCS charset designation actions that can have one or two possible intermediates. -// Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence -// Return Value: -// - True if handled successfully. False otherwise. -bool OutputStateMachineEngine::_IntermediateScsDispatch(const wchar_t wch, - const gsl::span intermediates) -{ - bool success = false; - - // If we have more than one intermediate, the second intermediate forms part of - // the charset identifier. Otherwise it's identified by just the final character. - const VTID charset = intermediates.size() > 1 ? (wch << 8) + til::at(intermediates, 1) : wch; - - switch (til::at(intermediates, 0)) - { - case L'(': - success = _dispatch->Designate94Charset(0, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG0); - break; - case L')': - success = _dispatch->Designate94Charset(1, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG1); - break; - case L'*': - success = _dispatch->Designate94Charset(2, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG2); - break; - case L'+': - success = _dispatch->Designate94Charset(3, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG3); - break; - case L'-': - success = _dispatch->Designate96Charset(1, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG1); - break; - case L'.': - success = _dispatch->Designate96Charset(2, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG2); - break; - case L'/': - success = _dispatch->Designate96Charset(3, charset); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DesignateG3); - break; - } - - return success; -} - // Routine Description: // - Triggers the CsiDispatch action to indicate that the listener should handle // a control sequence. These sequences perform various API-type commands diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index d9478325484..0a7e45dbfae 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -33,8 +33,7 @@ namespace Microsoft::Console::VirtualTerminal bool ActionPassThroughString(const std::wstring_view string) override; - bool ActionEscDispatch(const wchar_t wch, - const gsl::span intermediates) override; + bool ActionEscDispatch(const VTID id) override; bool ActionVt52EscDispatch(const wchar_t wch, const gsl::span intermediates, @@ -73,8 +72,6 @@ namespace Microsoft::Console::VirtualTerminal wchar_t _lastPrintedChar; std::vector _graphicsOptions; - bool _IntermediateScsDispatch(const wchar_t wch, - const gsl::span intermediates); bool _IntermediateQuestionMarkDispatch(const wchar_t wchAction, const gsl::span parameters); bool _IntermediateGreaterThanOrEqualDispatch(const wchar_t wch, @@ -84,25 +81,25 @@ namespace Microsoft::Console::VirtualTerminal bool _IntermediateSpaceDispatch(const wchar_t wchAction, const gsl::span parameters); - enum EscActionCodes : wchar_t + enum EscActionCodes : uint64_t { - DECSC_CursorSave = L'7', - DECRC_CursorRestore = L'8', - DECKPAM_KeypadApplicationMode = L'=', - DECKPNM_KeypadNumericMode = L'>', - IND_Index = L'D', - NEL_NextLine = L'E', - HTS_HorizontalTabSet = L'H', - RI_ReverseLineFeed = L'M', - SS2_SingleShift = L'N', - SS3_SingleShift = L'O', - RIS_ResetToInitialState = L'c', - LS2_LockingShift = L'n', - LS3_LockingShift = L'o', - LS1R_LockingShift = L'~', - LS2R_LockingShift = L'}', - LS3R_LockingShift = L'|', - DECALN_ScreenAlignmentPattern = L'8' // With # intermediate + DECSC_CursorSave = VTID("7"), + DECRC_CursorRestore = VTID("8"), + DECKPAM_KeypadApplicationMode = VTID("="), + DECKPNM_KeypadNumericMode = VTID(">"), + IND_Index = VTID("D"), + NEL_NextLine = VTID("E"), + HTS_HorizontalTabSet = VTID("H"), + RI_ReverseLineFeed = VTID("M"), + SS2_SingleShift = VTID("N"), + SS3_SingleShift = VTID("O"), + RIS_ResetToInitialState = VTID("c"), + LS2_LockingShift = VTID("n"), + LS3_LockingShift = VTID("o"), + LS1R_LockingShift = VTID("~"), + LS2R_LockingShift = VTID("}"), + LS3R_LockingShift = VTID("|"), + DECALN_ScreenAlignmentPattern = VTID("#8") }; enum CsiActionCodes : wchar_t diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index c49e52e3c78..72026c1a156 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -353,7 +353,12 @@ void StateMachine::_ActionEscDispatch(const wchar_t wch) { _trace.TraceOnAction(L"EscDispatch"); - const bool success = _engine->ActionEscDispatch(wch, { _intermediates.data(), _intermediates.size() }); + VTIDBuilder idBuilder; + for (wchar_t intermediate : _intermediates) + { + idBuilder.AddIntermediate(intermediate); + } + const bool success = _engine->ActionEscDispatch(idBuilder.Finalize(wch)); // Trace the result. _trace.DispatchSequenceTrace(success); diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index 74e29ea72af..3e332a4ddca 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -50,8 +50,7 @@ class Microsoft::Console::VirtualTerminal::TestStateMachineEngine : public IStat return true; }; - bool ActionEscDispatch(const wchar_t /* wch */, - const gsl::span /* intermediates */) override { return true; }; + bool ActionEscDispatch(const VTID /* id */) override { return true; }; bool ActionVt52EscDispatch(const wchar_t /*wch*/, const gsl::span /*intermediates*/, From f96249cebbadc23958164004a6a9600e18383ec4 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 21:10:02 +0100 Subject: [PATCH 07/11] Use a VTID instead of separate intermediate and final parameters in the VT52 dispatchers. --- src/terminal/parser/IStateMachineEngine.hpp | 4 +- .../parser/InputStateMachineEngine.cpp | 7 +- .../parser/InputStateMachineEngine.hpp | 4 +- .../parser/OutputStateMachineEngine.cpp | 121 +++++++++--------- .../parser/OutputStateMachineEngine.hpp | 36 +++--- src/terminal/parser/stateMachine.cpp | 8 +- .../parser/ut_parser/StateMachineTest.cpp | 4 +- 7 files changed, 85 insertions(+), 99 deletions(-) diff --git a/src/terminal/parser/IStateMachineEngine.hpp b/src/terminal/parser/IStateMachineEngine.hpp index 02a27bfe07e..f2466250a0e 100644 --- a/src/terminal/parser/IStateMachineEngine.hpp +++ b/src/terminal/parser/IStateMachineEngine.hpp @@ -34,9 +34,7 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ActionPassThroughString(const std::wstring_view string) = 0; virtual bool ActionEscDispatch(const VTID id) = 0; - virtual bool ActionVt52EscDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) = 0; + virtual bool ActionVt52EscDispatch(const VTID id, const gsl::span parameters) = 0; virtual bool ActionCsiDispatch(const wchar_t wch, const gsl::span intermediates, const gsl::span parameters) = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 146e2057d65..1b8245a4411 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -340,14 +340,11 @@ bool InputStateMachineEngine::ActionEscDispatch(const VTID id) // a VT52 escape sequence. These sequences start with ESC and a single letter, // sometimes followed by parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence. +// - id - Identifier of the VT52 sequence to dispatch. // - parameters - Set of parameters collected while parsing the sequence. // Return Value: // - true iff we successfully dispatched the sequence. -bool InputStateMachineEngine::ActionVt52EscDispatch(const wchar_t /*wch*/, - const gsl::span /*intermediates*/, - const gsl::span /*parameters*/) noexcept +bool InputStateMachineEngine::ActionVt52EscDispatch(const VTID /*id*/, const gsl::span /*parameters*/) noexcept { // VT52 escape sequences are not used in the input state machine. return false; diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index 824d6f8929f..db571a0fefd 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -147,9 +147,7 @@ namespace Microsoft::Console::VirtualTerminal bool ActionEscDispatch(const VTID id) override; - bool ActionVt52EscDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) noexcept override; + bool ActionVt52EscDispatch(const VTID id, const gsl::span parameters) noexcept override; bool ActionCsiDispatch(const wchar_t wch, const gsl::span intermediates, diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index d29d36e4d36..04bb00f5c3a 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -317,77 +317,70 @@ bool OutputStateMachineEngine::ActionEscDispatch(const VTID id) // a VT52 escape sequence. These sequences start with ESC and a single letter, // sometimes followed by parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence. +// - id - Identifier of the VT52 sequence to dispatch. // - parameters - Set of parameters collected while parsing the sequence. // Return Value: // - true iff we successfully dispatched the sequence. -bool OutputStateMachineEngine::ActionVt52EscDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) +bool OutputStateMachineEngine::ActionVt52EscDispatch(const VTID id, const gsl::span parameters) { bool success = false; - // no intermediates. - if (intermediates.empty()) + switch (id) { - switch (wch) - { - case Vt52ActionCodes::CursorUp: - success = _dispatch->CursorUp(1); - break; - case Vt52ActionCodes::CursorDown: - success = _dispatch->CursorDown(1); - break; - case Vt52ActionCodes::CursorRight: - success = _dispatch->CursorForward(1); - break; - case Vt52ActionCodes::CursorLeft: - success = _dispatch->CursorBackward(1); - break; - case Vt52ActionCodes::EnterGraphicsMode: - success = _dispatch->Designate94Charset(0, DispatchTypes::CharacterSets::DecSpecialGraphics); - break; - case Vt52ActionCodes::ExitGraphicsMode: - success = _dispatch->Designate94Charset(0, DispatchTypes::CharacterSets::ASCII); - break; - case Vt52ActionCodes::CursorToHome: - success = _dispatch->CursorPosition(1, 1); - break; - case Vt52ActionCodes::ReverseLineFeed: - success = _dispatch->ReverseLineFeed(); - break; - case Vt52ActionCodes::EraseToEndOfScreen: - success = _dispatch->EraseInDisplay(DispatchTypes::EraseType::ToEnd); - break; - case Vt52ActionCodes::EraseToEndOfLine: - success = _dispatch->EraseInLine(DispatchTypes::EraseType::ToEnd); - break; - case Vt52ActionCodes::DirectCursorAddress: - // VT52 cursor addresses are provided as ASCII characters, with - // the lowest value being a space, representing an address of 1. - success = _dispatch->CursorPosition(gsl::at(parameters, 0) - ' ' + 1, gsl::at(parameters, 1) - ' ' + 1); - break; - case Vt52ActionCodes::Identify: - success = _dispatch->Vt52DeviceAttributes(); - break; - case Vt52ActionCodes::EnterAlternateKeypadMode: - success = _dispatch->SetKeypadMode(true); - break; - case Vt52ActionCodes::ExitAlternateKeypadMode: - success = _dispatch->SetKeypadMode(false); - break; - case Vt52ActionCodes::ExitVt52Mode: - { - const DispatchTypes::PrivateModeParams mode[] = { DispatchTypes::PrivateModeParams::DECANM_AnsiMode }; - success = _dispatch->SetPrivateModes(mode); - break; - } - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } + case Vt52ActionCodes::CursorUp: + success = _dispatch->CursorUp(1); + break; + case Vt52ActionCodes::CursorDown: + success = _dispatch->CursorDown(1); + break; + case Vt52ActionCodes::CursorRight: + success = _dispatch->CursorForward(1); + break; + case Vt52ActionCodes::CursorLeft: + success = _dispatch->CursorBackward(1); + break; + case Vt52ActionCodes::EnterGraphicsMode: + success = _dispatch->Designate94Charset(0, DispatchTypes::CharacterSets::DecSpecialGraphics); + break; + case Vt52ActionCodes::ExitGraphicsMode: + success = _dispatch->Designate94Charset(0, DispatchTypes::CharacterSets::ASCII); + break; + case Vt52ActionCodes::CursorToHome: + success = _dispatch->CursorPosition(1, 1); + break; + case Vt52ActionCodes::ReverseLineFeed: + success = _dispatch->ReverseLineFeed(); + break; + case Vt52ActionCodes::EraseToEndOfScreen: + success = _dispatch->EraseInDisplay(DispatchTypes::EraseType::ToEnd); + break; + case Vt52ActionCodes::EraseToEndOfLine: + success = _dispatch->EraseInLine(DispatchTypes::EraseType::ToEnd); + break; + case Vt52ActionCodes::DirectCursorAddress: + // VT52 cursor addresses are provided as ASCII characters, with + // the lowest value being a space, representing an address of 1. + success = _dispatch->CursorPosition(gsl::at(parameters, 0) - ' ' + 1, gsl::at(parameters, 1) - ' ' + 1); + break; + case Vt52ActionCodes::Identify: + success = _dispatch->Vt52DeviceAttributes(); + break; + case Vt52ActionCodes::EnterAlternateKeypadMode: + success = _dispatch->SetKeypadMode(true); + break; + case Vt52ActionCodes::ExitAlternateKeypadMode: + success = _dispatch->SetKeypadMode(false); + break; + case Vt52ActionCodes::ExitVt52Mode: + { + const DispatchTypes::PrivateModeParams mode[] = { DispatchTypes::PrivateModeParams::DECANM_AnsiMode }; + success = _dispatch->SetPrivateModes(mode); + break; + } + default: + // If no functions to call, overall dispatch was a failure. + success = false; + break; } _ClearLastChar(); diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index 0a7e45dbfae..f525a9030a0 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -35,9 +35,7 @@ namespace Microsoft::Console::VirtualTerminal bool ActionEscDispatch(const VTID id) override; - bool ActionVt52EscDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) override; + bool ActionVt52EscDispatch(const VTID id, const gsl::span parameters) override; bool ActionCsiDispatch(const wchar_t wch, const gsl::span intermediates, @@ -144,23 +142,23 @@ namespace Microsoft::Console::VirtualTerminal DECSCPP_SetColumnsPerPage = L'|' // With $ intermediate }; - enum Vt52ActionCodes : wchar_t + enum Vt52ActionCodes : uint64_t { - CursorUp = L'A', - CursorDown = L'B', - CursorRight = L'C', - CursorLeft = L'D', - EnterGraphicsMode = L'F', - ExitGraphicsMode = L'G', - CursorToHome = L'H', - ReverseLineFeed = L'I', - EraseToEndOfScreen = L'J', - EraseToEndOfLine = L'K', - DirectCursorAddress = L'Y', - Identify = L'Z', - EnterAlternateKeypadMode = L'=', - ExitAlternateKeypadMode = L'>', - ExitVt52Mode = L'<' + 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 diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 72026c1a156..c5214979a89 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -381,8 +381,12 @@ void StateMachine::_ActionVt52EscDispatch(const wchar_t wch) { _trace.TraceOnAction(L"Vt52EscDispatch"); - const bool success = _engine->ActionVt52EscDispatch(wch, - { _intermediates.data(), _intermediates.size() }, + VTIDBuilder idBuilder; + for (wchar_t intermediate : _intermediates) + { + idBuilder.AddIntermediate(intermediate); + } + const bool success = _engine->ActionVt52EscDispatch(idBuilder.Finalize(wch), { _parameters.data(), _parameters.size() }); // Trace the result. diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index 3e332a4ddca..b80af3d17c9 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -52,9 +52,7 @@ class Microsoft::Console::VirtualTerminal::TestStateMachineEngine : public IStat bool ActionEscDispatch(const VTID /* id */) override { return true; }; - bool ActionVt52EscDispatch(const wchar_t /*wch*/, - const gsl::span /*intermediates*/, - const gsl::span /*parameters*/) override { return true; }; + bool ActionVt52EscDispatch(const VTID /*id*/, const gsl::span /*parameters*/) override { return true; }; bool ActionClear() override { return true; }; From a2962527fce03b4ed36c0723efc2d4c8be7a79b6 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 11 Aug 2020 23:56:25 +0100 Subject: [PATCH 08/11] Use a VTID instead of separate intermediate and final parameters in the CSI dispatchers. --- src/terminal/parser/IStateMachineEngine.hpp | 4 +- .../parser/InputStateMachineEngine.cpp | 74 +-- .../parser/InputStateMachineEngine.hpp | 51 +- .../parser/OutputStateMachineEngine.cpp | 550 +++++++----------- .../parser/OutputStateMachineEngine.hpp | 93 ++- src/terminal/parser/stateMachine.cpp | 8 +- .../parser/ut_parser/InputEngineTest.cpp | 10 +- .../parser/ut_parser/StateMachineTest.cpp | 4 +- 8 files changed, 311 insertions(+), 483 deletions(-) diff --git a/src/terminal/parser/IStateMachineEngine.hpp b/src/terminal/parser/IStateMachineEngine.hpp index f2466250a0e..169cf8ac021 100644 --- a/src/terminal/parser/IStateMachineEngine.hpp +++ b/src/terminal/parser/IStateMachineEngine.hpp @@ -35,9 +35,7 @@ namespace Microsoft::Console::VirtualTerminal virtual bool ActionEscDispatch(const VTID id) = 0; virtual bool ActionVt52EscDispatch(const VTID id, const gsl::span parameters) = 0; - virtual bool ActionCsiDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) = 0; + virtual bool ActionCsiDispatch(const VTID id, const gsl::span parameters) = 0; virtual bool ActionClear() = 0; diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index 1b8245a4411..e167a44f9b0 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -34,9 +34,9 @@ static constexpr std::array s_csiMap = { 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 @@ -355,17 +355,12 @@ bool InputStateMachineEngine::ActionVt52EscDispatch(const VTID /*id*/, const gsl // a control sequence. These sequences perform various API-type commands // that can include many parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence +// - id - Identifier of the control sequence to dispatch. // - parameters - set of numeric parameters collected while parsing the sequence. // Return Value: // - true iff we successfully dispatched the sequence. -bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) +bool InputStateMachineEngine::ActionCsiDispatch(const VTID id, const gsl::span parameters) { - const auto actionCode = static_cast(wch); - // 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 // client. It's impossibly unlikely that the client actually wanted @@ -374,7 +369,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, // client reads it. if (_pDispatch->IsVtInputEnabled() && _pfnFlushToInputQueue && - actionCode != CsiActionCodes::Win32KeyboardInput) + id != CsiActionCodes::Win32KeyboardInput) { return _pfnFlushToInputQueue(); } @@ -390,31 +385,22 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, const auto remainingArgs = parameters.size() > 1 ? parameters.subspan(1) : gsl::span{}; bool success = false; - // Handle intermediate characters, if any - if (!intermediates.empty()) + switch (id) { - switch (static_cast(til::at(intermediates, 0))) - { - case CsiIntermediateCodes::MOUSE_SGR: - { - DWORD buttonState = 0; - DWORD eventFlags = 0; - modifierState = _GetSGRMouseModifierState(parameters); - success = _GetSGRXYPosition(parameters, row, col); - - // 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. - success = _UpdateSGRMouseButtonState(wch, parameters, buttonState, eventFlags) && success; - success = success && _WriteMouseEvent(col, row, buttonState, modifierState, eventFlags); - } - default: - success = false; - break; - } + case CsiActionCodes::MouseDown: + case CsiActionCodes::MouseUp: + { + DWORD buttonState = 0; + DWORD eventFlags = 0; + modifierState = _GetSGRMouseModifierState(parameters); + success = _GetSGRXYPosition(parameters, row, col); + + // 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. + success = _UpdateSGRMouseButtonState(id, parameters, buttonState, eventFlags) && success; + success = success && _WriteMouseEvent(col, row, buttonState, modifierState, eventFlags); return success; } - switch (actionCode) - { case CsiActionCodes::Generic: modifierState = _GetGenericKeysModifierState(parameters); success = _GetGenericVkey(parameters, vkey); @@ -439,8 +425,8 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, case CsiActionCodes::CSI_F1: case CsiActionCodes::CSI_F2: case CsiActionCodes::CSI_F4: - success = _GetCursorKeysVkey(wch, vkey); - modifierState = _GetCursorKeysModifierState(parameters, static_cast(wch)); + success = _GetCursorKeysVkey(id, vkey); + modifierState = _GetCursorKeysModifierState(parameters, id); break; case CsiActionCodes::CursorBackTab: modifierState = SHIFT_PRESSED; @@ -460,7 +446,7 @@ bool InputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, if (success) { - switch (static_cast(wch)) + switch (id) { // case CsiActionCodes::DSR_DeviceStatusReportResponse: case CsiActionCodes::CSI_F3: @@ -795,10 +781,10 @@ bool InputStateMachineEngine::_WriteMouseEvent(const size_t column, const size_t // sequence. This is for Arrow keys, Home, End, etc. // Arguments: // - 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: // - the INPUT_RECORD compatible modifier state. -DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span parameters, const CsiActionCodes actionCode) noexcept +DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span parameters, const VTID id) noexcept { DWORD modifiers = 0; if (_IsModified(parameters.size()) && parameters.size() >= 2) @@ -812,7 +798,7 @@ DWORD InputStateMachineEngine::_GetCursorKeysModifierState(const gsl::span CsiActionCodes::CSI_F4) + if (id < CsiActionCodes::CSI_F1 || id > CsiActionCodes::CSI_F4) { WI_SetFlag(modifiers, ENHANCED_KEY); } @@ -913,13 +899,13 @@ DWORD InputStateMachineEngine::_GetModifier(const size_t modifierParam) noexcept // - 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 // 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) // - buttonState: Receives the button state for the record // - eventFlags: Receives the special mouse events for the record // Return Value: // true iff we were able to synthesize buttonState -bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const wchar_t wch, +bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const VTID id, const gsl::span parameters, DWORD& buttonState, DWORD& eventFlags) noexcept @@ -983,7 +969,7 @@ bool InputStateMachineEngine::_UpdateSGRMouseButtonState(const wchar_t wch, // 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 - switch (static_cast(wch)) + switch (id) { case CsiActionCodes::MouseDown: // set flag @@ -1045,15 +1031,15 @@ bool InputStateMachineEngine::_GetGenericVkey(const gsl::span para // Method Description: // - Gets the Vkey from the CSI codes table associated with a particular character. // 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 // Return Value: // 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; - 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()) { vkey = mapping->vkey; diff --git a/src/terminal/parser/InputStateMachineEngine.hpp b/src/terminal/parser/InputStateMachineEngine.hpp index db571a0fefd..a14aa87f633 100644 --- a/src/terminal/parser/InputStateMachineEngine.hpp +++ b/src/terminal/parser/InputStateMachineEngine.hpp @@ -50,30 +50,25 @@ namespace Microsoft::Console::VirtualTerminal // CAPSLOCK_ON 0x0080 // ENHANCED_KEY 0x0100 - enum CsiIntermediateCodes : wchar_t + enum CsiActionCodes : uint64_t { - MOUSE_SGR = L'<', - }; - - enum class CsiActionCodes : wchar_t - { - ArrowUp = L'A', - ArrowDown = L'B', - ArrowRight = L'C', - ArrowLeft = L'D', - Home = L'H', - End = L'F', - MouseDown = L'M', - MouseUp = L'm', - Generic = L'~', // Used for a whole bunch of possible keys - CSI_F1 = L'P', - CSI_F2 = L'Q', - CSI_F3 = L'R', // Both F3 and DSR are on R. - // DSR_DeviceStatusReportResponse = L'R', - CSI_F4 = L'S', - DTTERM_WindowManipulation = L't', - CursorBackTab = L'Z', - Win32KeyboardInput = L'_' + ArrowUp = VTID("A"), + ArrowDown = VTID("B"), + ArrowRight = VTID("C"), + ArrowLeft = VTID("D"), + Home = VTID("H"), + End = VTID("F"), + MouseDown = VTID(" parameters) noexcept override; - bool ActionCsiDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) override; + bool ActionCsiDispatch(const VTID id, const gsl::span parameters) override; bool ActionClear() noexcept override; @@ -177,7 +170,7 @@ namespace Microsoft::Console::VirtualTerminal bool _lookingForDSR; DWORD _mouseButtonState = 0; - DWORD _GetCursorKeysModifierState(const gsl::span parameters, const CsiActionCodes actionCode) noexcept; + DWORD _GetCursorKeysModifierState(const gsl::span parameters, const VTID id) noexcept; DWORD _GetGenericKeysModifierState(const gsl::span parameters) noexcept; DWORD _GetSGRMouseModifierState(const gsl::span parameters) noexcept; bool _GenerateKeyFromChar(const wchar_t wch, short& vkey, DWORD& modifierState) noexcept; @@ -185,13 +178,13 @@ namespace Microsoft::Console::VirtualTerminal bool _IsModified(const size_t paramCount) noexcept; DWORD _GetModifier(const size_t parameter) noexcept; - bool _UpdateSGRMouseButtonState(const wchar_t wch, + bool _UpdateSGRMouseButtonState(const VTID id, const gsl::span parameters, DWORD& buttonState, DWORD& eventFlags) noexcept; bool _GetGenericVkey(const gsl::span parameters, 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 _WriteSingleKey(const short vkey, const DWORD modifierState); diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index 04bb00f5c3a..0be249a6fed 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -393,14 +393,11 @@ bool OutputStateMachineEngine::ActionVt52EscDispatch(const VTID id, const gsl::s // a control sequence. These sequences perform various API-type commands // that can include many parameters. // Arguments: -// - wch - Character to dispatch. -// - intermediates - Intermediate characters in the sequence +// - id - Identifier of the control sequence to dispatch. // - parameters - set of numeric parameters collected while parsing the sequence. // Return Value: // - true iff we successfully dispatched the sequence. -bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, - const gsl::span intermediates, - gsl::span parameters) +bool OutputStateMachineEngine::ActionCsiDispatch(const VTID id, gsl::span parameters) { bool success = false; size_t distance = 0; @@ -412,252 +409,261 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, size_t clearType = 0; unsigned int function = 0; DispatchTypes::EraseType eraseType = DispatchTypes::EraseType::ToEnd; + std::vector privateModeParams; // We hold the vector in the class because client applications that do a lot of color work // would spend a lot of time reallocating/resizing the vector. _graphicsOptions.clear(); DispatchTypes::AnsiStatusType deviceStatusType = static_cast(0); // there is no default status type. size_t repeatCount = 0; + DispatchTypes::CursorStyle cursorStyle = DefaultCursorStyle; // This is all the args after the first arg, and the count of args not including the first one. const auto remainingParams = parameters.size() > 1 ? parameters.subspan(1) : gsl::span{}; - if (intermediates.empty()) + // fill params + switch (id) + { + case CsiActionCodes::CUU_CursorUp: + case CsiActionCodes::CUD_CursorDown: + case CsiActionCodes::CUF_CursorForward: + case CsiActionCodes::CUB_CursorBackward: + case CsiActionCodes::CNL_CursorNextLine: + case CsiActionCodes::CPL_CursorPrevLine: + case CsiActionCodes::CHA_CursorHorizontalAbsolute: + case CsiActionCodes::HPA_HorizontalPositionAbsolute: + case CsiActionCodes::VPA_VerticalLinePositionAbsolute: + case CsiActionCodes::HPR_HorizontalPositionRelative: + case CsiActionCodes::VPR_VerticalPositionRelative: + case CsiActionCodes::ICH_InsertCharacter: + case CsiActionCodes::DCH_DeleteCharacter: + case CsiActionCodes::ECH_EraseCharacters: + success = _GetCursorDistance(parameters, distance); + break; + case CsiActionCodes::HVP_HorizontalVerticalPosition: + case CsiActionCodes::CUP_CursorPosition: + success = _GetXYPosition(parameters, line, column); + break; + case CsiActionCodes::DECSTBM_SetScrollingRegion: + success = _GetTopBottomMargins(parameters, topMargin, bottomMargin); + break; + case CsiActionCodes::ED_EraseDisplay: + case CsiActionCodes::EL_EraseLine: + success = _GetEraseOperation(parameters, eraseType); + break; + case CsiActionCodes::DECSET_PrivateModeSet: + case CsiActionCodes::DECRST_PrivateModeReset: + success = _GetPrivateModeParams(parameters, privateModeParams); + break; + case CsiActionCodes::SGR_SetGraphicsRendition: + success = _GetGraphicsOptions(parameters, _graphicsOptions); + break; + case CsiActionCodes::DSR_DeviceStatusReport: + success = _GetDeviceStatusOperation(parameters, deviceStatusType); + break; + case CsiActionCodes::DA_DeviceAttributes: + case CsiActionCodes::DA2_SecondaryDeviceAttributes: + case CsiActionCodes::DA3_TertiaryDeviceAttributes: + success = _VerifyDeviceAttributesParams(parameters); + break; + case CsiActionCodes::SU_ScrollUp: + case CsiActionCodes::SD_ScrollDown: + success = _GetScrollDistance(parameters, distance); + break; + case CsiActionCodes::ANSISYSSC_CursorSave: + case CsiActionCodes::ANSISYSRC_CursorRestore: + success = _VerifyHasNoParameters(parameters); + break; + case CsiActionCodes::IL_InsertLine: + case CsiActionCodes::DL_DeleteLine: + success = _GetScrollDistance(parameters, distance); + break; + case CsiActionCodes::CHT_CursorForwardTab: + case CsiActionCodes::CBT_CursorBackTab: + success = _GetTabDistance(parameters, numTabs); + break; + case CsiActionCodes::TBC_TabClear: + success = _GetTabClearType(parameters, clearType); + break; + case CsiActionCodes::DTTERM_WindowManipulation: + success = _GetWindowManipulationType(parameters, function); + break; + case CsiActionCodes::REP_RepeatCharacter: + success = _GetRepeatCount(parameters, repeatCount); + break; + case CsiActionCodes::DECSCUSR_SetCursorStyle: + success = _GetCursorStyle(parameters, cursorStyle); + break; + default: + // If no params to fill, param filling was successful. + success = true; + break; + } + + // if param filling successful, try to dispatch + if (success) { - // fill params - switch (wch) + switch (id) { case CsiActionCodes::CUU_CursorUp: + success = _dispatch->CursorUp(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CUU); + break; case CsiActionCodes::CUD_CursorDown: + success = _dispatch->CursorDown(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CUD); + break; case CsiActionCodes::CUF_CursorForward: + success = _dispatch->CursorForward(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CUF); + break; case CsiActionCodes::CUB_CursorBackward: + success = _dispatch->CursorBackward(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CUB); + break; case CsiActionCodes::CNL_CursorNextLine: + success = _dispatch->CursorNextLine(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CNL); + break; case CsiActionCodes::CPL_CursorPrevLine: + success = _dispatch->CursorPrevLine(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CPL); + break; case CsiActionCodes::CHA_CursorHorizontalAbsolute: case CsiActionCodes::HPA_HorizontalPositionAbsolute: + success = _dispatch->CursorHorizontalPositionAbsolute(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CHA); + break; case CsiActionCodes::VPA_VerticalLinePositionAbsolute: + success = _dispatch->VerticalLinePositionAbsolute(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::VPA); + break; case CsiActionCodes::HPR_HorizontalPositionRelative: + success = _dispatch->HorizontalPositionRelative(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::HPR); + break; case CsiActionCodes::VPR_VerticalPositionRelative: - case CsiActionCodes::ICH_InsertCharacter: - case CsiActionCodes::DCH_DeleteCharacter: - case CsiActionCodes::ECH_EraseCharacters: - success = _GetCursorDistance(parameters, distance); + success = _dispatch->VerticalPositionRelative(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::VPR); break; - case CsiActionCodes::HVP_HorizontalVerticalPosition: case CsiActionCodes::CUP_CursorPosition: - success = _GetXYPosition(parameters, line, column); + case CsiActionCodes::HVP_HorizontalVerticalPosition: + success = _dispatch->CursorPosition(line, column); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CUP); break; case CsiActionCodes::DECSTBM_SetScrollingRegion: - success = _GetTopBottomMargins(parameters, topMargin, bottomMargin); + success = _dispatch->SetTopBottomScrollingMargins(topMargin, bottomMargin); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTBM); + break; + case CsiActionCodes::ICH_InsertCharacter: + success = _dispatch->InsertCharacter(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::ICH); + break; + case CsiActionCodes::DCH_DeleteCharacter: + success = _dispatch->DeleteCharacter(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DCH); break; case CsiActionCodes::ED_EraseDisplay: + success = _dispatch->EraseInDisplay(eraseType); + TermTelemetry::Instance().Log(TermTelemetry::Codes::ED); + break; case CsiActionCodes::EL_EraseLine: - success = _GetEraseOperation(parameters, eraseType); + success = _dispatch->EraseInLine(eraseType); + TermTelemetry::Instance().Log(TermTelemetry::Codes::EL); + break; + case CsiActionCodes::DECSET_PrivateModeSet: + success = _dispatch->SetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); + //TODO: MSFT:6367459 Add specific logging for each of the DECSET/DECRST codes + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSET); + break; + case CsiActionCodes::DECRST_PrivateModeReset: + success = _dispatch->ResetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRST); break; case CsiActionCodes::SGR_SetGraphicsRendition: - success = _GetGraphicsOptions(parameters, _graphicsOptions); + success = _dispatch->SetGraphicsRendition({ _graphicsOptions.data(), _graphicsOptions.size() }); + TermTelemetry::Instance().Log(TermTelemetry::Codes::SGR); break; case CsiActionCodes::DSR_DeviceStatusReport: - success = _GetDeviceStatusOperation(parameters, deviceStatusType); + success = _dispatch->DeviceStatusReport(deviceStatusType); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DSR); break; case CsiActionCodes::DA_DeviceAttributes: - success = _VerifyDeviceAttributesParams(parameters); + success = _dispatch->DeviceAttributes(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DA); + break; + case CsiActionCodes::DA2_SecondaryDeviceAttributes: + success = _dispatch->SecondaryDeviceAttributes(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DA2); + break; + case CsiActionCodes::DA3_TertiaryDeviceAttributes: + success = _dispatch->TertiaryDeviceAttributes(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DA3); break; case CsiActionCodes::SU_ScrollUp: + success = _dispatch->ScrollUp(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::SU); + break; case CsiActionCodes::SD_ScrollDown: - success = _GetScrollDistance(parameters, distance); + success = _dispatch->ScrollDown(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::SD); break; case CsiActionCodes::ANSISYSSC_CursorSave: + success = _dispatch->CursorSaveState(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSSC); + break; case CsiActionCodes::ANSISYSRC_CursorRestore: - success = _VerifyHasNoParameters(parameters); + success = _dispatch->CursorRestoreState(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSRC); break; case CsiActionCodes::IL_InsertLine: + success = _dispatch->InsertLine(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::IL); + break; case CsiActionCodes::DL_DeleteLine: - success = _GetScrollDistance(parameters, distance); + success = _dispatch->DeleteLine(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DL); break; case CsiActionCodes::CHT_CursorForwardTab: + success = _dispatch->ForwardTab(numTabs); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CHT); + break; case CsiActionCodes::CBT_CursorBackTab: - success = _GetTabDistance(parameters, numTabs); + success = _dispatch->BackwardsTab(numTabs); + TermTelemetry::Instance().Log(TermTelemetry::Codes::CBT); break; case CsiActionCodes::TBC_TabClear: - success = _GetTabClearType(parameters, clearType); + success = _dispatch->TabClear(clearType); + TermTelemetry::Instance().Log(TermTelemetry::Codes::TBC); + break; + case CsiActionCodes::ECH_EraseCharacters: + success = _dispatch->EraseCharacters(distance); + TermTelemetry::Instance().Log(TermTelemetry::Codes::ECH); break; case CsiActionCodes::DTTERM_WindowManipulation: - success = _GetWindowManipulationType(parameters, function); + success = _dispatch->WindowManipulation(static_cast(function), + remainingParams); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DTTERM_WM); break; case CsiActionCodes::REP_RepeatCharacter: - success = _GetRepeatCount(parameters, repeatCount); - break; - default: - // If no params to fill, param filling was successful. - success = true; - break; - } - - // if param filling successful, try to dispatch - if (success) - { - switch (wch) + // Handled w/o the dispatch. This function is unique in that way + // If this were in the ITerminalDispatch, then each + // implementation would effectively be the same, calling only + // functions that are already part of the interface. + // Print the last graphical character a number of times. + if (_lastPrintedChar != AsciiChars::NUL) { - case CsiActionCodes::CUU_CursorUp: - success = _dispatch->CursorUp(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CUU); - break; - case CsiActionCodes::CUD_CursorDown: - success = _dispatch->CursorDown(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CUD); - break; - case CsiActionCodes::CUF_CursorForward: - success = _dispatch->CursorForward(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CUF); - break; - case CsiActionCodes::CUB_CursorBackward: - success = _dispatch->CursorBackward(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CUB); - break; - case CsiActionCodes::CNL_CursorNextLine: - success = _dispatch->CursorNextLine(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CNL); - break; - case CsiActionCodes::CPL_CursorPrevLine: - success = _dispatch->CursorPrevLine(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CPL); - break; - case CsiActionCodes::CHA_CursorHorizontalAbsolute: - case CsiActionCodes::HPA_HorizontalPositionAbsolute: - success = _dispatch->CursorHorizontalPositionAbsolute(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CHA); - break; - case CsiActionCodes::VPA_VerticalLinePositionAbsolute: - success = _dispatch->VerticalLinePositionAbsolute(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::VPA); - break; - case CsiActionCodes::HPR_HorizontalPositionRelative: - success = _dispatch->HorizontalPositionRelative(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::HPR); - break; - case CsiActionCodes::VPR_VerticalPositionRelative: - success = _dispatch->VerticalPositionRelative(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::VPR); - break; - case CsiActionCodes::CUP_CursorPosition: - case CsiActionCodes::HVP_HorizontalVerticalPosition: - success = _dispatch->CursorPosition(line, column); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CUP); - break; - case CsiActionCodes::DECSTBM_SetScrollingRegion: - success = _dispatch->SetTopBottomScrollingMargins(topMargin, bottomMargin); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTBM); - break; - case CsiActionCodes::ICH_InsertCharacter: - success = _dispatch->InsertCharacter(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::ICH); - break; - case CsiActionCodes::DCH_DeleteCharacter: - success = _dispatch->DeleteCharacter(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DCH); - break; - case CsiActionCodes::ED_EraseDisplay: - success = _dispatch->EraseInDisplay(eraseType); - TermTelemetry::Instance().Log(TermTelemetry::Codes::ED); - break; - case CsiActionCodes::EL_EraseLine: - success = _dispatch->EraseInLine(eraseType); - TermTelemetry::Instance().Log(TermTelemetry::Codes::EL); - break; - case CsiActionCodes::SGR_SetGraphicsRendition: - success = _dispatch->SetGraphicsRendition({ _graphicsOptions.data(), _graphicsOptions.size() }); - TermTelemetry::Instance().Log(TermTelemetry::Codes::SGR); - break; - case CsiActionCodes::DSR_DeviceStatusReport: - success = _dispatch->DeviceStatusReport(deviceStatusType); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DSR); - break; - case CsiActionCodes::DA_DeviceAttributes: - success = _dispatch->DeviceAttributes(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DA); - break; - case CsiActionCodes::SU_ScrollUp: - success = _dispatch->ScrollUp(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::SU); - break; - case CsiActionCodes::SD_ScrollDown: - success = _dispatch->ScrollDown(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::SD); - break; - case CsiActionCodes::ANSISYSSC_CursorSave: - success = _dispatch->CursorSaveState(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSSC); - break; - case CsiActionCodes::ANSISYSRC_CursorRestore: - success = _dispatch->CursorRestoreState(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::ANSISYSRC); - break; - case CsiActionCodes::IL_InsertLine: - success = _dispatch->InsertLine(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::IL); - break; - case CsiActionCodes::DL_DeleteLine: - success = _dispatch->DeleteLine(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DL); - break; - case CsiActionCodes::CHT_CursorForwardTab: - success = _dispatch->ForwardTab(numTabs); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CHT); - break; - case CsiActionCodes::CBT_CursorBackTab: - success = _dispatch->BackwardsTab(numTabs); - TermTelemetry::Instance().Log(TermTelemetry::Codes::CBT); - break; - case CsiActionCodes::TBC_TabClear: - success = _dispatch->TabClear(clearType); - TermTelemetry::Instance().Log(TermTelemetry::Codes::TBC); - break; - case CsiActionCodes::ECH_EraseCharacters: - success = _dispatch->EraseCharacters(distance); - TermTelemetry::Instance().Log(TermTelemetry::Codes::ECH); - break; - case CsiActionCodes::DTTERM_WindowManipulation: - success = _dispatch->WindowManipulation(static_cast(function), - remainingParams); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DTTERM_WM); - break; - case CsiActionCodes::REP_RepeatCharacter: - // Handled w/o the dispatch. This function is unique in that way - // If this were in the ITerminalDispatch, then each - // implementation would effectively be the same, calling only - // functions that are already part of the interface. - // Print the last graphical character a number of times. - if (_lastPrintedChar != AsciiChars::NUL) - { - std::wstring wstr(repeatCount, _lastPrintedChar); - _dispatch->PrintString(wstr); - } - success = true; - TermTelemetry::Instance().Log(TermTelemetry::Codes::REP); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; + std::wstring wstr(repeatCount, _lastPrintedChar); + _dispatch->PrintString(wstr); } - } - } - else if (intermediates.size() == 1) - { - const auto value = til::at(intermediates, 0); - switch (value) - { - case L'?': - success = _IntermediateQuestionMarkDispatch(wch, parameters); - break; - case L'>': - case L'=': - success = _IntermediateGreaterThanOrEqualDispatch(wch, value, parameters); + success = true; + TermTelemetry::Instance().Log(TermTelemetry::Codes::REP); break; - case L'!': - success = _IntermediateExclamationDispatch(wch); + case CsiActionCodes::DECSCUSR_SetCursorStyle: + success = _dispatch->SetCursorStyle(cursorStyle); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSCUSR); break; - case L' ': - success = _IntermediateSpaceDispatch(wch, parameters); + case CsiActionCodes::DECSTR_SoftReset: + success = _dispatch->SoftReset(); + TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTR); break; default: // If no functions to call, overall dispatch was a failure. @@ -665,6 +671,7 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, break; } } + // If we were unable to process the string, and there's a TTY attached to us, // trigger the state machine to flush the string to the terminal. if (_pfnFlushToTerminal != nullptr && !success) @@ -677,159 +684,6 @@ bool OutputStateMachineEngine::ActionCsiDispatch(const wchar_t wch, return success; } -// Routine Description: -// - Handles actions that have postfix params on an intermediate '?', such as DECTCEM, DECCOLM, ATT610 -// Arguments: -// - wch - Character to dispatch. -// - parameters - set of numeric parameters collected while parsing the sequence. -// Return Value: -// - True if handled successfully. False otherwise. -bool OutputStateMachineEngine::_IntermediateQuestionMarkDispatch(const wchar_t wchAction, - const gsl::span parameters) -{ - bool success = false; - - std::vector privateModeParams; - // Ensure that there was the right number of params - switch (wchAction) - { - case CsiActionCodes::DECSET_PrivateModeSet: - case CsiActionCodes::DECRST_PrivateModeReset: - success = _GetPrivateModeParams(parameters, privateModeParams); - break; - - default: - // If no params to fill, param filling was successful. - success = true; - break; - } - if (success) - { - switch (wchAction) - { - case CsiActionCodes::DECSET_PrivateModeSet: - success = _dispatch->SetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); - //TODO: MSFT:6367459 Add specific logging for each of the DECSET/DECRST codes - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSET); - break; - case CsiActionCodes::DECRST_PrivateModeReset: - success = _dispatch->ResetPrivateModes({ privateModeParams.data(), privateModeParams.size() }); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECRST); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } - } - return success; -} - -// Routine Description: -// - Handles actions that have postfix params on an intermediate '>' or '='. -// Arguments: -// - wch - Character to dispatch. -// - intermediate - The intermediate character. -// - parameters - Set of numeric parameters collected while parsing the sequence. -// Return Value: -// - True if handled successfully. False otherwise. -bool OutputStateMachineEngine::_IntermediateGreaterThanOrEqualDispatch(const wchar_t wch, - const wchar_t intermediate, - const gsl::span parameters) -{ - bool success = false; - - switch (wch) - { - case CsiActionCodes::DA_DeviceAttributes: - if (_VerifyDeviceAttributesParams(parameters)) - { - switch (intermediate) - { - case L'>': - success = _dispatch->SecondaryDeviceAttributes(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DA2); - break; - case L'=': - success = _dispatch->TertiaryDeviceAttributes(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DA3); - break; - } - } - break; - } - - return success; -} - -// Routine Description: -// - Handles actions that have an intermediate '!', such as DECSTR -// Arguments: -// - wch - Character to dispatch. -// Return Value: -// - True if handled successfully. False otherwise. -bool OutputStateMachineEngine::_IntermediateExclamationDispatch(const wchar_t wchAction) -{ - bool success = false; - - switch (wchAction) - { - case CsiActionCodes::DECSTR_SoftReset: - success = _dispatch->SoftReset(); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSTR); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } - return success; -} - -// Routine Description: -// - Handles actions that have an intermediate ' ' (0x20), such as DECSCUSR -// Arguments: -// - wch - Character to dispatch. -// - parameters - set of numeric parameters collected while parsing the sequence. -// Return Value: -// - True if handled successfully. False otherwise. -bool OutputStateMachineEngine::_IntermediateSpaceDispatch(const wchar_t wchAction, - const gsl::span parameters) -{ - bool success = false; - DispatchTypes::CursorStyle cursorStyle = DefaultCursorStyle; - - // Parse params - switch (wchAction) - { - case CsiActionCodes::DECSCUSR_SetCursorStyle: - success = _GetCursorStyle(parameters, cursorStyle); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } - - // if param filling successful, try to dispatch - if (success) - { - switch (wchAction) - { - case CsiActionCodes::DECSCUSR_SetCursorStyle: - success = _dispatch->SetCursorStyle(cursorStyle); - TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSCUSR); - break; - default: - // If no functions to call, overall dispatch was a failure. - success = false; - break; - } - } - - return success; -} - // Routine Description: // - Triggers the Clear action to indicate that the state machine should erase // all internal state. diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index f525a9030a0..fbde1669819 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -37,9 +37,7 @@ namespace Microsoft::Console::VirtualTerminal bool ActionVt52EscDispatch(const VTID id, const gsl::span parameters) override; - bool ActionCsiDispatch(const wchar_t wch, - const gsl::span intermediates, - const gsl::span parameters) override; + bool ActionCsiDispatch(const VTID id, const gsl::span parameters) override; bool ActionClear() noexcept override; @@ -70,15 +68,6 @@ namespace Microsoft::Console::VirtualTerminal wchar_t _lastPrintedChar; std::vector _graphicsOptions; - bool _IntermediateQuestionMarkDispatch(const wchar_t wchAction, - const gsl::span parameters); - bool _IntermediateGreaterThanOrEqualDispatch(const wchar_t wch, - const wchar_t intermediate, - const gsl::span parameters); - bool _IntermediateExclamationDispatch(const wchar_t wch); - bool _IntermediateSpaceDispatch(const wchar_t wchAction, - const gsl::span parameters); - enum EscActionCodes : uint64_t { DECSC_CursorSave = VTID("7"), @@ -100,46 +89,48 @@ namespace Microsoft::Console::VirtualTerminal DECALN_ScreenAlignmentPattern = VTID("#8") }; - enum CsiActionCodes : wchar_t + enum CsiActionCodes : uint64_t { - ICH_InsertCharacter = L'@', - CUU_CursorUp = L'A', - CUD_CursorDown = L'B', - CUF_CursorForward = L'C', - CUB_CursorBackward = L'D', - CNL_CursorNextLine = L'E', - CPL_CursorPrevLine = L'F', - CHA_CursorHorizontalAbsolute = L'G', - CUP_CursorPosition = L'H', - CHT_CursorForwardTab = L'I', - ED_EraseDisplay = L'J', - EL_EraseLine = L'K', - IL_InsertLine = L'L', - DL_DeleteLine = L'M', - DCH_DeleteCharacter = L'P', - SU_ScrollUp = L'S', - SD_ScrollDown = L'T', - ECH_EraseCharacters = L'X', - CBT_CursorBackTab = L'Z', - HPA_HorizontalPositionAbsolute = L'`', - HPR_HorizontalPositionRelative = L'a', - REP_RepeatCharacter = L'b', - DA_DeviceAttributes = L'c', - VPA_VerticalLinePositionAbsolute = L'd', - VPR_VerticalPositionRelative = L'e', - HVP_HorizontalVerticalPosition = L'f', - TBC_TabClear = L'g', - DECSET_PrivateModeSet = L'h', - DECRST_PrivateModeReset = L'l', - SGR_SetGraphicsRendition = L'm', - DSR_DeviceStatusReport = L'n', - DECSTBM_SetScrollingRegion = L'r', - ANSISYSSC_CursorSave = L's', // NOTE: Overlaps with DECLRMM/DECSLRM. Fix when/if implemented. - DTTERM_WindowManipulation = L't', // NOTE: Overlaps with DECSLPP. Fix when/if implemented. - ANSISYSRC_CursorRestore = L'u', // NOTE: Overlaps with DECSMBV. Fix when/if implemented. - DECSCUSR_SetCursorStyle = L'q', // With SP intermediate - DECSTR_SoftReset = L'p', // With ! intermediate - DECSCPP_SetColumnsPerPage = L'|' // With $ intermediate + ICH_InsertCharacter = VTID("@"), + CUU_CursorUp = VTID("A"), + CUD_CursorDown = VTID("B"), + CUF_CursorForward = VTID("C"), + CUB_CursorBackward = VTID("D"), + CNL_CursorNextLine = VTID("E"), + CPL_CursorPrevLine = VTID("F"), + CHA_CursorHorizontalAbsolute = VTID("G"), + CUP_CursorPosition = VTID("H"), + CHT_CursorForwardTab = VTID("I"), + ED_EraseDisplay = VTID("J"), + EL_EraseLine = VTID("K"), + IL_InsertLine = VTID("L"), + DL_DeleteLine = VTID("M"), + 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 diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index c5214979a89..0dd27d9801c 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -410,8 +410,12 @@ void StateMachine::_ActionCsiDispatch(const wchar_t wch) { _trace.TraceOnAction(L"CsiDispatch"); - const bool success = _engine->ActionCsiDispatch(wch, - { _intermediates.data(), _intermediates.size() }, + VTIDBuilder idBuilder; + for (wchar_t intermediate : _intermediates) + { + idBuilder.AddIntermediate(intermediate); + } + const bool success = _engine->ActionCsiDispatch(idBuilder.Finalize(wch), { _parameters.data(), _parameters.size() }); // Trace the result. diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index b97fa263c1c..c9d97b86801 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -222,7 +222,7 @@ class Microsoft::Console::VirtualTerminal::InputEngineTest std::wstring GenerateSgrMouseSequence(const CsiMouseButtonCodes button, const unsigned short modifiers, const COORD position, - const CsiActionCodes direction); + const VTID direction); // SGR_PARAMS serves as test input // - the state of the buttons (constructed via InputStateMachineEngine::CsiActionMouseCodes) @@ -1089,7 +1089,7 @@ void InputEngineTest::AltBackspaceEnterTest() std::wstring InputEngineTest::GenerateSgrMouseSequence(const CsiMouseButtonCodes button, const unsigned short modifiers, const COORD position, - const CsiActionCodes direction) + const VTID direction) { // we first need to convert "button" and "modifiers" into an 8 bit sequence unsigned int actionCode = 0; @@ -1102,7 +1102,11 @@ std::wstring InputEngineTest::GenerateSgrMouseSequence(const CsiMouseButtonCodes // modifiers represents the middle 4 bits actionCode |= modifiers; - return wil::str_printf_failfast(L"\x1b[<%d;%d;%d%c", static_cast(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(L"\x1b[%c%d;%d;%d%c", prefixChar, static_cast(actionCode), position.X, position.Y, finalChar); } void InputEngineTest::VerifySGRMouseData(const std::vector> testData) diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index b80af3d17c9..1b4e2f057dd 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -79,9 +79,7 @@ class Microsoft::Console::VirtualTerminal::TestStateMachineEngine : public IStat bool DispatchIntermediatesFromEscape() const override { return false; }; // ActionCsiDispatch is the only method that's actually implemented. - bool ActionCsiDispatch(const wchar_t /*wch*/, - const gsl::span /*intermediates*/, - const gsl::span parameters) override + bool ActionCsiDispatch(const VTID /*id*/, const gsl::span parameters) override { // If flush to terminal is registered for a test, then use it. if (pfnFlushToTerminal) From 84850022459dcbb04ec089ae7369ac84d1b94ee2 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 13 Aug 2020 13:54:41 +0100 Subject: [PATCH 09/11] Use the VTID builder class to construct sequence identifiers in the state machine. --- src/terminal/parser/stateMachine.cpp | 28 ++++++---------------------- src/terminal/parser/stateMachine.hpp | 4 ++-- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/terminal/parser/stateMachine.cpp b/src/terminal/parser/stateMachine.cpp index 0dd27d9801c..54246d504a1 100644 --- a/src/terminal/parser/stateMachine.cpp +++ b/src/terminal/parser/stateMachine.cpp @@ -15,7 +15,6 @@ StateMachine::StateMachine(std::unique_ptr engine) : _state(VTStates::Ground), _trace(Microsoft::Console::VirtualTerminal::ParserTracing()), _isInAnsiMode(true), - _intermediates{}, _parameters{}, _oscString{}, _cachedSequence{ std::nullopt }, @@ -353,12 +352,7 @@ void StateMachine::_ActionEscDispatch(const wchar_t wch) { _trace.TraceOnAction(L"EscDispatch"); - VTIDBuilder idBuilder; - for (wchar_t intermediate : _intermediates) - { - idBuilder.AddIntermediate(intermediate); - } - const bool success = _engine->ActionEscDispatch(idBuilder.Finalize(wch)); + const bool success = _engine->ActionEscDispatch(_identifier.Finalize(wch)); // Trace the result. _trace.DispatchSequenceTrace(success); @@ -381,12 +375,7 @@ void StateMachine::_ActionVt52EscDispatch(const wchar_t wch) { _trace.TraceOnAction(L"Vt52EscDispatch"); - VTIDBuilder idBuilder; - for (wchar_t intermediate : _intermediates) - { - idBuilder.AddIntermediate(intermediate); - } - const bool success = _engine->ActionVt52EscDispatch(idBuilder.Finalize(wch), + const bool success = _engine->ActionVt52EscDispatch(_identifier.Finalize(wch), { _parameters.data(), _parameters.size() }); // Trace the result. @@ -410,12 +399,7 @@ void StateMachine::_ActionCsiDispatch(const wchar_t wch) { _trace.TraceOnAction(L"CsiDispatch"); - VTIDBuilder idBuilder; - for (wchar_t intermediate : _intermediates) - { - idBuilder.AddIntermediate(intermediate); - } - const bool success = _engine->ActionCsiDispatch(idBuilder.Finalize(wch), + const bool success = _engine->ActionCsiDispatch(_identifier.Finalize(wch), { _parameters.data(), _parameters.size() }); // Trace the result. @@ -434,12 +418,12 @@ void StateMachine::_ActionCsiDispatch(const wchar_t wch) // - wch - Character to dispatch. // Return Value: // - -void StateMachine::_ActionCollect(const wchar_t wch) +void StateMachine::_ActionCollect(const wchar_t wch) noexcept { _trace.TraceOnAction(L"Collect"); // store collect data - _intermediates.push_back(wch); + _identifier.AddIntermediate(wch); } // Routine Description: @@ -485,7 +469,7 @@ void StateMachine::_ActionClear() _trace.TraceOnAction(L"Clear"); // clear all internal stored state. - _intermediates.clear(); + _identifier.Clear(); _parameters.clear(); diff --git a/src/terminal/parser/stateMachine.hpp b/src/terminal/parser/stateMachine.hpp index dfa66e5ec1e..0649a7dd806 100644 --- a/src/terminal/parser/stateMachine.hpp +++ b/src/terminal/parser/stateMachine.hpp @@ -55,7 +55,7 @@ namespace Microsoft::Console::VirtualTerminal void _ActionPrint(const wchar_t wch); void _ActionEscDispatch(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 _ActionCsiDispatch(const wchar_t wch); void _ActionOscParam(const wchar_t wch) noexcept; @@ -123,7 +123,7 @@ namespace Microsoft::Console::VirtualTerminal std::wstring_view _run; - std::vector _intermediates; + VTIDBuilder _identifier; std::vector _parameters; std::wstring _oscString; From c885d4479e59ed030d3232ff91571be3e353350d Mon Sep 17 00:00:00 2001 From: James Holderness Date: Sat, 15 Aug 2020 02:50:47 +0100 Subject: [PATCH 10/11] Appease the spelling bot. --- .github/actions/spell-check/expect/expect.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/spell-check/expect/expect.txt b/.github/actions/spell-check/expect/expect.txt index 9379acedf80..c3f4d9d7dc2 100644 --- a/.github/actions/spell-check/expect/expect.txt +++ b/.github/actions/spell-check/expect/expect.txt @@ -542,6 +542,7 @@ DECSCUSR DECSED DECSEL DECSET +DECSLPP DECSLRM DECSMBV DECSMKR @@ -2546,6 +2547,7 @@ vstudio vswhere vtapp VTE +VTID vtio vtmode vtpipeterm From 8dd0c554970fb8e471896345453e113b7e5404c2 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 18 Aug 2020 18:51:28 +0100 Subject: [PATCH 11/11] Update the String Terminator check to use the new VTID system. --- src/terminal/parser/OutputStateMachineEngine.cpp | 10 ++++------ src/terminal/parser/OutputStateMachineEngine.hpp | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/terminal/parser/OutputStateMachineEngine.cpp b/src/terminal/parser/OutputStateMachineEngine.cpp index b090eee673f..cdecc28bbd8 100644 --- a/src/terminal/parser/OutputStateMachineEngine.cpp +++ b/src/terminal/parser/OutputStateMachineEngine.cpp @@ -184,16 +184,14 @@ bool OutputStateMachineEngine::ActionPassThroughString(const std::wstring_view s // - true iff we successfully dispatched the sequence. bool OutputStateMachineEngine::ActionEscDispatch(const VTID id) { - if (wch == L'\\' && intermediates.empty()) - { - // This is presumably the 7-bit string terminator, which is essentially a no-op. - return true; - } - bool success = false; switch (id) { + case EscActionCodes::ST_StringTerminator: + // This is the 7-bit string terminator, which is essentially a no-op. + success = true; + break; case EscActionCodes::DECSC_CursorSave: success = _dispatch->CursorSaveState(); TermTelemetry::Instance().Log(TermTelemetry::Codes::DECSC); diff --git a/src/terminal/parser/OutputStateMachineEngine.hpp b/src/terminal/parser/OutputStateMachineEngine.hpp index fbde1669819..e6332a076e1 100644 --- a/src/terminal/parser/OutputStateMachineEngine.hpp +++ b/src/terminal/parser/OutputStateMachineEngine.hpp @@ -80,6 +80,7 @@ namespace Microsoft::Console::VirtualTerminal RI_ReverseLineFeed = VTID("M"), SS2_SingleShift = VTID("N"), SS3_SingleShift = VTID("O"), + ST_StringTerminator = VTID("\\"), RIS_ResetToInitialState = VTID("c"), LS2_LockingShift = VTID("n"), LS3_LockingShift = VTID("o"),