terminal/src/host/outputStream.hpp
James Holderness 0d92f71e45
Add support for VT100 Auto Wrap Mode (DECAWM) (#3943)
## Summary of the Pull Request

This adds support for the [`DECAWM`](https://vt100.net/docs/vt510-rm/DECAWM) private mode escape sequence, which controls whether or not the output wraps to the next line when the cursor reaches the right edge of the screen. Tested manually, with [Vttest](https://invisible-island.net/vttest/), and with some new unit tests.

## PR Checklist
* [x] Closes #3826
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [x] Tests added/passed
* [ ] Requires documentation to be updated
* [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #3826

## Detailed Description of the Pull Request / Additional comments

The idea was to repurpose the existing `ENABLE_WRAP_AT_EOL_OUTPUT` mode, but the problem with that was it didn't work in VT mode - specifically, disabling it didn't prevent the wrapping from happening. This was because in VT mode the `WC_DELAY_EOL_WRAP` behaviour takes affect, and that bypasses the usual codepath where `ENABLE_WRAP_AT_EOL_OUTPUT` is checked,

To fix this, I had to add additional checks in the `WriteCharsLegacy` function (7dbefe06e41f191a0e83cfefe4896b66094c4089) to make sure the `WC_DELAY_EOL_WRAP` mode is only activated when `ENABLE_WRAP_AT_EOL_OUTPUT`  is also set.

Once that was fixed, though, another issue came to light: the `ENABLE_WRAP_AT_EOL_OUTPUT` mode doesn't actually work as documented. According to the docs, "if this mode is disabled, the last character in the row is overwritten with any subsequent characters". What actually happens is the cursor jumps back to the position at the start of the write, which could be anywhere on the line.

This seems completely broken to me, but I've checked in the Windows XP, and it has the same behaviour, so it looks like that's the way it has always been. So I've added a fix for this (9df98497ca38f7d0ea42623b723a8e2ecf9a4ab9), but it is only applied in VT mode.

Once that basic functionality was in place, though, we just needed a private API in the `ConGetSet` interface to toggle the mode, and then that API could be called from the `AdaptDispatch` class when the `DECAWM` escape sequence was received.

One last thing was to reenable the mode in reponse to a `DECSTR` soft reset. Technically the auto wrap mode was disabled by default on many of the DEC terminals, and some documentation suggests that `DECSTR` should reset it to that state, But most modern terminals (including XTerm) expect the wrapping to be enabled by default, and `DECSTR` reenables that state, so that's the behaviour I've copied.

## Validation Steps Performed

I've add a state machine test to confirm the `DECAWM` escape is dispatched correctly, and a screen buffer test to make sure the output is wrapped or clamped as appropriate for the two states.

I've also confirmed that the "wrap around" test is now working correctly in the _Test of screen features_ in Vttest.
2020-02-04 00:20:21 +00:00

172 lines
6.9 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- outputStream.hpp
Abstract:
- Classes to process text written into the console on the attached application's output stream (usually STDOUT).
Author:
- Michael Niksa <miniksa> July 27 2015
--*/
#pragma once
#include "..\terminal\adapter\adaptDefaults.hpp"
#include "..\types\inc\IInputEvent.hpp"
#include "..\inc\conattrs.hpp"
#include "IIoProvider.hpp"
class SCREEN_INFORMATION;
// The WriteBuffer class provides helpers for writing text into the TextBuffer that is backing a particular console screen buffer.
class WriteBuffer : public Microsoft::Console::VirtualTerminal::AdaptDefaults
{
public:
WriteBuffer(_In_ Microsoft::Console::IIoProvider& io);
// Implement Adapter callbacks for default cases (non-escape sequences)
void Print(const wchar_t wch) override;
void PrintString(const std::wstring_view string) override;
void Execute(const wchar_t wch) override;
[[nodiscard]] NTSTATUS GetResult() { return _ntstatus; };
private:
void _DefaultCase(const wchar_t wch);
void _DefaultStringCase(const std::wstring_view string);
Microsoft::Console::IIoProvider& _io;
NTSTATUS _ntstatus;
};
#include "..\terminal\adapter\conGetSet.hpp"
// The ConhostInternalGetSet is for the Conhost process to call the entrypoints for its own Get/Set APIs.
// Normally, these APIs are accessible from the outside of the conhost process (like by the process being "hosted") through
// the kernelbase/32 exposed public APIs and routed by the console driver (condrv) to this console host.
// But since we're trying to call them from *inside* the console host itself, we need to get in the way and route them straight to the
// v-table inside this process instance.
class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::ConGetSet
{
public:
ConhostInternalGetSet(_In_ Microsoft::Console::IIoProvider& io);
bool GetConsoleScreenBufferInfoEx(CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) const override;
bool SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX& screenBufferInfo) override;
bool SetConsoleCursorPosition(const COORD position) override;
bool GetConsoleCursorInfo(CONSOLE_CURSOR_INFO& cursorInfo) const override;
bool SetConsoleCursorInfo(const CONSOLE_CURSOR_INFO& cursorInfo) override;
bool SetConsoleTextAttribute(const WORD attr) override;
bool PrivateSetLegacyAttributes(const WORD attr,
const bool foreground,
const bool background,
const bool meta) override;
bool PrivateSetDefaultAttributes(const bool foreground,
const bool background) override;
bool SetConsoleXtermTextAttribute(const int xtermTableEntry,
const bool isForeground) override;
bool SetConsoleRGBTextAttribute(const COLORREF rgbColor,
const bool isForeground) override;
bool PrivateBoldText(const bool bolded) override;
bool PrivateGetExtendedTextAttributes(ExtendedAttributes& attrs) override;
bool PrivateSetExtendedTextAttributes(const ExtendedAttributes attrs) override;
bool PrivateGetTextAttributes(TextAttribute& attrs) const override;
bool PrivateSetTextAttributes(const TextAttribute& attrs) override;
bool PrivateWriteConsoleInputW(std::deque<std::unique_ptr<IInputEvent>>& events,
size_t& eventsWritten) override;
bool SetConsoleWindowInfo(bool const absolute,
const SMALL_RECT& window) override;
bool PrivateSetCursorKeysMode(const bool applicationMode) override;
bool PrivateSetKeypadMode(const bool applicationMode) override;
bool PrivateSetScreenMode(const bool reverseMode) override;
bool PrivateSetAutoWrapMode(const bool wrapAtEOL) override;
bool PrivateShowCursor(const bool show) noexcept override;
bool PrivateAllowCursorBlinking(const bool enable) override;
bool PrivateSetScrollingRegion(const SMALL_RECT& scrollMargins) override;
bool PrivateWarningBell() override;
bool PrivateGetLineFeedMode() const override;
bool PrivateLineFeed(const bool withReturn) override;
bool PrivateReverseLineFeed() override;
bool SetConsoleTitleW(const std::wstring_view title) override;
bool PrivateUseAlternateScreenBuffer() override;
bool PrivateUseMainScreenBuffer() override;
bool PrivateHorizontalTabSet();
bool PrivateForwardTab(const size_t numTabs) override;
bool PrivateBackwardsTab(const size_t numTabs) override;
bool PrivateTabClear(const bool clearAll) override;
bool PrivateSetDefaultTabStops() override;
bool PrivateEnableVT200MouseMode(const bool enabled) override;
bool PrivateEnableUTF8ExtendedMouseMode(const bool enabled) override;
bool PrivateEnableSGRExtendedMouseMode(const bool enabled) override;
bool PrivateEnableButtonEventMouseMode(const bool enabled) override;
bool PrivateEnableAnyEventMouseMode(const bool enabled) override;
bool PrivateEnableAlternateScroll(const bool enabled) override;
bool PrivateEraseAll() override;
bool PrivateGetConsoleScreenBufferAttributes(WORD& attributes) override;
bool PrivatePrependConsoleInput(std::deque<std::unique_ptr<IInputEvent>>& events,
size_t& eventsWritten) override;
bool SetCursorStyle(CursorType const style) override;
bool SetCursorColor(COLORREF const color) override;
bool PrivateRefreshWindow() override;
bool PrivateSuppressResizeRepaint() override;
bool PrivateWriteConsoleControlInput(const KeyEvent key) override;
bool GetConsoleOutputCP(unsigned int& codepage) override;
bool IsConsolePty(bool& isPty) const override;
bool DeleteLines(const size_t count) override;
bool InsertLines(const size_t count) override;
bool MoveToBottom() const override;
bool PrivateSetColorTableEntry(const short index, const COLORREF value) const noexcept override;
bool PrivateSetDefaultForeground(const COLORREF value) const noexcept override;
bool PrivateSetDefaultBackground(const COLORREF value) const noexcept override;
bool PrivateFillRegion(const COORD startPosition,
const size_t fillLength,
const wchar_t fillChar,
const bool standardFillAttrs) noexcept override;
bool PrivateScrollRegion(const SMALL_RECT scrollRect,
const std::optional<SMALL_RECT> clipRect,
const COORD destinationOrigin,
const bool standardFillAttrs) noexcept override;
private:
Microsoft::Console::IIoProvider& _io;
};