terminal/src/renderer/vt/Xterm256Engine.cpp
Mike Griese 7612044363
Implement a pair of shims for cls, Clear-Host in conpty mode (#5627)
## Summary of the Pull Request

This PR implements a pair of shims for `cmd` and `powershell`, so that their `cls` and `Clear-Host` functions will clear the entire terminal buffer (like they do in conhost), instead of just the viewport. With the conpty viewport and buffer being the same size, there's effectively no way to know if an application is calling these API's in this way with the intention of clearing the buffer or the viewport. We absolutely have to guess. 

Each of these shims checks to see if the way that the API is being called exactly matches the way `cmd` or `powershell` would call these APIs. If it does, we manually write a `^[[3J` to the connected terminal, to get he Terminal to clear it's own scrollback.

~~_⚠️ If another application were trying to clear the **viewport** with an exactly similar API call, this would also cause the terminal scrollback to get cleared ⚠️_~~

* [x] Should these shims be restricted to when the process that's calling them is actually `cmd.exe` or `powershell.exe`? Can I even do this? I think we've done such a good job of isolating the client process information from the rest of the host code that I can't figure out how to do this.
  - YES, this can be done, and I did it.
* [ ] **TODO**: _While I'm here_, should I have `DoSrvPrivateEraseAll` (the implementation for `^[[2J`, in `getset.cpp`) also manually trigger a EraseAll in the terminal in conpty mode?

## PR Checklist
* [x] Closes #3126
* [x] Actually closes #1305 too, which is really the same thing, but probably deserves a callout
* [x] I work here
* [x] Tests added/passed
* [n/a] Requires documentation to be updated

## Validation Steps Performed
* ran tests
* checked `cls` in the Terminal
* checked `Clear-Host` in the Terminal
* Checked running `powershell clear-host` from `cmd.exe`
2020-04-30 21:53:31 +00:00

127 lines
5.8 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "Xterm256Engine.hpp"
#pragma hdrstop
using namespace Microsoft::Console;
using namespace Microsoft::Console::Render;
using namespace Microsoft::Console::Types;
Xterm256Engine::Xterm256Engine(_In_ wil::unique_hfile hPipe,
const IDefaultColorProvider& colorProvider,
const Viewport initialViewport,
_In_reads_(cColorTable) const COLORREF* const ColorTable,
const WORD cColorTable) :
XtermEngine(std::move(hPipe), colorProvider, initialViewport, ColorTable, cColorTable, false),
_lastExtendedAttrsState{ ExtendedAttributes::Normal }
{
}
// Routine Description:
// - Write a VT sequence to change the current colors of text. Writes true RGB
// color sequences.
// Arguments:
// - colorForeground: The RGB Color to use to paint the foreground text.
// - colorBackground: The RGB Color to use to paint the background of the text.
// - legacyColorAttribute: A console attributes bit field specifying the brush
// colors we should use.
// - extendedAttrs - extended text attributes (italic, underline, etc.) to use.
// - isSettingDefaultBrushes: indicates if we should change the background color of
// the window. Unused for VT
// Return Value:
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT Xterm256Engine::UpdateDrawingBrushes(const COLORREF colorForeground,
const COLORREF colorBackground,
const WORD legacyColorAttribute,
const ExtendedAttributes extendedAttrs,
const bool /*isSettingDefaultBrushes*/) noexcept
{
//When we update the brushes, check the wAttrs to see if the LVB_UNDERSCORE
// flag is there. If the state of that flag is different then our
// current state, change the underlining state.
// We have to do this here, instead of in PaintBufferGridLines, because
// we'll have already painted the text by the time PaintBufferGridLines
// is called.
// TODO:GH#2915 Treat underline separately from LVB_UNDERSCORE
RETURN_IF_FAILED(_UpdateUnderline(legacyColorAttribute));
// Only do extended attributes in xterm-256color, as to not break telnet.exe.
RETURN_IF_FAILED(_UpdateExtendedAttrs(extendedAttrs));
return VtEngine::_RgbUpdateDrawingBrushes(colorForeground,
colorBackground,
WI_IsFlagSet(extendedAttrs, ExtendedAttributes::Bold),
_ColorTable,
_cColorTable);
}
// Routine Description:
// - Write a VT sequence to either start or stop underlining text.
// Arguments:
// - legacyColorAttribute: A console attributes bit field containing information
// about the underlining state of the text.
// Return Value:
// - S_OK if we succeeded, else an appropriate HRESULT for failing to allocate or write.
[[nodiscard]] HRESULT Xterm256Engine::_UpdateExtendedAttrs(const ExtendedAttributes extendedAttrs) noexcept
{
// Helper lambda to check if a state (attr) has changed since it's last
// value (lastState), and appropriately start/end that state with the given
// begin/end functions.
auto updateFlagAndState = [extendedAttrs, this](const ExtendedAttributes attr,
std::function<HRESULT(Xterm256Engine*)> beginFn,
std::function<HRESULT(Xterm256Engine*)> endFn) -> HRESULT {
const bool flagSet = WI_AreAllFlagsSet(extendedAttrs, attr);
const bool lastState = WI_AreAllFlagsSet(_lastExtendedAttrsState, attr);
if (flagSet != lastState)
{
if (flagSet)
{
RETURN_IF_FAILED(beginFn(this));
}
else
{
RETURN_IF_FAILED(endFn(this));
}
WI_ToggleAllFlags(_lastExtendedAttrsState, attr);
}
return S_OK;
};
auto hr = updateFlagAndState(ExtendedAttributes::Italics,
&Xterm256Engine::_BeginItalics,
&Xterm256Engine::_EndItalics);
RETURN_IF_FAILED(hr);
hr = updateFlagAndState(ExtendedAttributes::Blinking,
&Xterm256Engine::_BeginBlink,
&Xterm256Engine::_EndBlink);
RETURN_IF_FAILED(hr);
hr = updateFlagAndState(ExtendedAttributes::Invisible,
&Xterm256Engine::_BeginInvisible,
&Xterm256Engine::_EndInvisible);
RETURN_IF_FAILED(hr);
hr = updateFlagAndState(ExtendedAttributes::CrossedOut,
&Xterm256Engine::_BeginCrossedOut,
&Xterm256Engine::_EndCrossedOut);
RETURN_IF_FAILED(hr);
return S_OK;
}
// Method Description:
// - Manually emit a "Erase Scrollback" sequence to the connected terminal. We
// need to do this in certain cases that we've identified where we believe the
// client wanted the entire terminal buffer cleared, not just the viewport.
// For more information, see GH#3126.
// Arguments:
// - <none>
// Return Value:
// - S_OK if we wrote the sequences successfully, otherwise an appropriate HRESULT
[[nodiscard]] HRESULT Xterm256Engine::ManuallyClearScrollback() noexcept
{
return _ClearScrollback();
}