Skip to content

Commit

Permalink
Add support for the DECRQM escape sequence (#14444)
Browse files Browse the repository at this point in the history
This PR adds support for the `DECRQM` (Request Mode) escape sequence,
which allows applications to query the state of the various modes
supported by the terminal. It also adds support for the `DECNKM` mode,
which aliases the existing `DECKPAM` and `DECKPNM` operations, so they
can be queried with `DECRQM` too.

This is one solution for #10153 (saving and restoring the state of
bracketed paste mode), and should also help with #1040 (providing a way
for clients to determine the capabilities of the terminal).

Prior to adding `DECRQM`, I also did some refactoring of the mode
handling to get rid of the mode setting methods in the `ITermDispatch`
interface that had no need to be there. Most of them were essentially a
single line of code that could easily be executed directly from the
`_ModeParamsHelper` handler anyway.

As part of this refactoring I combined all the internal `AdaptDispatch`
modes into an `enumset` to allow for easier management, and made sure
all modes were correctly reset in the `HardReset` method (prior to this,
there were a number of modes that we weren't restoring when we should
have been).

And note that there are some differences in behavior between conhost and
Windows Terminal. In conhost, `DECRQM` will report bracketed paste mode
as unsupported, and in Terminal, both `DECCOLM` and `AllowDECCOLM` are
reported as unsupported. And `DECCOLM` is now explicitly ignored in
conpty mode, to avoid the conpty client and conhost getting out of sync.
 
## Validation Steps Performed

I've manually confirmed that all the supported modes are reported in the
`DECRQM` tests in Vttest, and I have my own test scripts which I've used
to confirm that `RIS` is now resetting the modes correctly.

I've also added a unit test in `AdapterTest` that iterates through the
modes, checking the responses from `DECRQM` for both the set and reset
states.

I should also mention that I had to do some refactoring of the existing
tests to compensate for methods that were removed from `ITermDispatch`,
particularly in `OutputEngineTest`. In many cases, though, these tests
weren't doing much more than testing the test framework.
  • Loading branch information
j4james authored Nov 30, 2022
1 parent d1fbbb8 commit 4379148
Show file tree
Hide file tree
Showing 20 changed files with 437 additions and 813 deletions.
2 changes: 1 addition & 1 deletion .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ argb
ARRAYSIZE
ARROWKEYS
asan
ASBRST
ASBSET
ASDF
asdfghjkl
Expand Down Expand Up @@ -433,6 +432,7 @@ DECRARA
DECRC
DECREQTPARM
DECRLM
DECRPM
DECRQM
DECRQSS
DECRQTSR
Expand Down
7 changes: 7 additions & 0 deletions src/buffer/out/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ void Cursor::SetIsOn(const bool fIsOn) noexcept
void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) noexcept
{
_fBlinkingAllowed = fBlinkingAllowed;
// GH#2642 - From what we've gathered from other terminals, when blinking is
// disabled, the cursor should remain On always, and have the visibility
// controlled by the IsVisible property. So when you do a printf "\e[?12l"
// to disable blinking, the cursor stays stuck On. At this point, only the
// cursor visibility property controls whether the user can see it or not.
// (Yes, the cursor can be On and NOT Visible)
_fIsOn = true;
_RedrawCursorAlways();
}

Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class Microsoft::Terminal::Core::Terminal final :
void SetViewportPosition(const til::point position) noexcept override;
void SetTextAttributes(const TextAttribute& attrs) noexcept override;
void SetAutoWrapMode(const bool wrapAtEOL) noexcept override;
bool GetAutoWrapMode() const noexcept override;
void SetScrollingRegion(const til::inclusive_rect& scrollMargins) noexcept override;
void WarningBell() override;
bool GetLineFeedMode() const noexcept override;
Expand All @@ -124,7 +125,8 @@ class Microsoft::Terminal::Core::Terminal final :
bool ResizeWindow(const til::CoordType width, const til::CoordType height) noexcept override;
void SetConsoleOutputCP(const unsigned int codepage) noexcept override;
unsigned int GetConsoleOutputCP() const noexcept override;
void EnableXtermBracketedPasteMode(const bool enabled) noexcept override;
void SetBracketedPasteMode(const bool enabled) noexcept override;
std::optional<bool> GetBracketedPasteMode() const noexcept override;
void CopyToClipboard(std::wstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
void SetWorkingDirectory(std::wstring_view uri) override;
Expand Down
13 changes: 12 additions & 1 deletion src/cascadia/TerminalCore/TerminalApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ void Terminal::SetAutoWrapMode(const bool /*wrapAtEOL*/) noexcept
// TODO: This will be needed to support DECAWM.
}

bool Terminal::GetAutoWrapMode() const noexcept
{
// TODO: This will be needed to support DECAWM.
return true;
}

