316 lines
11 KiB
C++
316 lines
11 KiB
C++
// 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;
|
|
using Microsoft::Console::Interactivity::ServiceLocator;
|
|
|
|
// 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.
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
|
const std::basic_string_view<WORD> attrs,
|
|
const COORD target,
|
|
size_t& used) noexcept
|
|
{
|
|
// 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.
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext,
|
|
const std::wstring_view chars,
|
|
const COORD target,
|
|
size_t& used) noexcept
|
|
{
|
|
// 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.
|
|
[[nodiscard]] HRESULT ApiRoutines::WriteConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext,
|
|
const std::string_view chars,
|
|
const COORD target,
|
|
size_t& used) noexcept
|
|
{
|
|
// 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 equivalent 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.)
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputAttributeImpl(IConsoleOutputObject& OutContext,
|
|
const WORD attribute,
|
|
const size_t lengthToWrite,
|
|
const COORD startingCoordinate,
|
|
size_t& cellsModified) noexcept
|
|
{
|
|
// 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.)
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputCharacterWImpl(IConsoleOutputObject& OutContext,
|
|
const wchar_t character,
|
|
const size_t lengthToWrite,
|
|
const COORD startingCoordinate,
|
|
size_t& cellsModified) noexcept
|
|
{
|
|
// 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);
|
|
|
|
// 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);
|
|
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.)
|
|
[[nodiscard]] HRESULT ApiRoutines::FillConsoleOutputCharacterAImpl(IConsoleOutputObject& OutContext,
|
|
const char character,
|
|
const size_t lengthToWrite,
|
|
const COORD startingCoordinate,
|
|
size_t& cellsModified) noexcept
|
|
{
|
|
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);
|
|
}
|