From 444de5b166d800b17c90510cde8664aefffcb236 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 28 Oct 2019 09:42:54 -0700 Subject: [PATCH] Introduce UiaRenderer project (#2930) As a part of setting up UIA Events, we need to be able to identify WHEN to notify the client. We'll be adopting the RendererEngine model that the VTRenderer and DxRenderer follow to identify when something on the screen is changing and what to alert the automation clients about. This PR just introduces the UiaRenderer. There's a lot of E_NOTIMPLs and S_FALSEs and a few comments throughout as to my thoughts. This'll make diffing future PRs easier and can make this process more iterative. The code does run with the PR so I plan on merging this into master as normal. --- OpenConsole.sln | 21 ++ src/renderer/uia/UiaRenderer.cpp | 404 +++++++++++++++++++++++++++++++ src/renderer/uia/UiaRenderer.hpp | 83 +++++++ src/renderer/uia/lib/uia.vcxproj | 24 ++ src/renderer/uia/precomp.cpp | 4 + src/renderer/uia/precomp.h | 11 + 6 files changed, 547 insertions(+) create mode 100644 src/renderer/uia/UiaRenderer.cpp create mode 100644 src/renderer/uia/UiaRenderer.hpp create mode 100644 src/renderer/uia/lib/uia.vcxproj create mode 100644 src/renderer/uia/precomp.cpp create mode 100644 src/renderer/uia/precomp.h diff --git a/OpenConsole.sln b/OpenConsole.sln index 4b9181d31a..08d5778de3 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -254,6 +254,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "s EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\winconpty.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RendererUia", "src\renderer\uia\lib\uia.vcxproj", "{48D21369-3D7B-4431-9967-24E81292CF63}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -1257,6 +1259,24 @@ Global {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.Build.0 = Release|x64 {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.ActiveCfg = Release|Win32 {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x86.Build.0 = Release|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x64.Build.0 = AuditMode|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.AuditMode|x86.Build.0 = AuditMode|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|ARM64.Build.0 = Debug|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.ActiveCfg = Debug|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x64.Build.0 = Debug|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x86.ActiveCfg = Debug|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.Debug|x86.Build.0 = Debug|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.ActiveCfg = Release|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|ARM64.Build.0 = Release|ARM64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.ActiveCfg = Release|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x64.Build.0 = Release|x64 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.ActiveCfg = Release|Win32 + {48D21369-3D7B-4431-9967-24E81292CF63}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1321,6 +1341,7 @@ Global {CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202} {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} + {48D21369-3D7B-4431-9967-24E81292CF63} = {05500DEF-2294-41E3-AF9A-24E580B82836} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp new file mode 100644 index 0000000000..cba61ff18a --- /dev/null +++ b/src/renderer/uia/UiaRenderer.cpp @@ -0,0 +1,404 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "UiaRenderer.hpp" + +#pragma hdrstop + +using namespace Microsoft::Console::Render; +using namespace Microsoft::Console::Types; + +// Routine Description: +// - Constructs a UIA engine for console text +// which primarily notifies automation clients of any activity +UiaEngine::UiaEngine() noexcept : + _isPainting{ false }, + _isEnabled{ false }, + RenderEngineBase() +{ +} + +// Routine Description: +// - Sets this engine to enabled allowing presentation to occur +// Arguments: +// - +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::Enable() noexcept +{ + _isEnabled = true; + return S_OK; +} + +// Routine Description: +// - Sets this engine to disabled to prevent presentation from occuring +// Arguments: +// - +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::Disable() noexcept +{ + _isEnabled = false; + return S_OK; +} + +// Routine Description: +// - Notifies us that the console has changed the character region specified. +// - NOTE: This typically triggers on cursor or text buffer changes +// Arguments: +// - psrRegion - Character region (SMALL_RECT) that has been changed +// Return Value: +// - S_OK, else an appropriate HRESULT for failing to allocate or write. +[[nodiscard]] HRESULT UiaEngine::Invalidate(const SMALL_RECT* const /*psrRegion*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Notifies us that the console has changed the position of the cursor. +// For UIA, this doesn't mean anything. So do nothing. +// Arguments: +// - pcoordCursor - the new position of the cursor +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::InvalidateCursor(const COORD* const /*pcoordCursor*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Invalidates a rectangle describing a pixel area on the display +// For UIA, this doesn't mean anything. So do nothing. +// Arguments: +// - prcDirtyClient - pixel rectangle +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::InvalidateSystem(const RECT* const /*prcDirtyClient*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Notifies us that the console has changed the selection region and would +// like it updated +// Arguments: +// - rectangles - One or more rectangles describing character positions on the grid +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::InvalidateSelection(const std::vector& /*rectangles*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Scrolls the existing dirty region (if it exists) and +// invalidates the area that is uncovered in the window. +// Arguments: +// - pcoordDelta - The number of characters to move and uncover. +// - -Y is up, Y is down, -X is left, X is right. +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::InvalidateScroll(const COORD* const /*pcoordDelta*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Notifies to repaint everything. +// - NOTE: Use sparingly. Only use when something that could affect the entire +// frame simultaneously occurs. +// Arguments: +// - +// Return Value: +// - S_OK, else an appropriate HRESULT for failing to allocate or write. +[[nodiscard]] HRESULT UiaEngine::InvalidateAll() noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - This currently has no effect in this renderer. +// Arguments: +// - pForcePaint - Always filled with false +// Return Value: +// - S_FALSE because we don't use this. +[[nodiscard]] HRESULT UiaEngine::InvalidateCircling(_Out_ bool* const pForcePaint) noexcept +{ + RETURN_HR_IF_NULL(E_INVALIDARG, pForcePaint); + + *pForcePaint = false; + return S_FALSE; +} + +// Routine Description: +// - This is unused by this renderer. +// Arguments: +// - pForcePaint - always filled with false. +// Return Value: +// - S_FALSE because this is unused. +[[nodiscard]] HRESULT UiaEngine::PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept +{ + RETURN_HR_IF_NULL(E_INVALIDARG, pForcePaint); + + *pForcePaint = false; + return S_FALSE; +} + +// Routine Description: +// - Prepares internal structures for a painting operation. +// Arguments: +// - +// Return Value: +// - S_OK if we started to paint. S_FALSE if we didn't need to paint. +[[nodiscard]] HRESULT UiaEngine::StartPaint() noexcept +{ + RETURN_HR_IF(S_FALSE, !_isEnabled); + + // If there's nothing to do, quick return + bool somethingToDo = false; + + if (_isEnabled) + { + somethingToDo = false; + + if (somethingToDo) + { + _isPainting = true; + } + } + + return S_OK; +} + +// Routine Description: +// - Ends batch drawing and notifies automation clients of updated regions +// Arguments: +// - +// Return Value: +// - S_OK, else an appropriate HRESULT for failing to allocate or write. +[[nodiscard]] HRESULT UiaEngine::EndPaint() noexcept +{ + RETURN_HR_IF(E_INVALIDARG, !_isPainting); // invalid to end paint when we're not painting + + if (_isEnabled) + { + _isPainting = false; + + // fire UIA events here + } + + return S_OK; +} + +// Routine Description: +// - Used to perform longer running presentation steps outside the lock so the +// other threads can continue. +// - Not currently used by UiaEngine. +// Arguments: +// - +// Return Value: +// - S_FALSE since we do nothing. +[[nodiscard]] HRESULT UiaEngine::Present() noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - This is currently unused. +// Arguments: +// - +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::ScrollFrame() noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Paints the background of the invalid area of the frame. +// For UIA, this doesn't mean anything. So do nothing. +// Arguments: +// - +// Return Value: +// - S_FALSE since we do nothing +[[nodiscard]] HRESULT UiaEngine::PaintBackground() noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Places one line of text onto the screen at the given position +// Arguments: +// - clusters - Iterable collection of cluster information (text and columns it should consume) +// - coord - Character coordinate position in the cell grid +// - fTrimLeft - Whether or not to trim off the left half of a double wide character +// Return Value: +// - E_NOTIMPL +[[nodiscard]] HRESULT UiaEngine::PaintBufferLine(std::basic_string_view const /*clusters*/, + COORD const /*coord*/, + const bool /*trimLeft*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Paints lines around cells (draws in pieces of the grid) +// For UIA, this doesn't mean anything. So do nothing. +// Arguments: +// - lines - +// - color - +// - cchLine - +// - coordTarget - +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::PaintBufferGridLines(GridLines const /*lines*/, + COLORREF const /*color*/, + size_t const /*cchLine*/, + COORD const /*coordTarget*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Reads the selected area, selection mode, and active screen buffer +// from the global properties and dispatches a GDI invert on the selected text area. +// Because the selection is the responsibility of the terminal, and not the +// host, render nothing. +// Arguments: +// - rect - Rectangle to invert or highlight to make the selection area +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::PaintSelection(const SMALL_RECT /*rect*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Draws the cursor on the screen +// Arguments: +// - options - Packed options relevant to how to draw the cursor +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::PaintCursor(const IRenderEngine::CursorOptions& /*options*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Updates the default brush colors used for drawing +// - Not currently used by UiaEngine. +// Arguments: +// - colorForeground - +// - colorBackground - +// - legacyColorAttribute - +// - isBold - +// - isSettingDefaultBrushes - +// Return Value: +// - S_FALSE since we do nothing +[[nodiscard]] HRESULT UiaEngine::UpdateDrawingBrushes(const COLORREF /*colorForeground*/, + const COLORREF /*colorBackground*/, + const WORD /*legacyColorAttribute*/, + const ExtendedAttributes /*extendedAttrs*/, + const bool /*isSettingDefaultBrushes*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Updates the font used for drawing +// Arguments: +// - pfiFontInfoDesired - +// - fiFontInfo - +// Return Value: +// - S_FALSE since we do nothing +[[nodiscard]] HRESULT UiaEngine::UpdateFont(const FontInfoDesired& /*pfiFontInfoDesired*/, FontInfo& /*fiFontInfo*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Sets the DPI in this renderer +// - Not currently used by UiaEngine. +// Arguments: +// - iDpi - DPI +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::UpdateDpi(int const /*iDpi*/) noexcept +{ + return E_NOTIMPL; +} + +// Method Description: +// - This method will update our internal reference for how big the viewport is. +// Arguments: +// - srNewViewport - The bounds of the new viewport. +// Return Value: +// - HRESULT S_OK +[[nodiscard]] HRESULT UiaEngine::UpdateViewport(const SMALL_RECT /*srNewViewport*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Currently unused by this renderer +// Arguments: +// - pfiFontInfoDesired - +// - pfiFontInfo - +// - iDpi - +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::GetProposedFont(const FontInfoDesired& /*pfiFontInfoDesired*/, + FontInfo& /*pfiFontInfo*/, + int const /*iDpi*/) noexcept +{ + return S_FALSE; +} + +// Routine Description: +// - Gets the area that we currently believe is dirty within the character cell grid +// - Not currently used by UiaEngine. +// Arguments: +// - +// Return Value: +// - Rectangle describing dirty area in characters. +[[nodiscard]] SMALL_RECT UiaEngine::GetDirtyRectInChars() noexcept +{ + return Viewport::Empty().ToInclusive(); +} + +// Routine Description: +// - Gets the current font size +// Arguments: +// - pFontSize - Filled with the font size. +// Return Value: +// - S_OK +[[nodiscard]] HRESULT UiaEngine::GetFontSize(_Out_ COORD* const /*pFontSize*/) noexcept +{ + return E_NOTIMPL; +} + +// Routine Description: +// - Currently unused by this renderer. +// Arguments: +// - glyph - The glyph run to process for column width. +// - pResult - True if it should take two columns. False if it should take one. +// Return Value: +// - E_NOTIMPL +[[nodiscard]] HRESULT UiaEngine::IsGlyphWideByFont(const std::wstring_view /*glyph*/, _Out_ bool* const /*pResult*/) noexcept +{ + return E_NOTIMPL; +} + +// Method Description: +// - Updates the window's title string. +// - Currently unused by this renderer. +// Arguments: +// - newTitle: the new string to use for the title of the window +// Return Value: +// - S_FALSE +[[nodiscard]] HRESULT UiaEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept +{ + return S_FALSE; +} diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp new file mode 100644 index 0000000000..1e93e8ba8e --- /dev/null +++ b/src/renderer/uia/UiaRenderer.hpp @@ -0,0 +1,83 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- UiaRenderer.hpp + +Abstract: +- This is the definition of the UIA specific implementation of the renderer +- It keeps track of what regions of the display have changed and notifies automation clients. + +Author(s): +- Carlos Zamora (CaZamor) Sep-2019 +--*/ + +#pragma once + +#include "../../renderer/inc/RenderEngineBase.hpp" + +#include "../../types/inc/Viewport.hpp" + +namespace Microsoft::Console::Render +{ + class UiaEngine final : public RenderEngineBase + { + public: + UiaEngine() noexcept; + + // Only one UiaEngine may present information at a time. + // This ensures that an automation client isn't overwhelmed + // by events when there are multiple TermControls + [[nodiscard]] HRESULT Enable() noexcept; + [[nodiscard]] HRESULT Disable() noexcept; + + // IRenderEngine Members + [[nodiscard]] HRESULT StartPaint() noexcept override; + [[nodiscard]] HRESULT EndPaint() noexcept override; + [[nodiscard]] HRESULT Present() noexcept override; + + [[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept override; + + [[nodiscard]] HRESULT ScrollFrame() noexcept override; + + [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; + [[nodiscard]] HRESULT InvalidateCursor(const COORD* const pcoordCursor) noexcept override; + [[nodiscard]] HRESULT InvalidateSystem(const RECT* const prcDirtyClient) noexcept override; + [[nodiscard]] HRESULT InvalidateSelection(const std::vector& rectangles) noexcept override; + [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; + [[nodiscard]] HRESULT InvalidateAll() noexcept override; + [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; + + [[nodiscard]] HRESULT PaintBackground() noexcept override; + [[nodiscard]] HRESULT PaintBufferLine(std::basic_string_view const clusters, + COORD const coord, + bool const fTrimLeft) noexcept override; + [[nodiscard]] HRESULT PaintBufferGridLines(GridLines const lines, COLORREF const color, size_t const cchLine, COORD const coordTarget) noexcept override; + [[nodiscard]] HRESULT PaintSelection(const SMALL_RECT rect) noexcept override; + + [[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override; + + [[nodiscard]] HRESULT UpdateDrawingBrushes(const COLORREF colorForeground, + const COLORREF colorBackground, + const WORD legacyColorAttribute, + const ExtendedAttributes extendedAttrs, + const bool isSettingDefaultBrushes) noexcept override; + [[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo) noexcept override; + [[nodiscard]] HRESULT UpdateDpi(int const iDpi) noexcept override; + [[nodiscard]] HRESULT UpdateViewport(const SMALL_RECT srNewViewport) noexcept override; + + [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override; + + [[nodiscard]] SMALL_RECT GetDirtyRectInChars() noexcept override; + [[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override; + [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; + + protected: + [[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept override; + + private: + bool _isEnabled; + bool _isPainting; + }; +} diff --git a/src/renderer/uia/lib/uia.vcxproj b/src/renderer/uia/lib/uia.vcxproj new file mode 100644 index 0000000000..1bb36777bc --- /dev/null +++ b/src/renderer/uia/lib/uia.vcxproj @@ -0,0 +1,24 @@ + + + + + + Create + + + + + + + + + {48D21369-3D7B-4431-9967-24E81292CF63} + Win32Proj + uia + RendererUia + ConRenderUia + + + + + diff --git a/src/renderer/uia/precomp.cpp b/src/renderer/uia/precomp.cpp new file mode 100644 index 0000000000..c51e9b31b2 --- /dev/null +++ b/src/renderer/uia/precomp.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" diff --git a/src/renderer/uia/precomp.h b/src/renderer/uia/precomp.h new file mode 100644 index 0000000000..ce87349d9d --- /dev/null +++ b/src/renderer/uia/precomp.h @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "LibraryIncludes.h" + +#include + +#pragma hdrstop