6742965bb8
There are some code pages with "unmapped" code points in the C1 range, which results in them being translated into Unicode C1 control codes, even though that is not their intended use. To avoid having these characters triggering unintentional escape sequences, this PR now disables C1 controls by default. Switching to ISO-2022 encoding will re-enable them, though, since that is the most likely scenario in which they would be required. They can also be explicitly enabled, even in UTF-8 mode, with the `DECAC1` escape sequence. What I've done is add a new mode to the `StateMachine` class that controls whether C1 code points are interpreted as control characters or not. When disabled, these code points are simply dropped from the output, similar to the way a `NUL` is interpreted. This isn't exactly the way they were handled in the v1 console (which I think replaces them with the font _notdef_ glyph), but it matches the XTerm behavior, which seems more appropriate considering this is in VT mode. And it's worth noting that Windows Explorer seems to work the same way. As mentioned above, the mode can be enabled by designating the ISO-2022 coding system with a `DOCS` sequence, and it will be disabled again when UTF-8 is designated. You can also enable it explicitly with a `DECAC1` sequence (originally this was actually a DEC printer sequence, but it doesn't seem unreasonable to use it in a terminal). I've also extended the operations that save and restore "cursor state" (e.g. `DECSC` and `DECRC`) to include the state of the C1 parser mode, since it's closely tied to the code page and character sets which are also saved there. Similarly, when a `DECSTR` sequence resets the code page and character sets, I've now made it reset the C1 mode as well. I should note that the new `StateMachine` mode is controlled via a generic `SetParserMode` method (with a matching API in the `ConGetSet` interface) to allow for easier addition of other modes in the future. And I've reimplemented the existing ANSI/VT52 mode in terms of these generic methods instead of it having to have its own separate APIs. ## Validation Steps Performed Some of the unit tests for OSC sequences were using a C1 `0x9C` for the string terminator, which doesn't work by default anymore. Since that's not a good practice anyway, I thought it best to change those to a standard 7-bit terminator. However, in tests that were explicitly validating the C1 controls, I've just enabled the C1 parser mode at the start of the tests in order to get them working again. There were also some ANSI mode adapter tests that had to be updated to account for the fact that it has now been reimplemented in terms of the `SetParserMode` API. I've added a new state machine test to validate the changes in behavior when the C1 parser mode is enabled or disabled. And I've added an adapter test to verify that the `DesignateCodingSystems` and `AcceptC1Controls` methods toggle the C1 parser mode as expected. I've manually verified the test cases in #10069 and #10310 to confirm that they're no longer triggering control sequences by default. Although, as I explained above, the C1 code points are completely dropped from the output rather than displayed as _notdef_ glyphs. I think this is a reasonable compromise though. Closes #10069 Closes #10310
229 lines
11 KiB
C++
229 lines
11 KiB
C++
/*++
|
|
Copyright (c) Microsoft Corporation
|
|
Licensed under the MIT license.
|
|
|
|
Module Name:
|
|
- adaptDispatch.hpp
|
|
|
|
Abstract:
|
|
- This serves as the Windows Console API-specific implementation of the callbacks from our generic Virtual Terminal parser.
|
|
|
|
Author(s):
|
|
- Michael Niksa (MiNiksa) 30-July-2015
|
|
--*/
|
|
|
|
#pragma once
|
|
|
|
#include "termDispatch.hpp"
|
|
#include "DispatchCommon.hpp"
|
|
#include "conGetSet.hpp"
|
|
#include "adaptDefaults.hpp"
|
|
#include "FontBuffer.hpp"
|
|
#include "terminalOutput.hpp"
|
|
#include "..\..\types\inc\sgrStack.hpp"
|
|
|
|
namespace Microsoft::Console::VirtualTerminal
|
|
{
|
|
class AdaptDispatch : public ITermDispatch
|
|
{
|
|
public:
|
|
AdaptDispatch(std::unique_ptr<ConGetSet> pConApi,
|
|
std::unique_ptr<AdaptDefaults> pDefaults);
|
|
|
|
void Execute(const wchar_t wchControl) override
|
|
{
|
|
_pDefaults->Execute(wchControl);
|
|
}
|
|
|
|
void PrintString(const std::wstring_view string) override;
|
|
void Print(const wchar_t wchPrintable) override;
|
|
|
|
bool CursorUp(const size_t distance) override; // CUU
|
|
bool CursorDown(const size_t distance) override; // CUD
|
|
bool CursorForward(const size_t distance) override; // CUF
|
|
bool CursorBackward(const size_t distance) override; // CUB, BS
|
|
bool CursorNextLine(const size_t distance) override; // CNL
|
|
bool CursorPrevLine(const size_t distance) override; // CPL
|
|
bool CursorHorizontalPositionAbsolute(const size_t column) override; // HPA, CHA
|
|
bool VerticalLinePositionAbsolute(const size_t line) override; // VPA
|
|
bool HorizontalPositionRelative(const size_t distance) override; // HPR
|
|
bool VerticalPositionRelative(const size_t distance) override; // VPR
|
|
bool CursorPosition(const size_t line, const size_t column) override; // CUP, HVP
|
|
bool CursorSaveState() override; // DECSC
|
|
bool CursorRestoreState() override; // DECRC
|
|
bool CursorVisibility(const bool isVisible) override; // DECTCEM
|
|
bool EraseInDisplay(const DispatchTypes::EraseType eraseType) override; // ED
|
|
bool EraseInLine(const DispatchTypes::EraseType eraseType) override; // EL
|
|
bool EraseCharacters(const size_t numChars) override; // ECH
|
|
bool InsertCharacter(const size_t count) override; // ICH
|
|
bool DeleteCharacter(const size_t count) override; // DCH
|
|
bool SetGraphicsRendition(const VTParameters options) override; // SGR
|
|
bool SetLineRendition(const LineRendition rendition) override; // DECSWL, DECDWL, DECDHL
|
|
bool PushGraphicsRendition(const VTParameters options) override; // XTPUSHSGR
|
|
bool PopGraphicsRendition() override; // XTPOPSGR
|
|
bool DeviceStatusReport(const DispatchTypes::AnsiStatusType statusType) override; // DSR, DSR-OS, DSR-CPR
|
|
bool DeviceAttributes() override; // DA1
|
|
bool SecondaryDeviceAttributes() override; // DA2
|
|
bool TertiaryDeviceAttributes() override; // DA3
|
|
bool Vt52DeviceAttributes() override; // VT52 Identify
|
|
bool RequestTerminalParameters(const DispatchTypes::ReportingPermission permission) override; // DECREQTPARM
|
|
bool ScrollUp(const size_t distance) override; // SU
|
|
bool ScrollDown(const size_t distance) override; // SD
|
|
bool InsertLine(const size_t distance) override; // IL
|
|
bool DeleteLine(const size_t distance) override; // DL
|
|
bool SetColumns(const size_t columns) override; // DECCOLM
|
|
bool SetMode(const DispatchTypes::ModeParams param) override; // DECSET
|
|
bool ResetMode(const DispatchTypes::ModeParams param) override; // DECRST
|
|
bool SetCursorKeysMode(const bool applicationMode) override; // DECCKM
|
|
bool SetKeypadMode(const bool applicationMode) override; // DECKPAM, DECKPNM
|
|
bool EnableWin32InputMode(const bool win32InputMode) override; // win32-input-mode
|
|
bool EnableCursorBlinking(const bool enable) override; // ATT610
|
|
bool SetAnsiMode(const bool ansiMode) override; // DECANM
|
|
bool SetScreenMode(const bool reverseMode) override; // DECSCNM
|
|
bool SetOriginMode(const bool relativeMode) noexcept override; // DECOM
|
|
bool SetAutoWrapMode(const bool wrapAtEOL) override; // DECAWM
|
|
bool SetTopBottomScrollingMargins(const size_t topMargin,
|
|
const size_t bottomMargin) override; // DECSTBM
|
|
bool WarningBell() override; // BEL
|
|
bool CarriageReturn() override; // CR
|
|
bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) override; // IND, NEL, LF, FF, VT
|
|
bool ReverseLineFeed() override; // RI
|
|
bool SetWindowTitle(const std::wstring_view title) override; // OSCWindowTitle
|
|
bool UseAlternateScreenBuffer() override; // ASBSET
|
|
bool UseMainScreenBuffer() override; // ASBRST
|
|
bool HorizontalTabSet() override; // HTS
|
|
bool ForwardTab(const size_t numTabs) override; // CHT, HT
|
|
bool BackwardsTab(const size_t numTabs) override; // CBT
|
|
bool TabClear(const DispatchTypes::TabClearType clearType) override; // TBC
|
|
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
|
|
bool LockingShiftRight(const size_t gsetNumber) override; // LS1R, LS2R, LS3R
|
|
bool SingleShift(const size_t gsetNumber) override; // SS2, SS3
|
|
bool AcceptC1Controls(const bool enabled) override; // DECAC1
|
|
bool SoftReset() override; // DECSTR
|
|
bool HardReset() override; // RIS
|
|
bool ScreenAlignmentPattern() override; // DECALN
|
|
bool EnableDECCOLMSupport(const bool enabled) noexcept override; // ?40
|
|
bool EnableVT200MouseMode(const bool enabled) override; // ?1000
|
|
bool EnableUTF8ExtendedMouseMode(const bool enabled) override; // ?1005
|
|
bool EnableSGRExtendedMouseMode(const bool enabled) override; // ?1006
|
|
bool EnableButtonEventMouseMode(const bool enabled) override; // ?1002
|
|
bool EnableAnyEventMouseMode(const bool enabled) override; // ?1003
|
|
bool EnableAlternateScroll(const bool enabled) override; // ?1007
|
|
bool EnableXtermBracketedPasteMode(const bool enabled) noexcept override; // ?2004
|
|
bool SetCursorStyle(const DispatchTypes::CursorStyle cursorStyle) override; // DECSCUSR
|
|
bool SetCursorColor(const COLORREF cursorColor) override;
|
|
|
|
bool SetClipboard(const std::wstring_view content) noexcept override; // OSCSetClipboard
|
|
|
|
bool SetColorTableEntry(const size_t tableIndex,
|
|
const DWORD color) override; // OSCColorTable
|
|
bool SetDefaultForeground(const DWORD color) override; // OSCDefaultForeground
|
|
bool SetDefaultBackground(const DWORD color) override; // OSCDefaultBackground
|
|
|
|
bool WindowManipulation(const DispatchTypes::WindowManipulationType function,
|
|
const VTParameter parameter1,
|
|
const VTParameter parameter2) override; // DTTERM_WindowManipulation
|
|
|
|
bool AddHyperlink(const std::wstring_view uri, const std::wstring_view params) override;
|
|
bool EndHyperlink() override;
|
|
|
|
bool DoConEmuAction(const std::wstring_view string) noexcept override;
|
|
|
|
StringHandler DownloadDRCS(const size_t fontNumber,
|
|
const VTParameter startChar,
|
|
const DispatchTypes::DrcsEraseControl eraseControl,
|
|
const DispatchTypes::DrcsCellMatrix cellMatrix,
|
|
const DispatchTypes::DrcsFontSet fontSet,
|
|
const DispatchTypes::DrcsFontUsage fontUsage,
|
|
const VTParameter cellHeight,
|
|
const DispatchTypes::DrcsCharsetSize charsetSize) override; // DECDLD
|
|
|
|
StringHandler RequestSetting() override; // DECRQSS
|
|
|
|
private:
|
|
enum class ScrollDirection
|
|
{
|
|
Up,
|
|
Down
|
|
};
|
|
struct CursorState
|
|
{
|
|
unsigned int Row = 1;
|
|
unsigned int Column = 1;
|
|
bool IsOriginModeRelative = false;
|
|
TextAttribute Attributes = {};
|
|
TerminalOutput TermOutput = {};
|
|
bool C1ControlsAccepted = false;
|
|
unsigned int CodePage = 0;
|
|
};
|
|
struct Offset
|
|
{
|
|
int Value;
|
|
bool IsAbsolute;
|
|
// VT origin is at 1,1 so we need to subtract 1 from absolute positions.
|
|
static constexpr Offset Absolute(const size_t value) { return { gsl::narrow_cast<int>(value) - 1, true }; };
|
|
static constexpr Offset Forward(const size_t value) { return { gsl::narrow_cast<int>(value), false }; };
|
|
static constexpr Offset Backward(const size_t value) { return { -gsl::narrow_cast<int>(value), false }; };
|
|
static constexpr Offset Unchanged() { return Forward(0); };
|
|
};
|
|
|
|
bool _CursorMovePosition(const Offset rowOffset, const Offset colOffset, const bool clampInMargins) const;
|
|
bool _EraseSingleLineHelper(const CONSOLE_SCREEN_BUFFER_INFOEX& csbiex,
|
|
const DispatchTypes::EraseType eraseType,
|
|
const size_t lineId) const;
|
|
bool _EraseScrollback();
|
|
bool _EraseAll();
|
|
bool _InsertDeleteHelper(const size_t count, const bool isInsert) const;
|
|
bool _ScrollMovement(const ScrollDirection dir, const size_t distance) const;
|
|
|
|
bool _DoSetTopBottomScrollingMargins(const size_t topMargin,
|
|
const size_t bottomMargin);
|
|
bool _OperatingStatus() const;
|
|
bool _CursorPositionReport() const;
|
|
|
|
bool _WriteResponse(const std::wstring_view reply) const;
|
|
bool _ModeParamsHelper(const DispatchTypes::ModeParams param, const bool enable);
|
|
bool _DoDECCOLMHelper(const size_t columns);
|
|
|
|
bool _ClearSingleTabStop();
|
|
bool _ClearAllTabStops() noexcept;
|
|
void _ResetTabStops() noexcept;
|
|
void _InitTabStopsForWidth(const size_t width);
|
|
|
|
void _ReportSGRSetting() const;
|
|
void _ReportDECSTBMSetting() const;
|
|
|
|
std::vector<bool> _tabStopColumns;
|
|
bool _initDefaultTabStops = true;
|
|
|
|
std::unique_ptr<ConGetSet> _pConApi;
|
|
std::unique_ptr<AdaptDefaults> _pDefaults;
|
|
TerminalOutput _termOutput;
|
|
std::unique_ptr<FontBuffer> _fontBuffer;
|
|
std::optional<unsigned int> _initialCodePage;
|
|
|
|
// We have two instances of the saved cursor state, because we need
|
|
// one for the main buffer (at index 0), and another for the alt buffer
|
|
// (at index 1). The _usingAltBuffer property keeps tracks of which
|
|
// buffer is active, so can be used as an index into this array to
|
|
// obtain the saved state that should be currently active.
|
|
std::array<CursorState, 2> _savedCursorState;
|
|
bool _usingAltBuffer;
|
|
|
|
SMALL_RECT _scrollMargins;
|
|
|
|
bool _isOriginModeRelative;
|
|
|
|
bool _isDECCOLMAllowed;
|
|
|
|
SgrStack _sgrStack;
|
|
|
|
size_t _SetRgbColorsHelper(const VTParameters options,
|
|
TextAttribute& attr,
|
|
const bool isForeground) noexcept;
|
|
};
|
|
}
|