// 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 #include #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: // - 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 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); }