2019-05-03 00:29:04 +02:00
|
|
|
// Copyright (c) Microsoft Corporation.
|
|
|
|
// Licensed under the MIT license.
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
|
|
#include "_output.h"
|
|
|
|
|
|
|
|
#include "dbcs.h"
|
|
|
|
#include "handle.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
|
|
|
#include "../buffer/out/CharRow.hpp"
|
|
|
|
|
|
|
|
#include "../interactivity/inc/ServiceLocator.hpp"
|
|
|
|
#include "../types/inc/Viewport.hpp"
|
|
|
|
#include "../types/inc/convert.hpp"
|
|
|
|
#include "../types/inc/Utf16Parser.hpp"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
using namespace Microsoft::Console::Types;
|
2019-05-30 20:14:21 +02:00
|
|
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
2019-05-03 00:29:04 +02:00
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - This routine writes a screen buffer region to the screen.
|
|
|
|
// Arguments:
|
|
|
|
// - screenInfo - reference to screen buffer information.
|
|
|
|
// - srRegion - Region to write in screen buffer coordinates. Region is inclusive
|
|
|
|
// Return Value:
|
|
|
|
// - <none>
|
|
|
|
void WriteToScreen(SCREEN_INFORMATION& screenInfo, const Viewport& region)
|
|
|
|
{
|
|
|
|
DBGOUTPUT(("WriteToScreen\n"));
|
|
|
|
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
|
|
// update to screen, if we're not iconic.
|
|
|
|
if (!screenInfo.IsActiveScreenBuffer() || WI_IsFlagSet(gci.Flags, CONSOLE_IS_ICONIC))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// clip region to fit within the viewport
|
|
|
|
const auto clippedRegion = screenInfo.GetViewport().Clamp(region);
|
|
|
|
if (!clippedRegion.IsValid())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (screenInfo.IsActiveScreenBuffer())
|
|
|
|
{
|
|
|
|
if (ServiceLocator::LocateGlobals().pRender != nullptr)
|
|
|
|
{
|
|
|
|
ServiceLocator::LocateGlobals().pRender->TriggerRedraw(region);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
WriteConvRegionToScreen(screenInfo, region);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - writes text attributes to the screen
|
|
|
|
// Arguments:
|
|
|
|
// - OutContext - the screen info to write to
|
|
|
|
// - attrs - the attrs to write to the screen
|
|
|
|
// - target - the starting coordinate in the screen
|
|
|
|
// - used - number of elements written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK, E_INVALIDARG or similar HRESULT error.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
2020-07-15 18:40:42 +02:00
|
|
|
const gsl::span<const WORD> attrs,
|
2019-06-11 22:27:09 +02:00
|
|
|
const COORD target,
|
|
|
|
size_t& used) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set used to 0 from the beginning in case we exit early.
|
|
|
|
used = 0;
|
|
|
|
|
|
|
|
if (attrs.empty())
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LockConsole();
|
|
|
|
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
|
|
|
|
|
|
|
auto& screenInfo = OutContext.GetActiveBuffer();
|
|
|
|
const auto bufferSize = screenInfo.GetBufferSize();
|
|
|
|
if (!bufferSize.IsInBounds(target))
|
|
|
|
{
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
2020-07-14 01:04:32 +02:00
|
|
|
const OutputCellIterator it(attrs);
|
2019-05-03 00:29:04 +02:00
|
|
|
const auto done = screenInfo.Write(it, target);
|
|
|
|
|
|
|
|
used = done.GetCellDistance(it);
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - writes text to the screen
|
|
|
|
// Arguments:
|
|
|
|
// - screenInfo - the screen info to write to
|
|
|
|
// - chars - the text to write to the screen
|
|
|
|
// - target - the starting coordinate in the screen
|
|
|
|
// - used - number of elements written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK, E_INVALIDARG or similar HRESULT error.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext,
|
|
|
|
const std::wstring_view chars,
|
|
|
|
const COORD target,
|
|
|
|
size_t& used) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set used to 0 from the beginning in case we exit early.
|
|
|
|
used = 0;
|
|
|
|
|
|
|
|
if (chars.empty())
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LockConsole();
|
|
|
|
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
|
|
|
|
|
|
|
auto& screenInfo = OutContext.GetActiveBuffer();
|
|
|
|
const auto bufferSize = screenInfo.GetBufferSize();
|
|
|
|
if (!bufferSize.IsInBounds(target))
|
|
|
|
{
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
OutputCellIterator it(chars);
|
|
|
|
const auto finished = screenInfo.Write(it, target);
|
|
|
|
used = finished.GetInputDistance(it);
|
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - writes text to the screen
|
|
|
|
// Arguments:
|
|
|
|
// - screenInfo - the screen info to write to
|
|
|
|
// - chars - the text to write to the screen
|
|
|
|
// - target - the starting coordinate in the screen
|
|
|
|
// - used - number of elements written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK, E_INVALIDARG or similar HRESULT error.
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext,
|
|
|
|
const std::string_view chars,
|
|
|
|
const COORD target,
|
|
|
|
size_t& used) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set used to 0 from the beginning in case we exit early.
|
|
|
|
used = 0;
|
|
|
|
|
|
|
|
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
|
|
const auto codepage = gci.OutputCP;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// convert to wide chars so we can call the W version of this function
|
|
|
|
const auto wideChars = ConvertToW(codepage, chars);
|
|
|
|
|
|
|
|
size_t wideCharsWritten = 0;
|
|
|
|
RETURN_IF_FAILED(WriteConsoleOutputCharacterWImpl(OutContext, wideChars, target, wideCharsWritten));
|
|
|
|
|
|
|
|
// Create a view over the wide chars and reduce it to the amount actually written (do in two steps to enforce bounds)
|
|
|
|
std::wstring_view writtenView(wideChars);
|
|
|
|
writtenView = writtenView.substr(0, wideCharsWritten);
|
|
|
|
|
2020-02-10 21:40:01 +01:00
|
|
|
// Look over written wide chars to find equivalent count of ascii chars so we can properly report back
|
2019-05-03 00:29:04 +02:00
|
|
|
// how many elements were actually written
|
|
|
|
used = GetALengthFromW(codepage, writtenView);
|
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - fills the screen buffer with the specified text attribute
|
|
|
|
// Arguments:
|
|
|
|
// - OutContext - reference to screen buffer information.
|
|
|
|
// - attribute - the text attribute to use to fill
|
|
|
|
// - lengthToWrite - the number of elements to write
|
|
|
|
// - startingCoordinate - Screen buffer coordinate to begin writing to.
|
|
|
|
// - cellsModified - the number of elements written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT code from failure to write (memory issues, invalid arg, etc.)
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
|
|
|
const WORD attribute,
|
|
|
|
const size_t lengthToWrite,
|
|
|
|
const COORD startingCoordinate,
|
|
|
|
size_t& cellsModified) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set modified cells to 0 from the beginning.
|
|
|
|
cellsModified = 0;
|
|
|
|
|
|
|
|
if (lengthToWrite == 0)
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LockConsole();
|
|
|
|
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
|
|
|
|
|
|
|
auto& screenBuffer = OutContext.GetActiveBuffer();
|
|
|
|
const auto bufferSize = screenBuffer.GetBufferSize();
|
|
|
|
if (!bufferSize.IsInBounds(startingCoordinate))
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
TextAttribute useThisAttr(attribute);
|
|
|
|
const OutputCellIterator it(useThisAttr, lengthToWrite);
|
|
|
|
const auto done = screenBuffer.Write(it, startingCoordinate);
|
|
|
|
|
|
|
|
cellsModified = done.GetCellDistance(it);
|
|
|
|
|
2021-07-09 20:45:44 +02:00
|
|
|
if (screenBuffer.HasAccessibilityEventing())
|
|
|
|
{
|
|
|
|
// Notify accessibility
|
|
|
|
auto endingCoordinate = startingCoordinate;
|
|
|
|
bufferSize.MoveInBounds(cellsModified, endingCoordinate);
|
|
|
|
screenBuffer.NotifyAccessibilityEventing(startingCoordinate.X, startingCoordinate.Y, endingCoordinate.X, endingCoordinate.Y);
|
|
|
|
}
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - fills the screen buffer with the specified wchar
|
|
|
|
// Arguments:
|
|
|
|
// - OutContext - reference to screen buffer information.
|
|
|
|
// - character - wchar to fill with
|
|
|
|
// - lengthToWrite - the number of elements to write
|
|
|
|
// - startingCoordinate - Screen buffer coordinate to begin writing to.
|
|
|
|
// - cellsModified - the number of elements written
|
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 23:53:31 +02:00
|
|
|
// - enablePowershellShim - true iff the client process that's calling this
|
|
|
|
// method is "powershell.exe". Used to enable certain compatibility shims for
|
|
|
|
// conpty mode. See GH#3126.
|
2019-05-03 00:29:04 +02:00
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT code from failure to write (memory issues, invalid arg, etc.)
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext,
|
|
|
|
const wchar_t character,
|
|
|
|
const size_t lengthToWrite,
|
|
|
|
const COORD startingCoordinate,
|
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 23:53:31 +02:00
|
|
|
size_t& cellsModified,
|
|
|
|
const bool enablePowershellShim) noexcept
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
|
|
|
// Set modified cells to 0 from the beginning.
|
|
|
|
cellsModified = 0;
|
|
|
|
|
|
|
|
if (lengthToWrite == 0)
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LockConsole();
|
|
|
|
auto Unlock = wil::scope_exit([&] { UnlockConsole(); });
|
|
|
|
|
|
|
|
// TODO: does this even need to be here or will it exit quickly?
|
|
|
|
auto& screenInfo = OutContext.GetActiveBuffer();
|
|
|
|
const auto bufferSize = screenInfo.GetBufferSize();
|
|
|
|
if (!bufferSize.IsInBounds(startingCoordinate))
|
|
|
|
{
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
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 23:53:31 +02:00
|
|
|
HRESULT hr = S_OK;
|
2019-05-03 00:29:04 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
const OutputCellIterator it(character, lengthToWrite);
|
make filling chars (and, thus, erase line/char) unset wrap (#2831)
EraseInLine calls `FillConsoleOutputCharacterW()`. In filling the row with
chars, we were setting the wrap flag. We need to specifically not do this on
ANY _FILL_ operation. Now a fill operation UNSETS the wrap flag if we fill to
the end of the line.
Originally, we had a boolean `setWrap` that would mean...
- **true**: if writing to the end of the row, SET the wrap value to true
- **false**: if writing to the end of the row, DON'T CHANGE the wrap value
Now we're making this bool a std::optional to allow for a ternary state. This
allows for us to handle the following cases completely. Refer to the table
below:
,- current wrap value
| ,- are we filling the last cell in the row?
| | ,- new wrap value
| | | ,- comments
|-- |-- |-- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 1 | 1 | THIS CASE WAS HANDLED CORRECTLY
| 1 | 0 | 0 | THIS CASE WAS UNHANDLED
| 1 | 0 | 1 |
| 1 | 1 | 1 |
To handle that special case (1-0-0), we need to UNSET the wrap. So now, we have
~setWrap~ `wrap` mean the following:
- **true**: if writing to the end of the row, SET the wrap value to TRUE
- **false**: if writing to the end of the row, SET the wrap value to FALSE
- **nullopt**: leave the wrap value as it is
Closes #1126
2019-10-01 03:16:31 +02:00
|
|
|
|
|
|
|
// when writing to the buffer, specifically unset wrap if we get to the last column.
|
|
|
|
// a fill operation should UNSET wrap in that scenario. See GH #1126 for more details.
|
|
|
|
const auto done = screenInfo.Write(it, startingCoordinate, false);
|
2019-05-03 00:29:04 +02:00
|
|
|
cellsModified = done.GetInputDistance(it);
|
|
|
|
|
|
|
|
// Notify accessibility
|
2021-07-09 20:45:44 +02:00
|
|
|
if (screenInfo.HasAccessibilityEventing())
|
|
|
|
{
|
|
|
|
auto endingCoordinate = startingCoordinate;
|
|
|
|
bufferSize.MoveInBounds(cellsModified, endingCoordinate);
|
|
|
|
screenInfo.NotifyAccessibilityEventing(startingCoordinate.X, startingCoordinate.Y, endingCoordinate.X, endingCoordinate.Y);
|
|
|
|
}
|
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 23:53:31 +02:00
|
|
|
|
|
|
|
// GH#3126 - This is a shim for powershell's `Clear-Host` function. In
|
|
|
|
// the vintage console, `Clear-Host` is supposed to clear the entire
|
|
|
|
// buffer. In conpty however, there's no difference between the viewport
|
|
|
|
// and the entirety of the buffer. We're going to see if this API call
|
|
|
|
// exactly matched the way we expect powershell to call it. If it does,
|
|
|
|
// then let's manually emit a ^[[3J to the connected terminal, so that
|
|
|
|
// their entire buffer will be cleared as well.
|
|
|
|
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
|
|
if (enablePowershellShim && gci.IsInVtIoMode())
|
|
|
|
{
|
|
|
|
const til::size currentBufferDimensions{ screenInfo.GetBufferSize().Dimensions() };
|
|
|
|
|
|
|
|
const bool wroteWholeBuffer = lengthToWrite == (currentBufferDimensions.area<size_t>());
|
|
|
|
const bool startedAtOrigin = startingCoordinate == COORD{ 0, 0 };
|
|
|
|
const bool wroteSpaces = character == UNICODE_SPACE;
|
|
|
|
|
|
|
|
if (wroteWholeBuffer && startedAtOrigin && wroteSpaces)
|
|
|
|
{
|
|
|
|
hr = gci.GetVtIo()->ManuallyClearScrollback();
|
|
|
|
}
|
|
|
|
}
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
|
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 23:53:31 +02:00
|
|
|
return hr;
|
2019-05-03 00:29:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Routine Description:
|
|
|
|
// - fills the screen buffer with the specified char
|
|
|
|
// Arguments:
|
|
|
|
// - OutContext - reference to screen buffer information.
|
|
|
|
// - character - ascii character to fill with
|
|
|
|
// - lengthToWrite - the number of elements to write
|
|
|
|
// - startingCoordinate - Screen buffer coordinate to begin writing to.
|
|
|
|
// - cellsModified - the number of elements written
|
|
|
|
// Return Value:
|
|
|
|
// - S_OK or suitable HRESULT code from failure to write (memory issues, invalid arg, etc.)
|
2019-06-11 22:27:09 +02:00
|
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext,
|
|
|
|
const char character,
|
|
|
|
const size_t lengthToWrite,
|
|
|
|
const COORD startingCoordinate,
|
|
|
|
size_t& cellsModified) noexcept
|
2020-02-10 23:09:08 +01:00
|
|
|
try
|
2019-05-03 00:29:04 +02:00
|
|
|
{
|
2020-02-10 23:09:08 +01:00
|
|
|
// In case ConvertToW throws causing an early return, set modified cells to 0.
|
|
|
|
cellsModified = 0;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
|
|
|
|
|
|
|
|
// convert to wide chars and call W version
|
|
|
|
const auto wchs = ConvertToW(gci.OutputCP, { &character, 1 });
|
|
|
|
|
|
|
|
LOG_HR_IF(E_UNEXPECTED, wchs.size() > 1);
|
|
|
|
|
|
|
|
return FillConsoleOutputCharacterWImpl(OutContext, wchs.at(0), lengthToWrite, startingCoordinate, cellsModified);
|
|
|
|
}
|
2020-02-10 23:09:08 +01:00
|
|
|
CATCH_RETURN()
|