/*++ Copyright (c) Microsoft Corporation. Licensed under the MIT license. Module Name: - CommonState.hpp Abstract: - This represents common boilerplate state setup required for unit tests to run Author(s): - Michael Niksa (miniksa) 18-Jun-2014 - Paul Campbell (paulcam) 18-Jun-2014 Revision History: - Transformed to header-only class so it can be included by multiple unit testing projects in the codebase without a bunch of overhead. --*/ #pragma once #include "../host/globals.h" #include "../host/inputReadHandleData.h" #include "../interactivity/inc/ServiceLocator.hpp" class CommonState { public: static const til::CoordType s_csWindowWidth = 80; static const til::CoordType s_csWindowHeight = 80; static const til::CoordType s_csBufferWidth = 80; static const til::CoordType s_csBufferHeight = 300; CommonState() : m_heap(GetProcessHeap()), m_hrTextBufferInfo(E_FAIL), m_pFontInfo{ L"Consolas", 0, 0, { 8, 12 }, 0 }, m_backupTextBufferInfo(), m_readHandle(nullptr) { } ~CommonState() { m_heap = nullptr; } void InitEvents() { Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().hInputEvent.create(wil::EventOptions::ManualReset); } void PrepareReadHandle() { m_readHandle = std::make_unique(); } void CleanupReadHandle() { m_readHandle.reset(nullptr); } void PrepareGlobalFont(const til::size coordFontSize = { 8, 12 }) { m_pFontInfo = { L"Consolas", 0, 0, coordFontSize, 0 }; } void PrepareGlobalScreenBuffer(const til::CoordType viewWidth = s_csWindowWidth, const til::CoordType viewHeight = s_csWindowHeight, const til::CoordType bufferWidth = s_csBufferWidth, const til::CoordType bufferHeight = s_csBufferHeight) { Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); til::size coordWindowSize; coordWindowSize.width = viewWidth; coordWindowSize.height = viewHeight; til::size coordScreenBufferSize; coordScreenBufferSize.width = bufferWidth; coordScreenBufferSize.height = bufferHeight; UINT uiCursorSize = 12; THROW_IF_FAILED(SCREEN_INFORMATION::CreateInstance(coordWindowSize, m_pFontInfo, coordScreenBufferSize, TextAttribute{}, TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED }, uiCursorSize, &gci.pCurrentScreenBuffer)); // If we have a renderer, we need to call EnablePainting to initialize // the viewport. If not, we mark the text buffer as inactive so that it // doesn't try to trigger a redraw on a nonexistent renderer. if (g.pRender) { g.pRender->EnablePainting(); } else { gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false); } } void CleanupGlobalScreenBuffer() { const CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); delete gci.pCurrentScreenBuffer; } void PrepareGlobalInputBuffer() { CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); gci.pInputBuffer = new InputBuffer(); } void CleanupGlobalInputBuffer() { const CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); delete gci.pInputBuffer; } void PrepareCookedReadData(const std::wstring_view initialData = {}) { CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); auto* readData = new COOKED_READ_DATA(gci.pInputBuffer, m_readHandle.get(), gci.GetActiveOutputBuffer(), 0, nullptr, 0, L"", initialData, nullptr); gci.SetCookedReadData(readData); } void CleanupCookedReadData() { CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); delete &gci.CookedReadData(); gci.SetCookedReadData(nullptr); } void PrepareNewTextBufferInfo(const bool useDefaultAttributes = false, const til::CoordType bufferWidth = s_csBufferWidth, const til::CoordType bufferHeight = s_csBufferHeight) { Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); til::size coordScreenBufferSize; coordScreenBufferSize.width = bufferWidth; coordScreenBufferSize.height = bufferHeight; UINT uiCursorSize = 12; auto initialAttributes = useDefaultAttributes ? TextAttribute{} : TextAttribute{ FOREGROUND_BLUE | FOREGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY }; m_backupTextBufferInfo.swap(gci.pCurrentScreenBuffer->_textBuffer); try { std::unique_ptr textBuffer = std::make_unique(coordScreenBufferSize, initialAttributes, uiCursorSize, true, g.pRender); if (textBuffer.get() == nullptr) { m_hrTextBufferInfo = E_OUTOFMEMORY; } else { m_hrTextBufferInfo = S_OK; } gci.pCurrentScreenBuffer->_textBuffer.swap(textBuffer); // If we have a renderer, we need to call EnablePainting to initialize // the viewport. If not, we mark the text buffer as inactive so that it // doesn't try to trigger a redraw on a nonexistent renderer. if (g.pRender) { g.pRender->EnablePainting(); } else { gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false); } } catch (...) { m_hrTextBufferInfo = wil::ResultFromCaughtException(); } } void CleanupNewTextBufferInfo() { CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); VERIFY_IS_TRUE(gci.HasActiveOutputBuffer()); gci.pCurrentScreenBuffer->_textBuffer.swap(m_backupTextBufferInfo); } void FillTextBuffer() { CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); // fill with some assorted text that doesn't consume the whole row const til::CoordType cRowsToFill = 4; VERIFY_IS_TRUE(gci.HasActiveOutputBuffer()); TextBuffer& textBuffer = gci.GetActiveOutputBuffer().GetTextBuffer(); for (til::CoordType iRow = 0; iRow < cRowsToFill; iRow++) { ROW& row = textBuffer.GetMutableRowByOffset(iRow); // fill a row // - Each row is populated with L"AB\u304bC\u304dDE " // - 7 characters, 6 spaces. 13 total // - The characters take up first 9 columns. (The wide glyphs take up 2 columns each) // - か = \x304b, き = \x304d uint16_t column = 0; for (const auto& ch : std::wstring_view{ L"AB\u304bC\u304dDE " }) { const uint16_t width = ch >= 0x80 ? 2 : 1; row.ReplaceCharacters(column, width, { &ch, 1 }); column += width; } // A = bright red on dark gray // This string starts at index 0 auto Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); row.SetAttrToEnd(0, Attr); // BかC = dark gold on bright blue // This string starts at index 1 Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); row.SetAttrToEnd(1, Attr); // き = bright white on dark purple // This string starts at index 5 Attr = TextAttribute(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE); row.SetAttrToEnd(5, Attr); // DE = black on dark green // This string starts at index 7 Attr = TextAttribute(BACKGROUND_GREEN); row.SetAttrToEnd(7, Attr); // odd rows forced a wrap row.SetWrapForced(iRow & 1); } textBuffer.GetCursor().SetYPosition(cRowsToFill); } [[nodiscard]] HRESULT GetTextBufferInfoInitResult() { return m_hrTextBufferInfo; } private: HANDLE m_heap; HRESULT m_hrTextBufferInfo; FontInfo m_pFontInfo; std::unique_ptr m_backupTextBufferInfo; std::unique_ptr m_readHandle; };