terminal/src/host/outputStream.hpp
James Holderness 53b6f143b3 Improve the DECSC/DECRC implementation (#3160)
The current DECSC implementation only saves the cursor position and
origin mode. This PR improves that functionality with additional support
for saving the SGR attributes, as well as the active character set.

It also fixes the way the saved state interacts with the alt buffer
(private mode 1049), triggering a save when switching to the alt buffer,
and a restore when switching back, and tracking the alt buffer state
independently from the main state.

In order to properly save and restore the SGR attributes, we first
needed to add a pair of APIs in the `ConGetSet` interface which could
round-trip the attributes with full 32-bit colors (the existing methods
only work with legacy attributes).

I then added a struct in the `AdaptDispatch` implementation to make it
easier to manage all of the parameters that needed to be saved. This
includes the cursor position and origin mode that we were already
tracking, and now also the SGR text attributes and the active character
set (via the `TermOutput` class).

Two instances of this structure are required, since changes made to the
saved state in the alt buffer need to be tracked separately from changes
in the main buffer. I've added a boolean property that specifies whether
we're in the alt buffer or not, and use that to decide which of the two
instances we're working with.

I also needed to explicitly trigger a save when switching to the alt
buffer, and a restore when switching back, since we weren't already
doing that (the existing implementation gave the impression that the
state was saved, because each buffer has its own cursor position, but
that doesn't properly match the XTerm behaviour).

For the state tracking itself, we've now got two additional properties -
the SGR attributes, which we obtain via the new private API, and the
active character set, which we get from a local `AdaptDispatch` field.
I'm saving the whole `TermOutput` class for the character set, since I'm
hoping that will make it automatically supports future enhancements. 

When restoring the cursor position, there is also now a fix to handle
the relative origin mode correctly. If the margins are changed between
the position being saved and restored, it's possible for the cursor to
end up outside of the new margins, which would be illegal. So there is
now an additional step that clamps the Y coordinate within the margin
boundaries if the origin mode is relative.

# Validation

I've added a couple of screen buffer tests which check that the various
parameters are saved and restored as expected, as well as checking that
the Y coordinate is clamped appropriately when the relative origin mode
is set.

I've also tested manually with vttest and confirmed that the
_SAVE/RESTORE CURSOR_ test (the last page of the _Test of screen
features_)) is now working a lot better than it used to.

Closes #148.
2019-11-05 13:35:50 -08:00

171 lines
7.3 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 wchar_t* const rgwch, const size_t cch) override;
void Execute(const wchar_t wch) override;
[[nodiscard]] NTSTATUS GetResult() { return _ntstatus; };
private:
void _DefaultCase(const wchar_t wch);
void _DefaultStringCase(_In_reads_(cch) const wchar_t* const rgwch, const size_t cch);
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(_Out_ CONSOLE_SCREEN_BUFFER_INFOEX* const pConsoleScreenBufferInfoEx) const override;
BOOL SetConsoleScreenBufferInfoEx(const CONSOLE_SCREEN_BUFFER_INFOEX* const pConsoleScreenBufferInfoEx) override;
BOOL SetConsoleCursorPosition(const COORD coordCursorPosition) override;
BOOL GetConsoleCursorInfo(_In_ CONSOLE_CURSOR_INFO* const pConsoleCursorInfo) const override;
BOOL SetConsoleCursorInfo(const CONSOLE_CURSOR_INFO* const pConsoleCursorInfo) override;
BOOL FillConsoleOutputCharacterW(const WCHAR wch,
const DWORD nLength,
const COORD dwWriteCoord,
size_t& numberOfCharsWritten) noexcept override;
BOOL FillConsoleOutputAttribute(const WORD wAttribute,
const DWORD nLength,
const COORD dwWriteCoord,
size_t& numberOfAttrsWritten) noexcept override;
BOOL SetConsoleTextAttribute(const WORD wAttr) override;
BOOL PrivateSetLegacyAttributes(const WORD wAttr,
const bool fForeground,
const bool fBackground,
const bool fMeta) override;
BOOL PrivateSetDefaultAttributes(const bool fForeground,
const bool fBackground) override;
BOOL SetConsoleXtermTextAttribute(const int iXtermTableEntry,
const bool fIsForeground) override;
BOOL SetConsoleRGBTextAttribute(const COLORREF rgbColor,
const bool fIsForeground) override;
BOOL PrivateBoldText(const bool bolded) override;
BOOL PrivateGetExtendedTextAttributes(ExtendedAttributes* const pAttrs) override;
BOOL PrivateSetExtendedTextAttributes(const ExtendedAttributes attrs) override;
BOOL PrivateGetTextAttributes(TextAttribute* const pAttrs) const override;
BOOL PrivateSetTextAttributes(const TextAttribute& attrs) override;
BOOL PrivateWriteConsoleInputW(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& events,
_Out_ size_t& eventsWritten) override;
BOOL ScrollConsoleScreenBufferW(const SMALL_RECT* pScrollRectangle,
_In_opt_ const SMALL_RECT* pClipRectangle,
_In_ COORD coordDestinationOrigin,
const CHAR_INFO* pFill) override;
BOOL SetConsoleWindowInfo(BOOL const bAbsolute,
const SMALL_RECT* const lpConsoleWindow) override;
BOOL PrivateSetCursorKeysMode(const bool fApplicationMode) override;
BOOL PrivateSetKeypadMode(const bool fApplicationMode) override;
BOOL PrivateShowCursor(const bool show) noexcept override;
BOOL PrivateAllowCursorBlinking(const bool fEnable) override;
BOOL PrivateSetScrollingRegion(const SMALL_RECT* const srScrollMargins) override;
BOOL PrivateReverseLineFeed() override;
BOOL MoveCursorVertically(const short lines) override;
BOOL SetConsoleTitleW(const std::wstring_view title) override;
BOOL PrivateUseAlternateScreenBuffer() override;
BOOL PrivateUseMainScreenBuffer() override;
BOOL PrivateHorizontalTabSet();
BOOL PrivateForwardTab(const SHORT sNumTabs) override;
BOOL PrivateBackwardsTab(const SHORT sNumTabs) override;
BOOL PrivateTabClear(const bool fClearAll) override;
BOOL PrivateSetDefaultTabStops() override;
BOOL PrivateEnableVT200MouseMode(const bool fEnabled) override;
BOOL PrivateEnableUTF8ExtendedMouseMode(const bool fEnabled) override;
BOOL PrivateEnableSGRExtendedMouseMode(const bool fEnabled) override;
BOOL PrivateEnableButtonEventMouseMode(const bool fEnabled) override;
BOOL PrivateEnableAnyEventMouseMode(const bool fEnabled) override;
BOOL PrivateEnableAlternateScroll(const bool fEnabled) override;
BOOL PrivateEraseAll() override;
BOOL PrivateGetConsoleScreenBufferAttributes(_Out_ WORD* const pwAttributes) override;
BOOL PrivatePrependConsoleInput(_Inout_ std::deque<std::unique_ptr<IInputEvent>>& events,
_Out_ size_t& eventsWritten) override;
BOOL SetCursorStyle(CursorType const cursorType) override;
BOOL SetCursorColor(COLORREF const cursorColor) override;
BOOL PrivateRefreshWindow() override;
BOOL PrivateSuppressResizeRepaint() override;
BOOL PrivateWriteConsoleControlInput(_In_ KeyEvent key) override;
BOOL GetConsoleOutputCP(_Out_ unsigned int* const puiOutputCP) override;
BOOL IsConsolePty(_Out_ bool* const pIsPty) const override;
BOOL DeleteLines(const unsigned int count) override;
BOOL InsertLines(const unsigned int 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;
private:
Microsoft::Console::IIoProvider& _io;
};