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,
|
|
|
|
const std::basic_string_view<WORD> attrs,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
const OutputCellIterator it(attrs, true);
|
|
|
|
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);
|
|
|
|
|
|
|
|
// Look over written wide chars to find equilalent count of ascii chars so we can properly report back
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
// Notify accessibility
|
|
|
|
auto endingCoordinate = startingCoordinate;
|
|
|
|
bufferSize.MoveInBounds(cellsModified, endingCoordinate);
|
|
|
|
screenBuffer.NotifyAccessibilityEventing(startingCoordinate.X, startingCoordinate.Y, endingCoordinate.X, endingCoordinate.Y);
|
|
|
|
}
|
|
|
|
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
|
|
|
|
// 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,
|
|
|
|
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(); });
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
auto endingCoordinate = startingCoordinate;
|
|
|
|
bufferSize.MoveInBounds(cellsModified, endingCoordinate);
|
|
|
|
screenInfo.NotifyAccessibilityEventing(startingCoordinate.X, startingCoordinate.Y, endingCoordinate.X, endingCoordinate.Y);
|
|
|
|
}
|
|
|
|
CATCH_RETURN();
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
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);
|
|
|
|
}
|