void Terminal::SetScrollingRegion(const til::inclusive_rect& /*scrollMargins*/) noexcept
{
// TODO: This will be needed to fully support DECSTBM.
Expand Down Expand Up @@ -123,11 +129,16 @@ unsigned int Terminal::GetConsoleOutputCP() const noexcept
return CP_UTF8;
}

void Terminal::EnableXtermBracketedPasteMode(const bool enabled) noexcept
void Terminal::SetBracketedPasteMode(const bool enabled) noexcept
{
_bracketedPasteMode = enabled;
}

std::optional<bool> Terminal::GetBracketedPasteMode() const noexcept
{
return _bracketedPasteMode;
}

void Terminal::CopyToClipboard(std::wstring_view content)
{
_pfnCopyToClipboard(content);
Expand Down
31 changes: 29 additions & 2 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,18 @@ void ConhostInternalGetSet::SetAutoWrapMode(const bool wrapAtEOL)
WI_UpdateFlag(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT, wrapAtEOL);
}

// Routine Description:
// - Retrieves the current state of ENABLE_WRAP_AT_EOL_OUTPUT mode.
// Arguments:
// - <none>
// Return Value:
// - true if the mode is enabled. false otherwise.
bool ConhostInternalGetSet::GetAutoWrapMode() const
{
const auto outputMode = _io.GetActiveOutputBuffer().OutputMode;
return WI_IsFlagSet(outputMode, ENABLE_WRAP_AT_EOL_OUTPUT);
}

// Routine Description:
// - Sets the top and bottom scrolling margins for the current page. This creates
// a subsection of the screen that scrolls when input reaches the end of the
Expand Down Expand Up @@ -325,9 +337,24 @@ unsigned int ConhostInternalGetSet::GetConsoleOutputCP() const
// - enable - set to true to enable bracketing, false to disable.
// Return Value:
// - <none>
void ConhostInternalGetSet::EnableXtermBracketedPasteMode(const bool /*enabled*/)
void ConhostInternalGetSet::SetBracketedPasteMode(const bool enabled)
{
// TODO
// TODO GH#395: Bracketed Paste Mode is not yet supported in conhost, but we
// still keep track of the state so it can be reported by DECRQM.
_bracketedPasteMode = enabled;
}

// Routine Description:
// - Gets the current state of XTerm bracketed paste mode.
// Arguments:
// - <none>
// Return Value:
// - true if the mode is enabled, false if not, nullopt if unsupported.
std::optional<bool> ConhostInternalGetSet::GetBracketedPasteMode() const
{
// TODO GH#395: Bracketed Paste Mode is not yet supported in conhost, so we
// only report the state if we're tracking it for conpty.
return IsConsolePty() ? std::optional{ _bracketedPasteMode } : std::nullopt;
}

// Routine Description:
Expand Down
5 changes: 4 additions & 1 deletion src/host/outputStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
void SetTextAttributes(const TextAttribute& attrs) override;

void SetAutoWrapMode(const bool wrapAtEOL) override;
bool GetAutoWrapMode() const override;

void SetScrollingRegion(const til::inclusive_rect& scrollMargins) override;

Expand All @@ -63,7 +64,8 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
void SetConsoleOutputCP(const unsigned int codepage) override;
unsigned int GetConsoleOutputCP() const override;

void EnableXtermBracketedPasteMode(const bool enabled) override;
void SetBracketedPasteMode(const bool enabled) override;
std::optional<bool> GetBracketedPasteMode() const override;
void CopyToClipboard(const std::wstring_view content) override;
void SetTaskbarProgress(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::TaskbarState state, const size_t progress) override;
void SetWorkingDirectory(const std::wstring_view uri) override;
Expand All @@ -78,4 +80,5 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::

private:
Microsoft::Console::IIoProvider& _io;
bool _bracketedPasteMode{ false };
};
1 change: 1 addition & 0 deletions src/terminal/adapter/DispatchTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ namespace Microsoft::Console::VirtualTerminal::DispatchTypes
ATT610_StartCursorBlink = DECPrivateMode(12),
DECTCEM_TextCursorEnableMode = DECPrivateMode(25),
XTERM_EnableDECCOLMSupport = DECPrivateMode(40),
DECNKM_NumericKeypadMode = DECPrivateMode(66),
DECBKM_BackarrowKeyMode = DECPrivateMode(67),
VT200_MOUSE_MODE = DECPrivateMode(1000),
BUTTON_EVENT_MOUSE_MODE = DECPrivateMode(1002),
Expand Down
21 changes: 1 addition & 20 deletions src/terminal/adapter/ITermDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,43 +45,24 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual bool CursorPosition(const VTInt line, const VTInt column) = 0; // CUP, HVP
virtual bool CursorSaveState() = 0; // DECSC
virtual bool CursorRestoreState() = 0; // DECRC
virtual bool CursorVisibility(const bool isVisible) = 0; // DECTCEM
virtual bool InsertCharacter(const VTInt count) = 0; // ICH
virtual bool DeleteCharacter(const VTInt count) = 0; // DCH
virtual bool ScrollUp(const VTInt distance) = 0; // SU
virtual bool ScrollDown(const VTInt distance) = 0; // SD
virtual bool InsertLine(const VTInt distance) = 0; // IL
virtual bool DeleteLine(const VTInt distance) = 0; // DL
virtual bool SetColumns(const VTInt columns) = 0; // DECCOLM
virtual bool SetCursorKeysMode(const bool applicationMode) = 0; // DECCKM
virtual bool SetKeypadMode(const bool applicationMode) = 0; // DECKPAM, DECKPNM
virtual bool EnableWin32InputMode(const bool win32InputMode) = 0; // win32-input-mode
virtual bool EnableCursorBlinking(const bool enable) = 0; // ATT610
virtual bool SetAnsiMode(const bool ansiMode) = 0; // DECANM
virtual bool SetScreenMode(const bool reverseMode) = 0; // DECSCNM
virtual bool SetOriginMode(const bool relativeMode) = 0; // DECOM
virtual bool SetAutoWrapMode(const bool wrapAtEOL) = 0; // DECAWM
virtual bool SetTopBottomScrollingMargins(const VTInt topMargin, const VTInt bottomMargin) = 0; // DECSTBM
virtual bool WarningBell() = 0; // BEL
virtual bool CarriageReturn() = 0; // CR
virtual bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) = 0; // IND, NEL, LF, FF, VT
virtual bool ReverseLineFeed() = 0; // RI
virtual bool SetWindowTitle(std::wstring_view title) = 0; // OscWindowTitle
virtual bool UseAlternateScreenBuffer() = 0; // ASBSET
virtual bool UseMainScreenBuffer() = 0; // ASBRST
virtual bool HorizontalTabSet() = 0; // HTS
virtual bool ForwardTab(const VTInt numTabs) = 0; // CHT, HT
virtual bool BackwardsTab(const VTInt numTabs) = 0; // CBT
virtual bool TabClear(const DispatchTypes::TabClearType clearType) = 0; // TBC
virtual bool EnableDECCOLMSupport(const bool enabled) = 0; // ?40
virtual bool EnableVT200MouseMode(const bool enabled) = 0; // ?1000
virtual bool EnableUTF8ExtendedMouseMode(const bool enabled) = 0; // ?1005
virtual bool EnableSGRExtendedMouseMode(const bool enabled) = 0; // ?1006
virtual bool EnableButtonEventMouseMode(const bool enabled) = 0; // ?1002
virtual bool EnableAnyEventMouseMode(const bool enabled) = 0; // ?1003
virtual bool EnableFocusEventMode(const bool enabled) = 0; // ?1004
virtual bool EnableAlternateScroll(const bool enabled) = 0; // ?1007
virtual bool EnableXtermBracketedPasteMode(const bool enabled) = 0; // ?2004
virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) = 0; // OSCColorTable
virtual bool SetDefaultForeground(const DWORD color) = 0; // OSCDefaultForeground
virtual bool SetDefaultBackground(const DWORD color) = 0; // OSCDefaultBackground
Expand Down Expand Up @@ -109,8 +90,8 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual bool PopGraphicsRendition() = 0; // XTPOPSGR

virtual bool SetMode(const DispatchTypes::ModeParams param) = 0; // DECSET

virtual bool ResetMode(const DispatchTypes::ModeParams param) = 0; // DECRST
virtual bool RequestMode(const DispatchTypes::ModeParams param) = 0; // DECRQM

virtual bool DeviceStatusReport(const DispatchTypes::StatusType statusType) = 0; // DSR, DSR-OS, DSR-CPR
virtual bool DeviceAttributes() = 0; // DA1
Expand Down
4 changes: 3 additions & 1 deletion src/terminal/adapter/ITerminalApi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace Microsoft::Console::VirtualTerminal
virtual void SetTextAttributes(const TextAttribute& attrs) = 0;

virtual void SetAutoWrapMode(const bool wrapAtEOL) = 0;
virtual bool GetAutoWrapMode() const = 0;

virtual void SetScrollingRegion(const til::inclusive_rect& scrollMargins) = 0;
virtual void WarningBell() = 0;
Expand All @@ -66,7 +67,8 @@ namespace Microsoft::Console::VirtualTerminal
virtual void SetConsoleOutputCP(const unsigned int codepage) = 0;
virtual unsigned int GetConsoleOutputCP() const = 0;

virtual void EnableXtermBracketedPasteMode(const bool enabled) = 0;
virtual void SetBracketedPasteMode(const bool enabled) = 0;
virtual std::optional<bool> GetBracketedPasteMode() const = 0;
virtual void CopyToClipboard(const std::wstring_view content) = 0;
virtual void SetTaskbarProgress(const DispatchTypes::TaskbarState state, const size_t progress) = 0;
virtual void SetWorkingDirectory(const std::wstring_view uri) = 0;
Expand Down
Loading

0 comments on commit 4379148

Please sign in to comment.