From 96496d8154113ae9e0bcbe47b4bbaf33a0d4e961 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 29 Jul 2019 15:21:15 -0700 Subject: [PATCH 001/154] Accessibility: Set-up UIA Tree (#1691) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **The Basics of Accessibility** - [What is a User Interaction Automation (UIA) Tree?](https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-tree-overview) - Other projects (i.e.: Narrator) can take advantage of this UIA tree and are used to present information within it. - Some things like XAML already have a UIA Tree. So some UIA tree navigation and features are already there. It's just a matter of getting them hooked up and looking right. **Accessibility in our Project** There's a few important classes... regarding Accessibility... - **WindowUiaProvider**: This sets up the UIA tree for a window. So this is the top-level for the UIA tree. - **ScreenInfoUiaProvider**: This sets up the UIA tree for a terminal buffer. - **UiaTextRange**: This is essential to interacting with the UIA tree for the terminal buffer. Actually gets portions of the buffer and presents them. regarding the Windows Terminal window... - **BaseWindow**: The foundation to a window. Deals with HWNDs and that kind of stuff. - **IslandWindow**: This extends `BaseWindow` and is actually what holds our Windows Terminal - **NonClientIslandWindow**: An extension of the `IslandWindow` regarding ConHost... - **IConsoleWindow**: This is an interface for the console window. - **Window**: This is the actual window for ConHost. Extends `IConsoleWindow` - `IConsoleWindow` changes: - move into `Microsoft::Console::Types` (a shared space) - Have `IslandWindow` extend it - `WindowUiaProvider` changes: - move into `Microsoft::Console::Types` (a shared space) - Hook up `WindowUiaProvider` to IslandWindow (yay! we now have a tree) ### Changes to the WindowUiaProvider As mentioned earlier, the WindowUiaProvider is the top-level UIA provider for our projects. To reuse as much code as possible, I created `Microsoft::Console::Types::WindowUiaProviderBase`. Any existing functions that reference a `ScreenInfoUiaProvider` were virtual-ized. In each project, a `WindowUiaProvider : WindowUiaProviderBase` was created to define those virtual functions. Note that that will be the main difference between ConHost and Windows Terminal moving forward: how many TextBuffers are on the screen. So, ConHost should be the same as before, with only one `ScreenInfoUiaProvider`, whereas Windows Terminal needs to (1) update which one is on the screen and (2) may have multiple on the screen. 🚨 Windows Terminal doesn't have the `ScreenInfoUiaProvider` hooked up yet. We'll have all the XAML elements in the UIA tree. But, since `TermControl` is a custom XAML Control, I need to hook up the `ScreenInfoUiaProvider` to it. This work will be done in a new PR and resolve GitHub Issue #1352. ### Moved to `Microsoft::Console::Types` These files got moved to a shared area so that they can be used by both ConHost and Windows Terminal. This means that any references to the `ServiceLocator` had to be removed. - `IConsoleWindow` - Windows Terminal: `IslandWindow : IConsoleWindow` - `ScreenInfoUiaProvider` - all references to `ServiceLocator` and `SCREEN_INFORMATION` were removed. `IRenderData` was used to accomplish this. Refer to next section for more details. - `UiaTextRange` - all references to `ServiceLocator` and `SCREEN_INFORMATION` were removed. `IRenderData` was used to accomplish this. Refer to next section for more details. - since most of the functions were `static`, that means that an `IRenderData` had to be added into most of them. ### Changes to IRenderData Since `IRenderData` is now being used to abstract out `ServiceLocator` and `SCREEN_INFORMATION`, I had to add a few functions here: - `bool IsAreaSelected()` - `void ClearSelection()` - `void SelectNewRegion(...)` - `HRESULT SearchForText(...)` `SearchForText()` is a problem here. The overall new design is great! But Windows Terminal doesn't have a way to search for text in the buffer yet, whereas ConHost does. So I'm punting on this issue for now. It looks nasty, but just look at all the other pretty things here. :) --- src/cascadia/TerminalCore/Terminal.hpp | 18 +- .../TerminalCore/TerminalSelection.cpp | 2 +- .../TerminalCore/terminalrenderdata.cpp | 27 + src/cascadia/WindowsTerminal/BaseWindow.h | 30 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 24 + src/cascadia/WindowsTerminal/IslandWindow.h | 40 +- .../WindowsTerminal/WindowUiaProvider.cpp | 163 ++++ .../WindowsTerminal/WindowUiaProvider.hpp | 53 ++ .../WindowsTerminal/WindowsTerminal.vcxproj | 2 + src/cascadia/WindowsTerminal/pch.h | 1 + src/host/renderData.cpp | 90 +++ src/host/renderData.hpp | 16 + src/host/screenInfo.hpp | 2 +- src/host/scrolling.cpp | 1 + src/host/selection.cpp | 1 + src/host/selection.hpp | 2 +- src/host/srvinit.cpp | 2 +- src/host/tracing.cpp | 59 +- src/host/tracing.hpp | 17 +- src/interactivity/base/ServiceLocator.cpp | 1 + .../base/lib/InteractivityBase.vcxproj | 1 - .../lib/InteractivityBase.vcxproj.filters | 6 +- src/interactivity/inc/ServiceLocator.hpp | 4 +- .../win32/AccessibilityNotifier.cpp | 1 + src/interactivity/win32/WindowMetrics.cpp | 4 +- src/interactivity/win32/find.cpp | 2 +- src/interactivity/win32/lib/win32.LIB.vcxproj | 4 - .../win32/lib/win32.LIB.vcxproj.filters | 19 +- .../UiaTextRangeTests.cpp | 185 ++--- src/interactivity/win32/window.cpp | 6 +- src/interactivity/win32/window.hpp | 6 +- src/interactivity/win32/windowUiaProvider.cpp | 283 +------ src/interactivity/win32/windowUiaProvider.hpp | 116 +-- src/interactivity/win32/windowproc.cpp | 7 +- src/renderer/inc/IRenderData.hpp | 18 + .../inc => types}/IConsoleWindow.hpp | 13 +- src/types/IUiaWindow.h | 31 + .../ScreenInfoUiaProvider.cpp} | 180 +++-- .../ScreenInfoUiaProvider.h} | 35 +- .../win32 => types}/UiaTextRange.cpp | 727 +++++++++--------- .../win32 => types}/UiaTextRange.hpp | 137 ++-- src/types/WindowUiaProviderBase.cpp | 238 ++++++ src/types/WindowUiaProviderBase.hpp | 139 ++++ src/types/lib/types.vcxproj | 12 +- src/types/lib/types.vcxproj.filters | 120 +-- src/types/precomp.h | 36 +- 46 files changed, 1732 insertions(+), 1149 deletions(-) create mode 100644 src/cascadia/WindowsTerminal/WindowUiaProvider.cpp create mode 100644 src/cascadia/WindowsTerminal/WindowUiaProvider.hpp rename src/{interactivity/inc => types}/IConsoleWindow.hpp (81%) create mode 100644 src/types/IUiaWindow.h rename src/{interactivity/win32/screenInfoUiaProvider.cpp => types/ScreenInfoUiaProvider.cpp} (72%) rename src/{interactivity/win32/screenInfoUiaProvider.hpp => types/ScreenInfoUiaProvider.h} (81%) rename src/{interactivity/win32 => types}/UiaTextRange.cpp (70%) rename src/{interactivity/win32 => types}/UiaTextRange.hpp (68%) create mode 100644 src/types/WindowUiaProviderBase.cpp create mode 100644 src/types/WindowUiaProviderBase.hpp diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 6e3064bd339..b41a0a9f46c 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -101,6 +101,23 @@ class Microsoft::Terminal::Core::Terminal final : const std::vector GetOverlays() const noexcept override; const bool IsGridLineDrawingAllowed() noexcept override; std::vector GetSelectionRects() noexcept override; + bool IsAreaSelected() const override; + void ClearSelection() override; + void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; + + // TODO GitHub #605: Search functionality + // For now, just adding it here to make UiaTextRange easier to create (Accessibility) + // We should actually abstract this out better once Windows Terminal has Search + HRESULT SearchForText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, + unsigned int _start, + unsigned int _end, + std::function _coordToEndpoint, + std::function _endpointToCoord, + std::function Clone) override; + const std::wstring GetConsoleTitle() const noexcept override; void LockConsole() noexcept override; void UnlockConsole() noexcept override; @@ -122,7 +139,6 @@ class Microsoft::Terminal::Core::Terminal final : void SetSelectionAnchor(const COORD position); void SetEndSelectionPosition(const COORD position); void SetBoxSelection(const bool isEnabled) noexcept; - void ClearSelection() noexcept; const std::wstring RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const; #pragma endregion diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 6b2f381f028..4d968f8f72f 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -210,7 +210,7 @@ void Terminal::SetBoxSelection(const bool isEnabled) noexcept // Method Description: // - clear selection data and disable rendering it -void Terminal::ClearSelection() noexcept +void Terminal::ClearSelection() { _selectionActive = false; _selectionAnchor = { 0, 0 }; diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index b55c03ac948..b32d0561ce5 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -117,6 +117,33 @@ std::vector Terminal::GetSelectionRects() n return result; } +bool Terminal::IsAreaSelected() const +{ + return _selectionActive; +} + +void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd) +{ + SetSelectionAnchor(coordStart); + SetEndSelectionPosition(coordEnd); +} + +// TODO GitHub #605: Search functionality +// For now, just adding it here to make UiaTextRange easier to create (Accessibility) +// We should actually abstract this out better once Windows Terminal has Search +HRESULT Terminal::SearchForText(_In_ BSTR /*text*/, + _In_ BOOL /*searchBackward*/, + _In_ BOOL /*ignoreCase*/, + _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/, + unsigned int /*_start*/, + unsigned int /*_end*/, + std::function /*_coordToEndpoint*/, + std::function /*_endpointToCoord*/, + std::function /*Clone*/) +{ + return E_NOTIMPL; +} + const std::wstring Terminal::GetConsoleTitle() const noexcept { return _title; diff --git a/src/cascadia/WindowsTerminal/BaseWindow.h b/src/cascadia/WindowsTerminal/BaseWindow.h index 227c738db10..1ae388dcd37 100644 --- a/src/cascadia/WindowsTerminal/BaseWindow.h +++ b/src/cascadia/WindowsTerminal/BaseWindow.h @@ -3,11 +3,16 @@ #pragma once +#include "..\types\IConsoleWindow.hpp" +#include "..\types\WindowUiaProviderBase.hpp" + // Custom window messages #define CM_UPDATE_TITLE (WM_USER) #include +using namespace Microsoft::Console::Types; + template class BaseWindow { @@ -51,6 +56,11 @@ class BaseWindow return HandleDpiChange(_window.get(), wparam, lparam); } + case WM_GETOBJECT: + { + return HandleGetObject(_window.get(), wparam, lparam); + } + case WM_DESTROY: { PostQuitMessage(0); @@ -121,6 +131,22 @@ class BaseWindow return 0; } + [[nodiscard]] LRESULT HandleGetObject(const HWND hWnd, const WPARAM wParam, const LPARAM lParam) + { + LRESULT retVal = 0; + + // If we are receiving a request from Microsoft UI Automation framework, then return the basic UIA COM interface. + if (static_cast(lParam) == static_cast(UiaRootObjectId)) + { + retVal = UiaReturnRawElementProvider(hWnd, wParam, lParam, _GetUiaProvider()); + } + // Otherwise, return 0. We don't implement MS Active Accessibility (the other framework that calls WM_GETOBJECT). + + return retVal; + } + + virtual IRawElementProviderSimple* _GetUiaProvider() = 0; + virtual void OnResize(const UINT width, const UINT height) = 0; virtual void OnMinimize() = 0; virtual void OnRestore() = 0; @@ -135,7 +161,7 @@ class BaseWindow HWND GetHandle() const noexcept { return _window.get(); - }; + } float GetCurrentDpiScale() const noexcept { @@ -187,7 +213,7 @@ class BaseWindow { _title = newTitle; PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast(nullptr)); - }; + } protected: using base_type = BaseWindow; diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index d63715442fa..142c9118509 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -179,6 +179,30 @@ void IslandWindow::OnSize(const UINT width, const UINT height) return base_type::MessageHandler(message, wparam, lparam); } +// Routine Description: +// - Creates/retrieves a handle to the UI Automation provider COM interfaces +// Arguments: +// - +// Return Value: +// - Pointer to UI Automation provider class/interfaces. +IRawElementProviderSimple* IslandWindow::_GetUiaProvider() +{ + if (nullptr == _pUiaProvider) + { + try + { + _pUiaProvider = WindowUiaProvider::Create(this); + } + catch (...) + { + LOG_HR(wil::ResultFromCaughtException()); + _pUiaProvider = nullptr; + } + } + + return _pUiaProvider; +} + // Method Description: // - Called when the window has been resized (or maximized) // Arguments: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 436a3cf9f1f..b7d853ee70c 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -3,10 +3,14 @@ #include "pch.h" #include "BaseWindow.h" +#include "../types/IUiaWindow.h" +#include "WindowUiaProvider.hpp" #include #include -class IslandWindow : public BaseWindow +class IslandWindow : + public BaseWindow, + public IUiaWindow { public: IslandWindow() noexcept; @@ -17,6 +21,7 @@ class IslandWindow : public BaseWindow virtual void OnSize(const UINT width, const UINT height); [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; + IRawElementProviderSimple* _GetUiaProvider(); void OnResize(const UINT width, const UINT height) override; void OnMinimize() override; void OnRestore() override; @@ -29,6 +34,38 @@ class IslandWindow : public BaseWindow void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); +#pragma region IUiaWindow + void ChangeViewport(const SMALL_RECT NewWindow) + { + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // Relevant comment from zadjii-msft: + /* + In my head for designing this, I'd then have IslandWindow::ChangeViewport + call a callback that AppHost sets, where AppHost will then call into the + TerminalApp to have TerminalApp handle the ChangeViewport call. + (See IslandWindow::SetCreateCallback as an example of a similar + pattern we're using today.) That way, if someone else were trying + to resuse this, they could have their own AppHost (or TerminalApp + equivalent) handle the ChangeViewport call their own way. + */ + return; + }; + + HWND GetWindowHandle() const noexcept override + { + return BaseWindow::GetHandle(); + }; + + [[nodiscard]] HRESULT SignalUia(_In_ EVENTID id) override { return E_NOTIMPL; }; + [[nodiscard]] HRESULT UiaSetTextAreaFocus() override { return E_NOTIMPL; }; + + RECT GetWindowRect() const noexcept override + { + return BaseWindow::GetWindowRect(); + }; + +#pragma endregion + protected: void ForceResize() { @@ -38,6 +75,7 @@ class IslandWindow : public BaseWindow } HWND _interopWindowHandle; + WindowUiaProvider* _pUiaProvider; winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source; diff --git a/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp new file mode 100644 index 00000000000..d25f3b86b99 --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +#include "pch.h" + +#include "WindowUiaProvider.hpp" +#include "../types/ScreenInfoUiaProvider.h" + +#include "../host/renderData.hpp" + +WindowUiaProvider::WindowUiaProvider(Microsoft::Console::Types::IUiaWindow* baseWindow) : + WindowUiaProviderBase(baseWindow) +{ +} + +WindowUiaProvider::~WindowUiaProvider() +{ +} + +WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWindow* baseWindow) +{ + WindowUiaProvider* pWindowProvider = nullptr; + Microsoft::Console::Types::ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; + try + { + pWindowProvider = new WindowUiaProvider(baseWindow); + + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + /*Globals& g = ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = g.getConsoleInformation(); + Microsoft::Console::Render::IRenderData* renderData = &gci.renderData; + + pScreenInfoProvider = new Microsoft::Console::Types::ScreenInfoUiaProvider(renderData, pWindowProvider); + pWindowProvider->_pScreenInfoProvider = pScreenInfoProvider; + */ + + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(pWindowProvider, ApiCall::Create, nullptr); + + return pWindowProvider; + } + catch (...) + { + if (nullptr != pWindowProvider) + { + pWindowProvider->Release(); + } + + if (nullptr != pScreenInfoProvider) + { + pScreenInfoProvider->Release(); + } + + LOG_CAUGHT_EXCEPTION(); + + return nullptr; + } +} + +[[nodiscard]] HRESULT WindowUiaProvider::SetTextAreaFocus() +{ + try + { + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + //return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId); + return E_NOTIMPL; + } + CATCH_RETURN(); +} + +[[nodiscard]] HRESULT WindowUiaProvider::Signal(_In_ EVENTID id) +{ + HRESULT hr = S_OK; + + // ScreenInfoUiaProvider is responsible for signaling selection + // changed events and text changed events + if (id == UIA_Text_TextSelectionChangedEventId || + id == UIA_Text_TextChangedEventId) + { + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + /*if (_pScreenInfoProvider) + { + hr = _pScreenInfoProvider->Signal(id); + } + else + { + hr = E_POINTER; + }*/ + hr = E_POINTER; + return hr; + } + + if (_signalEventFiring.find(id) != _signalEventFiring.end() && + _signalEventFiring[id] == true) + { + return hr; + } + + try + { + _signalEventFiring[id] = true; + } + CATCH_RETURN(); + + IRawElementProviderSimple* pProvider = static_cast(this); + hr = UiaRaiseAutomationEvent(pProvider, id); + _signalEventFiring[id] = false; + + return hr; +} + +#pragma region IRawElementProviderFragment + +IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + *ppProvider = nullptr; + HRESULT hr = S_OK; + + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + /*if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) + { + *ppProvider = _pScreenInfoProvider; + (*ppProvider)->AddRef(); + + // signal that the focus changed + LOG_IF_FAILED(_pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId)); + }*/ + + // For the other directions (parent, next, previous) the default of nullptr is correct + return hr; +} + +IFACEMETHODIMP WindowUiaProvider::SetFocus() +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + return Signal(UIA_AutomationFocusChangedEventId); +} +#pragma endregion + +#pragma region IRawElementProviderFragmentRoot + +IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, + _In_ double /*y*/, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + /**ppProvider = _pScreenInfoProvider; + (*ppProvider)->AddRef();*/ + + return S_OK; +} + +IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + //return _pScreenInfoProvider->QueryInterface(IID_PPV_ARGS(ppProvider)); + return S_OK; +} + +#pragma endregion diff --git a/src/cascadia/WindowsTerminal/WindowUiaProvider.hpp b/src/cascadia/WindowsTerminal/WindowUiaProvider.hpp new file mode 100644 index 00000000000..835ba87211b --- /dev/null +++ b/src/cascadia/WindowsTerminal/WindowUiaProvider.hpp @@ -0,0 +1,53 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- windowUiaProvider.hpp + +Abstract: +- This module provides UI Automation access to the console window to + support both automation tests and accessibility (screen reading) + applications. +- Based on examples, sample code, and guidance from + https://msdn.microsoft.com/en-us/library/windows/desktop/ee671596(v=vs.85).aspx + +Author(s): +- Michael Niksa (MiNiksa) 2017 +- Austin Diviness (AustDi) 2017 +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "../types/WindowUiaProviderBase.hpp" +#include "../types/IUiaWindow.h" + +class WindowUiaProvider final : + public Microsoft::Console::Types::WindowUiaProviderBase +{ +public: + static WindowUiaProvider* Create(Microsoft::Console::Types::IUiaWindow* baseWindow); + + [[nodiscard]] HRESULT Signal(_In_ EVENTID id) override; + [[nodiscard]] HRESULT SetTextAreaFocus() override; + + // IRawElementProviderFragment methods + IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP SetFocus() override; + + // IRawElementProviderFragmentRoot methods + IFACEMETHODIMP ElementProviderFromPoint(_In_ double x, + _In_ double y, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + +protected: + const OLECHAR* AutomationIdPropertyName = L"Terminal Window"; + const OLECHAR* ProviderDescriptionPropertyName = L"Microsoft Windows Terminal Window"; + +private: + WindowUiaProvider(Microsoft::Console::Types::IUiaWindow* baseWindow); + ~WindowUiaProvider(); +}; diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index 7cf65caa7d2..9f7f13b7b0b 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -53,6 +53,7 @@ + @@ -62,6 +63,7 @@ + diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 9537c74693b..0ae28bc43b8 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -24,6 +24,7 @@ Module Name: #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) #include +#include #include #include #include diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 7b747f62163..7f9910a9dc7 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -9,6 +9,8 @@ #include "handle.h" #include "..\interactivity\inc\ServiceLocator.hpp" +#include "search.h" +#include "..\types\UiaTextRange.hpp" #pragma hdrstop @@ -246,6 +248,94 @@ std::vector RenderData::GetSelectionRects() noexcept return result; } +// Routine Description: +// - Determines whether the selection area is empty. +// Arguments: +// - +// Return Value: +// - True if the selection variables contain valid selection data. False otherwise. +bool RenderData::IsAreaSelected() const +{ + return Selection::Instance().IsAreaSelected(); +} + +// Routine Description: +// - If a selection exists, clears it and restores the state. +// Will also unblock a blocked write if one exists. +// Arguments: +// - (Uses global state) +// Return Value: +// - +void RenderData::ClearSelection() +{ + Selection::Instance().ClearSelection(); +} + +// Routine Description: +// - Resets the current selection and selects a new region from the start to end coordinates +// Arguments: +// - coordStart - Position to start selection area from +// - coordEnd - Position to select up to +// Return Value: +// - +void RenderData::SelectNewRegion(const COORD coordStart, const COORD coordEnd) +{ + Selection::Instance().SelectNewRegion(coordStart, coordEnd); +} + +// TODO GitHub #605: Search functionality +// For now, just adding it here to make UiaTextRange easier to create (Accessibility) +// We should actually abstract this out better once Windows Terminal has Search +HRESULT RenderData::SearchForText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, + unsigned int _start, + unsigned int _end, + std::function _coordToEndpoint, + std::function _endpointToCoord, + std::function Clone) +{ + typedef unsigned int Endpoint; + + const std::wstring wstr{ text, SysStringLen(text) }; + const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive; + + auto searchDirection = Search::Direction::Forward; + Endpoint searchAnchor = _start; + if (searchBackward) + { + searchDirection = Search::Direction::Backward; + searchAnchor = _end; + } + + CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_HR_IF(E_POINTER, !gci.HasActiveOutputBuffer()); + const auto& screenInfo = gci.GetActiveOutputBuffer().GetActiveBuffer(); + + Search searcher{ screenInfo, wstr, searchDirection, sensitivity, _endpointToCoord(this, searchAnchor) }; + + HRESULT hr = S_OK; + if (searcher.FindNext()) + { + const auto foundLocation = searcher.GetFoundLocation(); + const Endpoint start = _coordToEndpoint(this, foundLocation.first); + const Endpoint end = _coordToEndpoint(this, foundLocation.second); + // make sure what was found is within the bounds of the current range + if ((searchDirection == Search::Direction::Forward && end < _end) || + (searchDirection == Search::Direction::Backward && start > _start)) + { + hr = Clone(ppRetVal); + if (SUCCEEDED(hr)) + { + UiaTextRange& range = static_cast(**ppRetVal); + range.SetRangeValues(start, end, false); + } + } + } + return hr; +} + // Routine Description: // - Checks the user preference as to whether grid line drawing is allowed around the edges of each cell. // - This is for backwards compatibility with old behaviors in the legacy console. diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 359d4348ff4..668a5f9f826 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -41,6 +41,22 @@ class RenderData final : public Microsoft::Console::Render::IRenderData const bool IsGridLineDrawingAllowed() noexcept override; std::vector GetSelectionRects() noexcept override; + bool IsAreaSelected() const override; + void ClearSelection() override; + void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; + + // TODO GitHub #605: Search functionality + // For now, just adding it here to make UiaTextRange easier to create (Accessibility) + // We should actually abstract this out better once Windows Terminal has Search + HRESULT SearchForText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, + unsigned int _start, + unsigned int _end, + std::function _coordToEndpoint, + std::function _endpointToCoord, + std::function Clone); const std::wstring GetConsoleTitle() const noexcept override; diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index d44a16e05d6..fc6cbbf07c1 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -39,7 +39,6 @@ Revision History: #include "../server/ObjectHeader.h" #include "../interactivity/inc/IAccessibilityNotifier.hpp" -#include "../interactivity/inc/IConsoleWindow.hpp" #include "../interactivity/inc/IWindowMetrics.hpp" #include "../inc/ITerminalOutputConnection.hpp" @@ -48,6 +47,7 @@ Revision History: #include "../renderer/inc/FontInfoDesired.hpp" #include "../types/inc/Viewport.hpp" +#include "../types/IConsoleWindow.hpp" class ConversionAreaInfo; // forward decl window. circular reference class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console::IIoProvider diff --git a/src/host/scrolling.cpp b/src/host/scrolling.cpp index 1c344b5e594..e45d9aece7e 100644 --- a/src/host/scrolling.cpp +++ b/src/host/scrolling.cpp @@ -11,6 +11,7 @@ using Microsoft::Console::VirtualTerminal::StateMachine; using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Types; ULONG Scrolling::s_ucWheelScrollLines = 0; ULONG Scrolling::s_ucWheelScrollChars = 0; diff --git a/src/host/selection.cpp b/src/host/selection.cpp index 6dee0d808ad..5a361595f6f 100644 --- a/src/host/selection.cpp +++ b/src/host/selection.cpp @@ -10,6 +10,7 @@ #include "../interactivity/inc/ServiceLocator.hpp" using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Types; std::unique_ptr Selection::_instance; diff --git a/src/host/selection.hpp b/src/host/selection.hpp index aee97594f34..1d8733479b0 100644 --- a/src/host/selection.hpp +++ b/src/host/selection.hpp @@ -21,7 +21,7 @@ Revision History: #include "input.h" #include "..\interactivity\inc\IAccessibilityNotifier.hpp" -#include "..\interactivity\inc\IConsoleWindow.hpp" +#include "..\types\IConsoleWindow.hpp" class Selection { diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index be91304d2bf..fe32f13d245 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -203,7 +203,7 @@ static bool s_IsOnDesktop() if (fRecomputeOwner) { - IConsoleWindow* pWindow = ServiceLocator::LocateConsoleWindow(); + Microsoft::Console::Types::IConsoleWindow* pWindow = ServiceLocator::LocateConsoleWindow(); if (pWindow != nullptr) { pWindow->SetOwner(); diff --git a/src/host/tracing.cpp b/src/host/tracing.cpp index 4b80090ae8b..a3ecf287e12 100644 --- a/src/host/tracing.cpp +++ b/src/host/tracing.cpp @@ -3,10 +3,12 @@ #include "precomp.h" #include "tracing.hpp" -#include "../interactivity/win32/UiaTextRange.hpp" -#include "../interactivity/win32/screenInfoUiaProvider.hpp" -#include "../interactivity/win32/windowUiaProvider.hpp" +#include "../types/UiaTextRange.hpp" +#include "../types/ScreenInfoUiaProvider.h" +#include "../types/WindowUiaProviderBase.hpp" + +using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity::Win32; enum TraceKeywords @@ -245,7 +247,7 @@ void Tracing::s_TraceApi(const CONSOLE_WRITECONSOLEOUTPUTSTRING_MSG* const a) TraceLoggingKeyword(TraceKeywords::API)); } -void Tracing::s_TraceWindowViewport(const Microsoft::Console::Types::Viewport& viewport) +void Tracing::s_TraceWindowViewport(const Viewport& viewport) { TraceLoggingWrite( g_hConhostV2EventTraceProvider, @@ -397,6 +399,8 @@ void __stdcall Tracing::TraceFailure(const wil::FailureInfo& failure) noexcept TraceLoggingLevel(WINEVENT_LEVEL_ERROR)); } +// TODO GitHub #1914: Re-attach Tracing to UIA Tree +#if 0 void Tracing::s_TraceUia(const UiaTextRange* const range, const UiaTextRangeTracing::ApiCall apiCall, const UiaTextRangeTracing::IApiMsg* const apiMsg) @@ -729,7 +733,7 @@ void Tracing::s_TraceUia(const UiaTextRange* const range, } } -void Tracing::s_TraceUia(const ScreenInfoUiaProvider* const /*pProvider*/, +void Tracing::s_TraceUia(const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProvider* const /*pProvider*/, const ScreenInfoUiaProviderTracing::ApiCall apiCall, const ScreenInfoUiaProviderTracing::IApiMsg* const apiMsg) { @@ -898,22 +902,22 @@ void Tracing::s_TraceUia(const ScreenInfoUiaProvider* const /*pProvider*/, } } -void Tracing::s_TraceUia(const WindowUiaProvider* const /*pProvider*/, - const WindowUiaProviderTracing::ApiCall apiCall, - const WindowUiaProviderTracing::IApiMsg* const apiMsg) +void Tracing::s_TraceUia(const Microsoft::Console::Types::WindowUiaProvider* const /*pProvider*/, + const Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall apiCall, + const Microsoft::Console::Types::WindowUiaProviderTracing::IApiMsg* const apiMsg) { switch (apiCall) { - case WindowUiaProviderTracing::ApiCall::Create: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Create: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::Create", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::Signal: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Signal: { - const WindowUiaProviderTracing::ApiMessageSignal* const msg = static_cast(apiMsg); + const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMessageSignal* const msg = static_cast(apiMsg); const wchar_t* const eventName = _eventIdToString(msg->Signal); TraceLoggingWrite( g_hConhostV2EventTraceProvider, @@ -924,58 +928,58 @@ void Tracing::s_TraceUia(const WindowUiaProvider* const /*pProvider*/, TraceLoggingKeyword(TraceKeywords::UIA)); break; } - case WindowUiaProviderTracing::ApiCall::AddRef: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::AddRef: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::AddRef", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::Release: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Release: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::Release", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::QueryInterface: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::QueryInterface: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::QueryInterface", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetProviderOptions: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetProviderOptions: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetProviderOptions", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetPatternProvider: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetPatternProvider: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetPatternProvider", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetPropertyValue: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetPropertyValue: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetPropertyValue", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetHostRawElementProvider: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetHostRawElementProvider: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetHostRawElementProvider", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::Navigate: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::Navigate: { - const WindowUiaProviderTracing::ApiMsgNavigate* const msg = static_cast(apiMsg); + const Microsoft::Console::Types::WindowUiaProviderTracing::ApiMsgNavigate* const msg = static_cast(apiMsg); const wchar_t* const direction = _directionToString(msg->Direction); TraceLoggingWrite( g_hConhostV2EventTraceProvider, @@ -985,49 +989,49 @@ void Tracing::s_TraceUia(const WindowUiaProvider* const /*pProvider*/, TraceLoggingKeyword(TraceKeywords::UIA)); break; } - case WindowUiaProviderTracing::ApiCall::GetRuntimeId: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetRuntimeId: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetRuntimeId", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetBoundingRectangle: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetBoundingRectangle: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetBoundingRectangle", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetEmbeddedFragmentRoots: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetEmbeddedFragmentRoots: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetEmbeddedFragmentRoots", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::SetFocus: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::SetFocus: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::SetFocus", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetFragmentRoot: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetFragmentRoot: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetFragmentRoot", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::ElementProviderFromPoint: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::ElementProviderFromPoint: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::ElementProviderFromPoint", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TraceKeywords::UIA)); break; - case WindowUiaProviderTracing::ApiCall::GetFocus: + case Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall::GetFocus: TraceLoggingWrite( g_hConhostV2EventTraceProvider, "WindowUiaProvider::GetFocus", @@ -1038,6 +1042,7 @@ void Tracing::s_TraceUia(const WindowUiaProvider* const /*pProvider*/, break; } } +#endif const wchar_t* const Tracing::_textPatternRangeEndpointToString(int endpoint) { diff --git a/src/host/tracing.hpp b/src/host/tracing.hpp index a54b7c9bc2c..4f5d8b44fcd 100644 --- a/src/host/tracing.hpp +++ b/src/host/tracing.hpp @@ -38,14 +38,6 @@ namespace Microsoft::Console::Interactivity::Win32 enum class ApiCall; struct IApiMsg; } - - class WindowUiaProvider; - - namespace WindowUiaProviderTracing - { - enum class ApiCall; - struct IApiMsg; - } } #if DBG @@ -91,6 +83,8 @@ class Tracing static void __stdcall TraceFailure(const wil::FailureInfo& failure) noexcept; +// TODO GitHub #1914: Re-attach Tracing to UIA Tree +#if 0 static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::UiaTextRange* const range, const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::ApiCall apiCall, const Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing::IApiMsg* const apiMsg); @@ -99,9 +93,10 @@ class Tracing const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::ApiCall apiCall, const Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing::IApiMsg* const apiMsg); - static void s_TraceUia(const Microsoft::Console::Interactivity::Win32::WindowUiaProvider* const pProvider, - const Microsoft::Console::Interactivity::Win32::WindowUiaProviderTracing::ApiCall apiCall, - const Microsoft::Console::Interactivity::Win32::WindowUiaProviderTracing::IApiMsg* const apiMsg); + static void s_TraceUia(const Microsoft::Console::Types::WindowUiaProvider* const pProvider, + const Microsoft::Console::Types::WindowUiaProviderTracing::ApiCall apiCall, + const Microsoft::Console::Types::WindowUiaProviderTracing::IApiMsg* const apiMsg); +#endif private: static ULONG s_ulDebugFlag; diff --git a/src/interactivity/base/ServiceLocator.cpp b/src/interactivity/base/ServiceLocator.cpp index 858c25a027a..ef8411c5389 100644 --- a/src/interactivity/base/ServiceLocator.cpp +++ b/src/interactivity/base/ServiceLocator.cpp @@ -9,6 +9,7 @@ #pragma hdrstop +using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity; #pragma region Private Static Member Initialization diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj b/src/interactivity/base/lib/InteractivityBase.vcxproj index f8d69520075..57075e72c8c 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj @@ -23,7 +23,6 @@ - diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters index a47641e36e4..0266e68564b 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters @@ -50,9 +50,6 @@ Header Files - - Header Files - Header Files @@ -75,4 +72,7 @@ Header Files + + + \ No newline at end of file diff --git a/src/interactivity/inc/ServiceLocator.hpp b/src/interactivity/inc/ServiceLocator.hpp index fcd1f0936cd..d81d77d666b 100644 --- a/src/interactivity/inc/ServiceLocator.hpp +++ b/src/interactivity/inc/ServiceLocator.hpp @@ -16,13 +16,15 @@ Author(s): #pragma once #include "IInteractivityFactory.hpp" -#include "IConsoleWindow.hpp" +#include "../types/IConsoleWindow.hpp" #include "../../host/globals.h" #include #pragma hdrstop +using namespace Microsoft::Console::Types; + namespace Microsoft::Console::Interactivity { class ServiceLocator final diff --git a/src/interactivity/win32/AccessibilityNotifier.cpp b/src/interactivity/win32/AccessibilityNotifier.cpp index a6d99b18203..e7eeaaca6f0 100644 --- a/src/interactivity/win32/AccessibilityNotifier.cpp +++ b/src/interactivity/win32/AccessibilityNotifier.cpp @@ -8,6 +8,7 @@ #include "..\inc\ServiceLocator.hpp" #include "ConsoleControl.hpp" +using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity::Win32; void AccessibilityNotifier::NotifyConsoleCaretEvent(_In_ RECT rectangle) diff --git a/src/interactivity/win32/WindowMetrics.cpp b/src/interactivity/win32/WindowMetrics.cpp index b985faf8375..690e20f789b 100644 --- a/src/interactivity/win32/WindowMetrics.cpp +++ b/src/interactivity/win32/WindowMetrics.cpp @@ -96,7 +96,7 @@ RECT WindowMetrics::GetMaxWindowRectInPixels(const RECT* const prcSuggested, _Ou // NOTE: We must use the nearest monitor because sometimes the system moves the window around into strange spots while performing snap and Win+D operations. // Those operations won't work correctly if we use MONITOR_DEFAULTTOPRIMARY. - IConsoleWindow* pWindow = ServiceLocator::LocateConsoleWindow(); + auto pWindow = ServiceLocator::LocateConsoleWindow(); if (pWindow == nullptr || (TRUE != EqualRect(&rc, &rcZero))) { // For invalid window handles or when we were passed a non-zero suggestion rectangle, get the monitor from the rect. @@ -258,7 +258,7 @@ void WindowMetrics::ConvertRect(_Inout_ RECT* const prc, const ConvertRectangle DWORD dwStyle = 0; DWORD dwExStyle = 0; - IConsoleWindow* pWindow = ServiceLocator::LocateConsoleWindow(); + Microsoft::Console::Types::IConsoleWindow* pWindow = ServiceLocator::LocateConsoleWindow(); if (pWindow != nullptr) { dwStyle = GetWindowStyle(pWindow->GetWindowHandle()); diff --git a/src/interactivity/win32/find.cpp b/src/interactivity/win32/find.cpp index 91124cec480..e1702d586ae 100644 --- a/src/interactivity/win32/find.cpp +++ b/src/interactivity/win32/find.cpp @@ -86,7 +86,7 @@ INT_PTR CALLBACK FindDialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM l void DoFind() { Globals& g = ServiceLocator::LocateGlobals(); - IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow(); + Microsoft::Console::Types::IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow(); UnlockConsole(); if (pWindow != nullptr) diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj b/src/interactivity/win32/lib/win32.LIB.vcxproj index 659a554486d..8833c49c5f9 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj @@ -19,9 +19,7 @@ Create - - @@ -43,9 +41,7 @@ - - diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters index 900e08aa14e..015db990ff1 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters @@ -45,9 +45,6 @@ Source Files - - Source Files - Source Files @@ -69,15 +66,12 @@ Source Files - + Source Files Source Files - - Source Files - @@ -113,9 +107,6 @@ Header Files - - Header Files - Header Files @@ -134,14 +125,14 @@ Header Files - + Header Files Header Files - - Header Files - + + + \ No newline at end of file diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index f9314b5435e..1fb699c3bf4 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -6,14 +6,15 @@ #include "..\..\inc\consoletaeftemplates.hpp" #include "CommonState.hpp" -#include "UiaTextRange.hpp" +#include "..\types\UiaTextRange.hpp" +#include "..\host\renderData.hpp" #include "../../../buffer/out/textBuffer.hpp" using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; -using namespace Microsoft::Console::Interactivity::Win32; +using namespace Microsoft::Console::Types; // UiaTextRange takes an object that implements // IRawElementProviderSimple as a constructor argument. Making a real @@ -75,6 +76,7 @@ class UiaTextRangeTests SCREEN_INFORMATION* _pScreenInfo; TextBuffer* _pTextBuffer; UiaTextRange* _range; + RenderData* _pRenderData; TEST_METHOD_SETUP(MethodSetup) { @@ -88,6 +90,7 @@ class UiaTextRangeTests // set up pointers _pScreenInfo = &gci.GetActiveOutputBuffer(); _pTextBuffer = &_pScreenInfo->GetTextBuffer(); + _pRenderData = &gci.renderData; // fill text buffer with text for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i) @@ -102,6 +105,7 @@ class UiaTextRangeTests // set up default range _range = new UiaTextRange{ + _pRenderData, &_dummyProvider, 0, 0, @@ -121,6 +125,7 @@ class UiaTextRangeTests _pScreenInfo = nullptr; _pTextBuffer = nullptr; + _pRenderData = nullptr; return true; } @@ -134,24 +139,26 @@ class UiaTextRangeTests { // make a degenerate range and verify that it reports degenerate UiaTextRange degenerate{ + _pRenderData, &_dummyProvider, 20, 19, true }; VERIFY_IS_TRUE(degenerate.IsDegenerate()); - VERIFY_ARE_EQUAL(0u, degenerate._rowCountInRange()); + VERIFY_ARE_EQUAL(0u, degenerate._rowCountInRange(_pRenderData)); VERIFY_ARE_EQUAL(degenerate._start, degenerate._end); // make a non-degenerate range and verify that it reports as such UiaTextRange notDegenerate1{ + _pRenderData, &_dummyProvider, 20, 20, false }; VERIFY_IS_FALSE(notDegenerate1.IsDegenerate()); - VERIFY_ARE_EQUAL(1u, notDegenerate1._rowCountInRange()); + VERIFY_ARE_EQUAL(1u, notDegenerate1._rowCountInRange(_pRenderData)); } TEST_METHOD(CanCheckIfScreenInfoRowIsInViewport) @@ -212,7 +219,7 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (auto i = 0; i < 300; ++i) { - VERIFY_ARE_EQUAL(i / rowWidth, _range->_endpointToTextBufferRow(i)); + VERIFY_ARE_EQUAL(i / rowWidth, _range->_endpointToTextBufferRow(_pRenderData, i)); } } @@ -221,9 +228,9 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (unsigned int i = 0; i < 5; ++i) { - VERIFY_ARE_EQUAL(i * rowWidth, _range->_textBufferRowToEndpoint(i)); + VERIFY_ARE_EQUAL(i * rowWidth, _range->_textBufferRowToEndpoint(_pRenderData, i)); // make sure that the translation is reversible - VERIFY_ARE_EQUAL(i, _range->_endpointToTextBufferRow(_range->_textBufferRowToEndpoint(i))); + VERIFY_ARE_EQUAL(i, _range->_endpointToTextBufferRow(_pRenderData, _range->_textBufferRowToEndpoint(_pRenderData, i))); } } @@ -232,7 +239,7 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (unsigned int i = 0; i < 5; ++i) { - VERIFY_ARE_EQUAL(i, _range->_textBufferRowToScreenInfoRow(_range->_screenInfoRowToTextBufferRow(i))); + VERIFY_ARE_EQUAL(i, _range->_textBufferRowToScreenInfoRow(_pRenderData, _range->_screenInfoRowToTextBufferRow(_pRenderData, i))); } } @@ -242,7 +249,7 @@ class UiaTextRangeTests for (auto i = 0; i < 300; ++i) { const auto column = i % rowWidth; - VERIFY_ARE_EQUAL(column, _range->_endpointToColumn(i)); + VERIFY_ARE_EQUAL(column, _range->_endpointToColumn(_pRenderData, i)); } } @@ -250,13 +257,13 @@ class UiaTextRangeTests { const auto totalRows = _pTextBuffer->TotalRowCount(); VERIFY_ARE_EQUAL(totalRows, - _range->_getTotalRows()); + _range->_getTotalRows(_pRenderData)); } TEST_METHOD(CanGetRowWidth) { const auto rowWidth = _getRowWidth(); - VERIFY_ARE_EQUAL(rowWidth, _range->_getRowWidth()); + VERIFY_ARE_EQUAL(rowWidth, _range->_getRowWidth(_pRenderData)); } TEST_METHOD(CanNormalizeRow) @@ -273,7 +280,7 @@ class UiaTextRangeTests for (auto it = rowMappings.begin(); it != rowMappings.end(); ++it) { - VERIFY_ARE_EQUAL(static_cast(it->second), _range->_normalizeRow(it->first)); + VERIFY_ARE_EQUAL(static_cast(it->second), _range->_normalizeRow(_pRenderData, it->first)); } } @@ -323,7 +330,8 @@ class UiaTextRangeTests for (auto data : testData) { VERIFY_ARE_EQUAL(std::get<4>(data), - UiaTextRange::_compareScreenCoords(std::get<0>(data), + UiaTextRange::_compareScreenCoords(_pRenderData, + std::get<0>(data), std::get<1>(data), std::get<2>(data), std::get<3>(data))); @@ -393,8 +401,8 @@ class UiaTextRangeTests }, 5, 5, - UiaTextRange::_screenInfoRowToEndpoint(2) + 6, - UiaTextRange::_screenInfoRowToEndpoint(2) + 6 + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, 2) + 6, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, 2) + 6 }, { @@ -410,8 +418,8 @@ class UiaTextRangeTests }, 5, 0, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex }, { @@ -427,8 +435,8 @@ class UiaTextRangeTests }, 5, 5, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 1) + 4, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 1) + 4 + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + 4 }, { @@ -444,8 +452,8 @@ class UiaTextRangeTests }, -5, -5, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + (lastColumnIndex - 4), - UiaTextRange::_screenInfoRowToEndpoint(topRow) + (lastColumnIndex - 4) + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + (lastColumnIndex - 4), + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + (lastColumnIndex - 4) } }; // clang-format on @@ -454,7 +462,8 @@ class UiaTextRangeTests { Log::Comment(std::get<0>(data).c_str()); int amountMoved; - std::pair newEndpoints = UiaTextRange::_moveByCharacter(std::get<2>(data), + std::pair newEndpoints = UiaTextRange::_moveByCharacter(_pRenderData, + std::get<2>(data), std::get<1>(data), &amountMoved); @@ -493,8 +502,8 @@ class UiaTextRangeTests }, -4, 0, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex }, { @@ -510,8 +519,8 @@ class UiaTextRangeTests }, 4, 4, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 4) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 4) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 4) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 4) + lastColumnIndex }, { @@ -527,8 +536,8 @@ class UiaTextRangeTests }, 3, 0, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex }, { @@ -544,8 +553,8 @@ class UiaTextRangeTests }, -3, -3, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow - 3) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow - 3) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow - 3) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow - 3) + lastColumnIndex }, { @@ -561,8 +570,8 @@ class UiaTextRangeTests }, -1, 0, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex + 5, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex + 5, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex }, { @@ -578,8 +587,8 @@ class UiaTextRangeTests }, 1, 0, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + firstColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex } }; // clang-format on @@ -588,7 +597,8 @@ class UiaTextRangeTests { Log::Comment(std::get<0>(data).c_str()); int amountMoved; - std::pair newEndpoints = UiaTextRange::_moveByLine(std::get<2>(data), + std::pair newEndpoints = UiaTextRange::_moveByLine(_pRenderData, + std::get<2>(data), std::get<1>(data), &amountMoved); @@ -630,8 +640,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, false }, @@ -649,8 +659,8 @@ class UiaTextRangeTests -5, -3, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, false }, @@ -668,8 +678,8 @@ class UiaTextRangeTests -5, -4, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, false }, @@ -687,8 +697,8 @@ class UiaTextRangeTests -7, -7, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + 3, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + 3, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 3, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 3, true }, @@ -706,8 +716,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -725,8 +735,8 @@ class UiaTextRangeTests 5, 3, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -744,8 +754,8 @@ class UiaTextRangeTests 5, 4, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -763,8 +773,8 @@ class UiaTextRangeTests 7, 7, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + 12, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + 12, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 12, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 12, true }, }; @@ -775,7 +785,8 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitCharacter(std::get<2>(data), + result = UiaTextRange::_moveEndpointByUnitCharacter(_pRenderData, + std::get<2>(data), std::get<4>(data), std::get<1>(data), &amountMoved); @@ -819,8 +830,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 1) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + lastColumnIndex, false }, @@ -838,8 +849,8 @@ class UiaTextRangeTests -2, -2, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 1) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 3) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + lastColumnIndex, false }, @@ -857,8 +868,8 @@ class UiaTextRangeTests 2, 2, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 3) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 5) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + lastColumnIndex, false }, @@ -876,8 +887,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 1) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 5) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + lastColumnIndex, false }, @@ -895,8 +906,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, false }, @@ -914,8 +925,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, false }, @@ -933,8 +944,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -952,8 +963,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -971,8 +982,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, true }, @@ -990,8 +1001,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, true } }; @@ -1002,7 +1013,8 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitLine(std::get<2>(data), + result = UiaTextRange::_moveEndpointByUnitLine(_pRenderData, + std::get<2>(data), std::get<4>(data), std::get<1>(data), &amountMoved); @@ -1046,8 +1058,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex + 4, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -1065,8 +1077,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 4, false }, @@ -1084,8 +1096,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 3) + firstColumnIndex + 2, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + firstColumnIndex + 2, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, false }, @@ -1103,8 +1115,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow + 5) + 6, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + 6, false }, @@ -1122,8 +1134,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, true }, @@ -1141,8 +1153,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, true } }; @@ -1153,7 +1165,8 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitDocument(std::get<2>(data), + result = UiaTextRange::_moveEndpointByUnitDocument(_pRenderData, + std::get<2>(data), std::get<4>(data), std::get<1>(data), &amountMoved); diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 5156ec90b59..6e4f5e2cfde 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -11,7 +11,6 @@ #include "windowdpiapi.hpp" #include "windowmetrics.hpp" #include "windowtheme.hpp" -#include "windowUiaProvider.hpp" #include "..\..\host\globals.h" #include "..\..\host\dbcs.h" @@ -32,6 +31,7 @@ #include "..\inc\ServiceLocator.hpp" #include "..\..\types\inc\Viewport.hpp" +#include "..\interactivity\win32\windowUiaProvider.hpp" // The following default masks are used in creating windows // Make sure that these flags match when switching to fullscreen and back @@ -979,7 +979,7 @@ void Window::s_CalculateWindowRect(const COORD coordWindowInChars, prectWindow->bottom = prectWindow->top + RECT_HEIGHT(&rectProposed); } -RECT Window::GetWindowRect() const +RECT Window::GetWindowRect() const noexcept { RECT rc = { 0 }; ::GetWindowRect(GetWindowHandle(), &rc); @@ -1287,7 +1287,7 @@ IRawElementProviderSimple* Window::_GetUiaProvider() { try { - _pUiaProvider = WindowUiaProvider::Create(); + _pUiaProvider = WindowUiaProvider::Create(this); } catch (...) { diff --git a/src/interactivity/win32/window.hpp b/src/interactivity/win32/window.hpp index dc884c008a4..c753d9f345e 100644 --- a/src/interactivity/win32/window.hpp +++ b/src/interactivity/win32/window.hpp @@ -14,13 +14,13 @@ Author(s): --*/ #pragma once -#include "..\inc\IConsoleWindow.hpp" +#include "..\types\IConsoleWindow.hpp" namespace Microsoft::Console::Interactivity::Win32 { class WindowUiaProvider; - class Window final : public IConsoleWindow + class Window final : public Microsoft::Console::Types::IConsoleWindow { public: [[nodiscard]] static NTSTATUS CreateInstance(_In_ Settings* const pSettings, @@ -30,7 +30,7 @@ namespace Microsoft::Console::Interactivity::Win32 ~Window(); - RECT GetWindowRect() const; + RECT GetWindowRect() const noexcept; HWND GetWindowHandle() const; SCREEN_INFORMATION& GetScreenInfo(); const SCREEN_INFORMATION& GetScreenInfo() const; diff --git a/src/interactivity/win32/windowUiaProvider.cpp b/src/interactivity/win32/windowUiaProvider.cpp index 89806da35a2..084b834b277 100644 --- a/src/interactivity/win32/windowUiaProvider.cpp +++ b/src/interactivity/win32/windowUiaProvider.cpp @@ -3,20 +3,17 @@ #include "precomp.h" #include "windowUiaProvider.hpp" -#include "window.hpp" - -#include "screenInfoUiaProvider.hpp" -#include "UiaTextRange.hpp" +#include "../types/ScreenInfoUiaProvider.h" +#include "../host/renderData.hpp" #include "../inc/ServiceLocator.hpp" +using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity::Win32; -using namespace Microsoft::Console::Interactivity::Win32::WindowUiaProviderTracing; -WindowUiaProvider::WindowUiaProvider() : - _signalEventFiring{}, +WindowUiaProvider::WindowUiaProvider(IConsoleWindow* baseWindow) : _pScreenInfoProvider{ nullptr }, - _cRefs(1) + WindowUiaProviderBase(baseWindow) { } @@ -28,17 +25,23 @@ WindowUiaProvider::~WindowUiaProvider() } } -WindowUiaProvider* WindowUiaProvider::Create() +WindowUiaProvider* WindowUiaProvider::Create(IConsoleWindow* baseWindow) { WindowUiaProvider* pWindowProvider = nullptr; - ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; + Microsoft::Console::Types::ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; try { - pWindowProvider = new WindowUiaProvider(); - pScreenInfoProvider = new ScreenInfoUiaProvider(pWindowProvider); + pWindowProvider = new WindowUiaProvider(baseWindow); + + Globals& g = ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = g.getConsoleInformation(); + Microsoft::Console::Render::IRenderData* renderData = &gci.renderData; + + pScreenInfoProvider = new Microsoft::Console::Types::ScreenInfoUiaProvider(renderData, pWindowProvider); pWindowProvider->_pScreenInfoProvider = pScreenInfoProvider; - Tracing::s_TraceUia(pWindowProvider, ApiCall::Create, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(pWindowProvider, ApiCall::Create, nullptr); return pWindowProvider; } @@ -60,6 +63,15 @@ WindowUiaProvider* WindowUiaProvider::Create() } } +[[nodiscard]] HRESULT WindowUiaProvider::SetTextAreaFocus() +{ + try + { + return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId); + } + CATCH_RETURN(); +} + [[nodiscard]] HRESULT WindowUiaProvider::Signal(_In_ EVENTID id) { HRESULT hr = S_OK; @@ -96,183 +108,13 @@ WindowUiaProvider* WindowUiaProvider::Create() hr = UiaRaiseAutomationEvent(pProvider, id); _signalEventFiring[id] = false; - // tracing - ApiMessageSignal apiMsg; - apiMsg.Signal = id; - Tracing::s_TraceUia(this, ApiCall::Signal, &apiMsg); - return hr; } -[[nodiscard]] HRESULT WindowUiaProvider::SetTextAreaFocus() -{ - try - { - return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId); - } - CATCH_RETURN(); -} - -#pragma region IUnknown - -IFACEMETHODIMP_(ULONG) -WindowUiaProvider::AddRef() -{ - Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); - return InterlockedIncrement(&_cRefs); -} - -IFACEMETHODIMP_(ULONG) -WindowUiaProvider::Release() -{ - Tracing::s_TraceUia(this, ApiCall::Release, nullptr); - long val = InterlockedDecrement(&_cRefs); - if (val == 0) - { - delete this; - } - return val; -} - -IFACEMETHODIMP WindowUiaProvider::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) -{ - Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); - if (riid == __uuidof(IUnknown)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderSimple)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderFragment)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderFragmentRoot)) - { - *ppInterface = static_cast(this); - } - else - { - *ppInterface = nullptr; - return E_NOINTERFACE; - } - - (static_cast(*ppInterface))->AddRef(); - - return S_OK; -} - -#pragma endregion - -#pragma region IRawElementProviderSimple - -// Implementation of IRawElementProviderSimple::get_ProviderOptions. -// Gets UI Automation provider options. -IFACEMETHODIMP WindowUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* pOptions) -{ - Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - - *pOptions = ProviderOptions_ServerSideProvider; - return S_OK; -} - -// Implementation of IRawElementProviderSimple::get_PatternProvider. -// Gets the object that supports ISelectionPattern. -IFACEMETHODIMP WindowUiaProvider::GetPatternProvider(_In_ PATTERNID /*patternId*/, - _COM_Outptr_result_maybenull_ IUnknown** ppInterface) -{ - Tracing::s_TraceUia(this, ApiCall::GetPatternProvider, nullptr); - *ppInterface = nullptr; - RETURN_IF_FAILED(_EnsureValidHwnd()); - - return S_OK; -} - -// Implementation of IRawElementProviderSimple::get_PropertyValue. -// Gets custom properties. -IFACEMETHODIMP WindowUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyId, _Out_ VARIANT* pVariant) -{ - Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - - pVariant->vt = VT_EMPTY; - - // Returning the default will leave the property as the default - // so we only really need to touch it for the properties we want to implement - if (propertyId == UIA_ControlTypePropertyId) - { - pVariant->vt = VT_I4; - pVariant->lVal = UIA_WindowControlTypeId; - } - else if (propertyId == UIA_AutomationIdPropertyId) - { - pVariant->bstrVal = SysAllocString(L"Console Window"); - if (pVariant->bstrVal != nullptr) - { - pVariant->vt = VT_BSTR; - } - } - else if (propertyId == UIA_IsControlElementPropertyId) - { - pVariant->vt = VT_BOOL; - pVariant->boolVal = VARIANT_TRUE; - } - else if (propertyId == UIA_IsContentElementPropertyId) - { - pVariant->vt = VT_BOOL; - pVariant->boolVal = VARIANT_TRUE; - } - else if (propertyId == UIA_IsKeyboardFocusablePropertyId) - { - pVariant->vt = VT_BOOL; - pVariant->boolVal = VARIANT_TRUE; - } - else if (propertyId == UIA_HasKeyboardFocusPropertyId) - { - pVariant->vt = VT_BOOL; - pVariant->boolVal = VARIANT_TRUE; - } - else if (propertyId == UIA_ProviderDescriptionPropertyId) - { - pVariant->bstrVal = SysAllocString(L"Microsoft Console Host Window"); - if (pVariant->bstrVal != nullptr) - { - pVariant->vt = VT_BSTR; - } - } - - return S_OK; -} - -// Implementation of IRawElementProviderSimple::get_HostRawElementProvider. -// Gets the default UI Automation provider for the host window. This provider -// supplies many properties. -IFACEMETHODIMP WindowUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) -{ - Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr); - try - { - const HWND hwnd = _GetWindowHandle(); - return UiaHostProviderFromHwnd(hwnd, ppProvider); - } - catch (...) - { - return static_cast(UIA_E_ELEMENTNOTAVAILABLE); - } -} -#pragma endregion - #pragma region IRawElementProviderFragment IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { - ApiMsgNavigate apiMsg; - apiMsg.Direction = direction; - Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg); - RETURN_IF_FAILED(_EnsureValidHwnd()); *ppProvider = nullptr; HRESULT hr = S_OK; @@ -290,60 +132,11 @@ IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _CO return hr; } -IFACEMETHODIMP WindowUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) -{ - Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - // Root defers this to host, others must implement it... - *ppRuntimeId = nullptr; - - return S_OK; -} - -IFACEMETHODIMP WindowUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) -{ - Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - - const IConsoleWindow* const pIConsoleWindow = _getIConsoleWindow(); - RETURN_HR_IF_NULL((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, pIConsoleWindow); - - RECT const rc = pIConsoleWindow->GetWindowRect(); - - pRect->left = rc.left; - pRect->top = rc.top; - pRect->width = rc.right - rc.left; - pRect->height = rc.bottom - rc.top; - - return S_OK; -} - -IFACEMETHODIMP WindowUiaProvider::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) -{ - Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - - *ppRoots = nullptr; - return S_OK; -} - IFACEMETHODIMP WindowUiaProvider::SetFocus() { - Tracing::s_TraceUia(this, ApiCall::SetFocus, nullptr); RETURN_IF_FAILED(_EnsureValidHwnd()); return Signal(UIA_AutomationFocusChangedEventId); } - -IFACEMETHODIMP WindowUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) -{ - Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); - RETURN_IF_FAILED(_EnsureValidHwnd()); - - *ppProvider = this; - AddRef(); - return S_OK; -} - #pragma endregion #pragma region IRawElementProviderFragmentRoot @@ -352,7 +145,6 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, _In_ double /*y*/, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { - Tracing::s_TraceUia(this, ApiCall::ElementProviderFromPoint, nullptr); RETURN_IF_FAILED(_EnsureValidHwnd()); *ppProvider = _pScreenInfoProvider; @@ -363,33 +155,8 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { - Tracing::s_TraceUia(this, ApiCall::GetFocus, nullptr); RETURN_IF_FAILED(_EnsureValidHwnd()); return _pScreenInfoProvider->QueryInterface(IID_PPV_ARGS(ppProvider)); } #pragma endregion - -HWND WindowUiaProvider::_GetWindowHandle() const -{ - IConsoleWindow* const pIConsoleWindow = _getIConsoleWindow(); - THROW_HR_IF_NULL(E_POINTER, pIConsoleWindow); - - return pIConsoleWindow->GetWindowHandle(); -} - -[[nodiscard]] HRESULT WindowUiaProvider::_EnsureValidHwnd() const -{ - try - { - HWND const hwnd = _GetWindowHandle(); - RETURN_HR_IF((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, !(IsWindow(hwnd))); - } - CATCH_RETURN(); - return S_OK; -} - -Microsoft::Console::Interactivity::IConsoleWindow* const WindowUiaProvider::_getIConsoleWindow() -{ - return Microsoft::Console::Interactivity::ServiceLocator::LocateConsoleWindow(); -} diff --git a/src/interactivity/win32/windowUiaProvider.hpp b/src/interactivity/win32/windowUiaProvider.hpp index a14135e21cc..cb9f1b31bb3 100644 --- a/src/interactivity/win32/windowUiaProvider.hpp +++ b/src/interactivity/win32/windowUiaProvider.hpp @@ -20,117 +20,43 @@ Author(s): #pragma once #include "precomp.h" +#include "../types/WindowUiaProviderBase.hpp" -namespace Microsoft::Console::Interactivity::Win32 +namespace Microsoft::Console::Types { - // Forward declare, prevent circular ref. - class Window; - class ScreenInfoUiaProvider; + class IConsoleWindow; +} +namespace Microsoft::Console::Interactivity::Win32 +{ class WindowUiaProvider final : - public IRawElementProviderSimple, - public IRawElementProviderFragment, - public IRawElementProviderFragmentRoot + public Microsoft::Console::Types::WindowUiaProviderBase { public: - static WindowUiaProvider* Create(); - virtual ~WindowUiaProvider(); + static WindowUiaProvider* Create(Microsoft::Console::Types::IConsoleWindow* baseWindow); - [[nodiscard]] HRESULT Signal(_In_ EVENTID id); - [[nodiscard]] HRESULT SetTextAreaFocus(); - - // IUnknown methods - IFACEMETHODIMP_(ULONG) - AddRef(); - IFACEMETHODIMP_(ULONG) - Release(); - IFACEMETHODIMP QueryInterface(_In_ REFIID riid, - _COM_Outptr_result_maybenull_ void** ppInterface); - - // IRawElementProviderSimple methods - IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions); - IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, - _COM_Outptr_result_maybenull_ IUnknown** ppInterface); - IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, - _Out_ VARIANT* pVariant); - IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider); + [[nodiscard]] HRESULT Signal(_In_ EVENTID id) override; + [[nodiscard]] HRESULT SetTextAreaFocus() override; // IRawElementProviderFragment methods IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, - _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider); - IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId); - IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect); - IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots); - IFACEMETHODIMP SetFocus(); - IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider); + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP SetFocus() override; // IRawElementProviderFragmentRoot methods IFACEMETHODIMP ElementProviderFromPoint(_In_ double x, _In_ double y, - _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider); - IFACEMETHODIMP GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider); - - private: - WindowUiaProvider(); + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; - HWND _GetWindowHandle() const; - [[nodiscard]] HRESULT _EnsureValidHwnd() const; - static IConsoleWindow* const _getIConsoleWindow(); + protected: + const OLECHAR* AutomationIdPropertyName = L"Console Window"; + const OLECHAR* ProviderDescriptionPropertyName = L"Microsoft Console Host Window"; - // this is used to prevent the object from - // signaling an event while it is already in the - // process of signalling another event. - // This fixes a problem with JAWS where it would - // call a public method that calls - // UiaRaiseAutomationEvent to signal something - // happened, which JAWS then detects the signal - // and calls the same method in response, - // eventually overflowing the stack. - // We aren't using this as a cheap locking - // mechanism for multi-threaded code. - std::map _signalEventFiring; - - ScreenInfoUiaProvider* _pScreenInfoProvider; + private: + WindowUiaProvider(Microsoft::Console::Types::IConsoleWindow* baseWindow); + ~WindowUiaProvider(); - // Ref counter for COM object - ULONG _cRefs; + Microsoft::Console::Types::ScreenInfoUiaProvider* _pScreenInfoProvider; }; - - namespace WindowUiaProviderTracing - { - enum class ApiCall - { - Create, - Signal, - AddRef, - Release, - QueryInterface, - GetProviderOptions, - GetPatternProvider, - GetPropertyValue, - GetHostRawElementProvider, - Navigate, - GetRuntimeId, - GetBoundingRectangle, - GetEmbeddedFragmentRoots, - SetFocus, - GetFragmentRoot, - ElementProviderFromPoint, - GetFocus - }; - - struct IApiMsg - { - }; - - struct ApiMessageSignal : public IApiMsg - { - EVENTID Signal; - }; - - struct ApiMsgNavigate : public IApiMsg - { - NavigateDirection Direction; - }; - } } diff --git a/src/interactivity/win32/windowproc.cpp b/src/interactivity/win32/windowproc.cpp index 5957eb65fa6..9292bef9823 100644 --- a/src/interactivity/win32/windowproc.cpp +++ b/src/interactivity/win32/windowproc.cpp @@ -25,9 +25,10 @@ #include "..\inc\ServiceLocator.hpp" -#include "../interactivity/win32/windowtheme.hpp" -#include "../interactivity/win32/windowUiaProvider.hpp" -#include "../interactivity/win32/CustomWindowMessages.h" +#include "..\interactivity\win32\windowtheme.hpp" +#include "..\interactivity\win32\CustomWindowMessages.h" + +#include "..\interactivity\win32\windowUiaProvider.hpp" #include #include diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 33bbbc947ad..40c8ed2862a 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -20,6 +20,7 @@ Author(s): class TextBuffer; class Cursor; +struct ITextRangeProvider; namespace Microsoft::Console::Render { @@ -63,7 +64,24 @@ namespace Microsoft::Console::Render virtual const bool IsGridLineDrawingAllowed() noexcept = 0; + // TODO GitHub #1992: Move some of these functions to IAccessibilityData (or IUiaData) virtual std::vector GetSelectionRects() noexcept = 0; + virtual bool IsAreaSelected() const = 0; + virtual void ClearSelection() = 0; + virtual void SelectNewRegion(const COORD coordStart, const COORD coordEnd) = 0; + + // TODO GitHub #605: Search functionality + // For now, just adding it here to make UiaTextRange easier to create (Accessibility) + // We should actually abstract this out better once Windows Terminal has Search + virtual HRESULT SearchForText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, + unsigned int _start, + unsigned int _end, + std::function _coordToEndpoint, + std::function _endpointToCoord, + std::function Clone) = 0; virtual const std::wstring GetConsoleTitle() const noexcept = 0; diff --git a/src/interactivity/inc/IConsoleWindow.hpp b/src/types/IConsoleWindow.hpp similarity index 81% rename from src/interactivity/inc/IConsoleWindow.hpp rename to src/types/IConsoleWindow.hpp index d6585e020e1..632010edf08 100644 --- a/src/interactivity/inc/IConsoleWindow.hpp +++ b/src/types/IConsoleWindow.hpp @@ -15,12 +15,14 @@ Author(s): #pragma once +#include "IUiaWindow.h" + // copied typedef from uiautomationcore.h typedef int EVENTID; -namespace Microsoft::Console::Interactivity +namespace Microsoft::Console::Types { - class IConsoleWindow + class IConsoleWindow : public IUiaWindow { public: virtual ~IConsoleWindow() = 0; @@ -36,13 +38,9 @@ namespace Microsoft::Console::Interactivity virtual void SetIsFullscreen(const bool fFullscreenEnabled) = 0; - virtual void ChangeViewport(const SMALL_RECT NewWindow) = 0; - virtual void CaptureMouse() = 0; virtual BOOL ReleaseMouse() = 0; - virtual HWND GetWindowHandle() const = 0; - // Pass null. virtual void SetOwner() = 0; @@ -64,9 +62,6 @@ namespace Microsoft::Console::Interactivity const WORD wAbsoluteChange) = 0; virtual void VerticalScroll(const WORD wScrollCommand, const WORD wAbsoluteChange) = 0; - [[nodiscard]] virtual HRESULT SignalUia(_In_ EVENTID id) = 0; - [[nodiscard]] virtual HRESULT UiaSetTextAreaFocus() = 0; - virtual RECT GetWindowRect() const = 0; }; inline IConsoleWindow::~IConsoleWindow() {} diff --git a/src/types/IUiaWindow.h b/src/types/IUiaWindow.h new file mode 100644 index 00000000000..ad1a0c1cd20 --- /dev/null +++ b/src/types/IUiaWindow.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- IUiaWindow.hpp + +Abstract: +- Defines the methods and properties of what makes a window generate a UIA Tree for accessibility + +Author(s): +- Carlos Zamora (CaZamor) July 2019 +--*/ + +#pragma once + +// copied typedef from uiautomationcore.h +typedef int EVENTID; + +namespace Microsoft::Console::Types +{ + class IUiaWindow + { + public: + virtual void ChangeViewport(const SMALL_RECT NewWindow) = 0; + virtual HWND GetWindowHandle() const = 0; + [[nodiscard]] virtual HRESULT SignalUia(_In_ EVENTID id) = 0; + [[nodiscard]] virtual HRESULT UiaSetTextAreaFocus() = 0; + virtual RECT GetWindowRect() const noexcept = 0; + }; +} diff --git a/src/interactivity/win32/screenInfoUiaProvider.cpp b/src/types/ScreenInfoUiaProvider.cpp similarity index 72% rename from src/interactivity/win32/screenInfoUiaProvider.cpp rename to src/types/ScreenInfoUiaProvider.cpp index c8127e6c66b..e6dd03b016f 100644 --- a/src/interactivity/win32/screenInfoUiaProvider.cpp +++ b/src/types/ScreenInfoUiaProvider.cpp @@ -3,19 +3,14 @@ #include "precomp.h" -#include "screenInfoUiaProvider.hpp" -#include "../../host/screenInfo.hpp" -#include "../inc/ServiceLocator.hpp" - -#include "windowUiaProvider.hpp" -#include "window.hpp" -#include "windowdpiapi.hpp" +#include "ScreenInfoUiaProvider.h" +#include "WindowUiaProviderBase.hpp" #include "UiaTextRange.hpp" -using namespace Microsoft::Console::Interactivity::Win32; -using namespace Microsoft::Console::Interactivity::Win32::ScreenInfoUiaProviderTracing; -using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing; + // A helper function to create a SafeArray Version of an int array of a specified length SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int length) { @@ -36,12 +31,15 @@ SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int return psa; } -ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ WindowUiaProvider* const pUiaParent) : +ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ WindowUiaProviderBase* const pUiaParent) : _pUiaParent(THROW_HR_IF_NULL(E_INVALIDARG, pUiaParent)), _signalFiringMapping{}, - _cRefs(1) + _cRefs(1), + _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)) { - Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); } ScreenInfoUiaProvider::~ScreenInfoUiaProvider() @@ -68,10 +66,11 @@ ScreenInfoUiaProvider::~ScreenInfoUiaProvider() hr = UiaRaiseAutomationEvent(pProvider, id); _signalFiringMapping[id] = false; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgSignal apiMsg; + /*ApiMsgSignal apiMsg; apiMsg.Signal = id; - Tracing::s_TraceUia(this, ApiCall::Signal, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::Signal, &apiMsg);*/ return hr; } @@ -80,14 +79,16 @@ ScreenInfoUiaProvider::~ScreenInfoUiaProvider() IFACEMETHODIMP_(ULONG) ScreenInfoUiaProvider::AddRef() { - Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); return InterlockedIncrement(&_cRefs); } IFACEMETHODIMP_(ULONG) ScreenInfoUiaProvider::Release() { - Tracing::s_TraceUia(this, ApiCall::Release, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::Release, nullptr); long val = InterlockedDecrement(&_cRefs); if (val == 0) { @@ -99,7 +100,8 @@ ScreenInfoUiaProvider::Release() IFACEMETHODIMP ScreenInfoUiaProvider::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { - Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); if (riid == __uuidof(IUnknown)) { *ppInterface = static_cast(this); @@ -135,7 +137,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::QueryInterface(_In_ REFIID riid, // Gets UI Automation provider options. IFACEMETHODIMP ScreenInfoUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* pOptions) { - Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); *pOptions = ProviderOptions_ServerSideProvider; return S_OK; } @@ -145,7 +148,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* IFACEMETHODIMP ScreenInfoUiaProvider::GetPatternProvider(_In_ PATTERNID patternId, _COM_Outptr_result_maybenull_ IUnknown** ppInterface) { - Tracing::s_TraceUia(this, ApiCall::GetPatternProvider, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetPatternProvider, nullptr); *ppInterface = nullptr; HRESULT hr = S_OK; @@ -166,7 +170,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetPatternProvider(_In_ PATTERNID patternI IFACEMETHODIMP ScreenInfoUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyId, _Out_ VARIANT* pVariant) { - Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr); pVariant->vt = VT_EMPTY; @@ -235,7 +240,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyI IFACEMETHODIMP ScreenInfoUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) { - Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr); *ppProvider = nullptr; return S_OK; @@ -247,9 +253,10 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_HostRawElementProvider(_COM_Outptr_res IFACEMETHODIMP ScreenInfoUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { - ApiMsgNavigate apiMsg; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + /*ApiMsgNavigate apiMsg; apiMsg.Direction = direction; - Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg);*/ *ppProvider = nullptr; if (direction == NavigateDirection_Parent) @@ -272,7 +279,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::Navigate(_In_ NavigateDirection direction, IFACEMETHODIMP ScreenInfoUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) { - Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); + // Root defers this to host, others must implement it... *ppRuntimeId = nullptr; @@ -287,11 +296,10 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAF IFACEMETHODIMP ScreenInfoUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) { - Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); - const IConsoleWindow* const pIConsoleWindow = _getIConsoleWindow(); - RETURN_HR_IF_NULL((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, pIConsoleWindow); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); - RECT rc = pIConsoleWindow->GetWindowRect(); + RECT rc = _pUiaParent->GetWindowRect(); pRect->left = rc.left; pRect->top = rc.top; @@ -303,20 +311,24 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect IFACEMETHODIMP ScreenInfoUiaProvider::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) { - Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr); + *ppRoots = nullptr; return S_OK; } IFACEMETHODIMP ScreenInfoUiaProvider::SetFocus() { - Tracing::s_TraceUia(this, ApiCall::SetFocus, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::SetFocus, nullptr); + return Signal(UIA_AutomationFocusChangedEventId); } IFACEMETHODIMP ScreenInfoUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) { - Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); + //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); try { _pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); @@ -336,23 +348,25 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_FragmentRoot(_COM_Outptr_result_mayben IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - ApiMsgGetSelection apiMsg; - gci.LockConsole(); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //ApiMsgGetSelection apiMsg; + + _LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _UnlockConsole(); }); *ppRetVal = nullptr; HRESULT hr = S_OK; - if (!Selection::Instance().IsAreaSelected()) + if (!_pData->IsAreaSelected()) { - apiMsg.AreaSelected = false; - apiMsg.SelectionRowCount = 1; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //apiMsg.AreaSelected = false; + //apiMsg.SelectionRowCount = 1; + // return a degenerate range at the cursor position - SCREEN_INFORMATION& screenInfo = _getScreenInfo(); - const Cursor& cursor = screenInfo.GetTextBuffer().GetCursor(); + const Cursor& cursor = _getTextBuffer().GetCursor(); // make a safe array *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1); @@ -373,7 +387,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF UiaTextRange* range; try { - range = UiaTextRange::Create(pProvider, + range = UiaTextRange::Create(_pData, + pProvider, cursor); } catch (...) @@ -406,7 +421,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF RETURN_IF_FAILED(QueryInterface(IID_PPV_ARGS(&pProvider))); try { - ranges = UiaTextRange::GetSelectionRanges(pProvider); + ranges = UiaTextRange::GetSelectionRanges(_pData, pProvider); } catch (...) { @@ -415,8 +430,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF pProvider->Release(); RETURN_IF_FAILED(hr); - apiMsg.AreaSelected = true; - apiMsg.SelectionRowCount = static_cast(ranges.size()); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //apiMsg.AreaSelected = true; + //apiMsg.SelectionRowCount = static_cast(ranges.size()); // make a safe array *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, static_cast(ranges.size())); @@ -444,22 +460,22 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF } } - Tracing::s_TraceUia(this, ApiCall::GetSelection, &apiMsg); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetSelection, &apiMsg); return S_OK; } IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetVisibleRanges, nullptr); - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetVisibleRanges, nullptr); - gci.LockConsole(); + _LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _UnlockConsole(); }); - const SCREEN_INFORMATION& screenInfo = _getScreenInfo(); - const auto viewport = screenInfo.GetViewport(); + const auto viewport = _getViewport(); const COORD screenBufferCoords = _getScreenBufferCoords(); const int totalLines = screenBufferCoords.Y; @@ -491,7 +507,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ UiaTextRange* range; try { - range = UiaTextRange::Create(pProvider, + range = UiaTextRange::Create(_pData, + pProvider, start, end, false); @@ -525,7 +542,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromChild(_In_ IRawElementProviderSimple* /*childElement*/, _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::RangeFromChild, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::RangeFromChild, nullptr); IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); @@ -533,7 +551,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromChild(_In_ IRawElementProviderSim HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(pProvider); + *ppRetVal = UiaTextRange::Create(_pData, pProvider); } catch (...) { @@ -548,14 +566,17 @@ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromChild(_In_ IRawElementProviderSim IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromPoint(_In_ UiaPoint point, _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::RangeFromPoint, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::RangeFromPoint, nullptr); + IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(pProvider, + *ppRetVal = UiaTextRange::Create(_pData, + pProvider, point); } catch (...) @@ -570,14 +591,16 @@ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromPoint(_In_ UiaPoint point, IFACEMETHODIMP ScreenInfoUiaProvider::get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetDocumentRange, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetDocumentRange, nullptr); + IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(pProvider); + *ppRetVal = UiaTextRange::Create(_pData, pProvider); } catch (...) { @@ -596,7 +619,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_DocumentRange(_COM_Outptr_result_maybe IFACEMETHODIMP ScreenInfoUiaProvider::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr); + *pRetVal = SupportedTextSelection::SupportedTextSelection_Single; return S_OK; } @@ -605,18 +630,37 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_SupportedTextSelection(_Out_ Supported const COORD ScreenInfoUiaProvider::_getScreenBufferCoords() const { - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - return gci.GetScreenBufferSize(); + return _getTextBuffer().GetSize().Dimensions(); +} + +const TextBuffer& ScreenInfoUiaProvider::_getTextBuffer() const +{ + return _pData->GetTextBuffer(); +} + +const Viewport ScreenInfoUiaProvider::_getViewport() const +{ + return _pData->GetViewport(); +} + +void ScreenInfoUiaProvider::_LockConsole() noexcept +{ + // TODO GitHub #2141: Lock and Unlock in conhost should decouple Ctrl+C dispatch and use smarter handling + _pData->LockConsole(); +} + +void ScreenInfoUiaProvider::_UnlockConsole() noexcept +{ + // TODO GitHub #2141: Lock and Unlock in conhost should decouple Ctrl+C dispatch and use smarter handling + _pData->UnlockConsole(); } -SCREEN_INFORMATION& ScreenInfoUiaProvider::_getScreenInfo() +HWND ScreenInfoUiaProvider::GetWindowHandle() const { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - THROW_HR_IF(E_POINTER, !gci.HasActiveOutputBuffer()); - return gci.GetActiveOutputBuffer(); + return _pUiaParent->GetWindowHandle(); } -IConsoleWindow* const ScreenInfoUiaProvider::_getIConsoleWindow() +void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow) { - return ServiceLocator::LocateConsoleWindow(); + _pUiaParent->ChangeViewport(NewWindow); } diff --git a/src/interactivity/win32/screenInfoUiaProvider.hpp b/src/types/ScreenInfoUiaProvider.h similarity index 81% rename from src/interactivity/win32/screenInfoUiaProvider.hpp rename to src/types/ScreenInfoUiaProvider.h index 695105a1f5f..707bd0428bd 100644 --- a/src/interactivity/win32/screenInfoUiaProvider.hpp +++ b/src/types/ScreenInfoUiaProvider.h @@ -9,34 +9,35 @@ Module Name: - This module provides UI Automation access to the screen buffer to support both automation tests and accessibility (screen reading) applications. +- ConHost and Windows Terminal must use IRenderData to have access to the proper information - Based on examples, sample code, and guidance from https://msdn.microsoft.com/en-us/library/windows/desktop/ee671596(v=vs.85).aspx Author(s): -- Michael Niksa (MiNiksa) 2017 +- Michael Niksa (MiNiksa) 2017 - Austin Diviness (AustDi) 2017 +- Carlos Zamora (CaZamor) 2019 --*/ #pragma once #include "precomp.h" +#include "../buffer/out/textBuffer.hpp" +#include "../renderer/inc/IRenderData.hpp" -// Forward declare, prevent circular ref. -class SCREEN_INFORMATION; - -namespace Microsoft::Console::Interactivity::Win32 +namespace Microsoft::Console::Types { - class Window; - - class WindowUiaProvider; + class WindowUiaProviderBase; + class Viewport; - class ScreenInfoUiaProvider final : + class ScreenInfoUiaProvider : public IRawElementProviderSimple, public IRawElementProviderFragment, public ITextProvider { public: - ScreenInfoUiaProvider(_In_ WindowUiaProvider* const pUiaParent); + ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ WindowUiaProviderBase* const pUiaParent); virtual ~ScreenInfoUiaProvider(); [[nodiscard]] HRESULT Signal(_In_ EVENTID id); @@ -76,12 +77,18 @@ namespace Microsoft::Console::Interactivity::Win32 IFACEMETHODIMP get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal); + HWND GetWindowHandle() const; + void ChangeViewport(const SMALL_RECT NewWindow); + private: // Ref counter for COM object ULONG _cRefs; // weak reference to uia parent - WindowUiaProvider* const _pUiaParent; + WindowUiaProviderBase* const _pUiaParent; + + // weak reference to IRenderData + Microsoft::Console::Render::IRenderData* _pData; // this is used to prevent the object from // signaling an event while it is already in the @@ -97,8 +104,10 @@ namespace Microsoft::Console::Interactivity::Win32 std::map _signalFiringMapping; const COORD _getScreenBufferCoords() const; - static SCREEN_INFORMATION& _getScreenInfo(); - static IConsoleWindow* const _getIConsoleWindow(); + const TextBuffer& _getTextBuffer() const; + const Viewport _getViewport() const; + void _LockConsole() noexcept; + void _UnlockConsole() noexcept; }; namespace ScreenInfoUiaProviderTracing diff --git a/src/interactivity/win32/UiaTextRange.cpp b/src/types/UiaTextRange.cpp similarity index 70% rename from src/interactivity/win32/UiaTextRange.cpp rename to src/types/UiaTextRange.cpp index eedb8305c04..aa96f4db59a 100644 --- a/src/interactivity/win32/UiaTextRange.cpp +++ b/src/types/UiaTextRange.cpp @@ -3,18 +3,10 @@ #include "precomp.h" #include "UiaTextRange.hpp" -#include "../inc/ServiceLocator.hpp" +#include "ScreenInfoUiaProvider.h" -#include "window.hpp" -#include "windowdpiapi.hpp" -#include "../host/tracing.hpp" - -#include "../host/selection.hpp" -#include "../host/search.h" - -using namespace Microsoft::Console::Interactivity::Win32; -using namespace Microsoft::Console::Interactivity::Win32::UiaTextRangeTracing; -using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Types::UiaTextRangeTracing; // toggle these for additional logging in a debug build //#define UIATEXTRANGE_DEBUG_MSGS 1 @@ -22,25 +14,26 @@ using namespace Microsoft::Console::Interactivity; IdType UiaTextRange::id = 1; -UiaTextRange::MoveState::MoveState(const UiaTextRange& range, +UiaTextRange::MoveState::MoveState(Microsoft::Console::Render::IRenderData* pData, + const UiaTextRange& range, const MovementDirection direction) : - StartScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(range.GetStart()) }, - StartColumn{ UiaTextRange::_endpointToColumn(range.GetStart()) }, - EndScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(range.GetEnd()) }, - EndColumn{ UiaTextRange::_endpointToColumn(range.GetEnd()) }, + StartScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(pData, range.GetStart()) }, + StartColumn{ UiaTextRange::_endpointToColumn(pData, range.GetStart()) }, + EndScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(pData, range.GetEnd()) }, + EndColumn{ UiaTextRange::_endpointToColumn(pData, range.GetEnd()) }, Direction{ direction } { if (direction == MovementDirection::Forward) { - LimitingRow = UiaTextRange::_getLastScreenInfoRowIndex(); + LimitingRow = UiaTextRange::_getLastScreenInfoRowIndex(pData); FirstColumnInRow = UiaTextRange::_getFirstColumnIndex(); - LastColumnInRow = UiaTextRange::_getLastColumnIndex(); + LastColumnInRow = UiaTextRange::_getLastColumnIndex(pData); Increment = MovementIncrement::Forward; } else { LimitingRow = UiaTextRange::_getFirstScreenInfoRowIndex(); - FirstColumnInRow = UiaTextRange::_getLastColumnIndex(); + FirstColumnInRow = UiaTextRange::_getLastColumnIndex(pData); LastColumnInRow = UiaTextRange::_getFirstColumnIndex(); Increment = MovementIncrement::Backward; } @@ -72,16 +65,16 @@ UiaTextRange::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, // This is a debugging function that prints out the current // relationship between screen info rows, text buffer rows, and // endpoints. -void UiaTextRange::_outputRowConversions() +void UiaTextRange::_outputRowConversions(Microsoft::Console::Render::IRenderData* pData) { try { - unsigned int totalRows = _getTotalRows(); + unsigned int totalRows = _getTotalRows(pData); OutputDebugString(L"screenBuffer\ttextBuffer\tendpoint\n"); for (unsigned int i = 0; i < totalRows; ++i) { std::wstringstream ss; - ss << i << "\t" << _screenInfoRowToTextBufferRow(i) << "\t" << _screenInfoRowToEndpoint(i) << "\n"; + ss << i << "\t" << _screenInfoRowToTextBufferRow(pData, i) << "\t" << _screenInfoRowToEndpoint(pData, i) << "\n"; std::wstring str = ss.str(); OutputDebugString(str.c_str()); } @@ -108,20 +101,22 @@ void UiaTextRange::_outputObjectState() } #endif // _DEBUG -std::deque UiaTextRange::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) +std::deque UiaTextRange::GetSelectionRanges(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* pProvider) { std::deque ranges; // get the selection rects - const auto rectangles = Selection::Instance().GetSelectionRects(); + const auto rectangles = pData->GetSelectionRects(); // create a range for each row for (const auto& rect : rectangles) { - ScreenInfoRow currentRow = rect.Top; - Endpoint start = _screenInfoRowToEndpoint(currentRow) + rect.Left; - Endpoint end = _screenInfoRowToEndpoint(currentRow) + rect.Right; - UiaTextRange* range = UiaTextRange::Create(pProvider, + ScreenInfoRow currentRow = rect.Top(); + Endpoint start = _screenInfoRowToEndpoint(pData, currentRow) + rect.Left(); + Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive(); + UiaTextRange* range = UiaTextRange::Create(pData, + pProvider, start, end, false); @@ -144,13 +139,14 @@ std::deque UiaTextRange::GetSelectionRanges(_In_ IRawElementProvi return ranges; } -UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvider) +UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider) { UiaTextRange* range = nullptr; ; try { - range = new UiaTextRange(pProvider); + range = new UiaTextRange(pData, pProvider); } catch (...) { @@ -164,13 +160,14 @@ UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvid return range; } -UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor) { UiaTextRange* range = nullptr; try { - range = new UiaTextRange(pProvider, cursor); + range = new UiaTextRange(pData, pProvider, cursor); } catch (...) { @@ -184,7 +181,8 @@ UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvid return range; } -UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate) @@ -192,7 +190,8 @@ UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvid UiaTextRange* range = nullptr; try { - range = new UiaTextRange(pProvider, + range = new UiaTextRange(pData, + pProvider, start, end, degenerate); @@ -209,13 +208,14 @@ UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvid return range; } -UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point) { UiaTextRange* range = nullptr; try { - range = new UiaTextRange(pProvider, point); + range = new UiaTextRange(pData, pProvider, point); } catch (...) { @@ -230,28 +230,31 @@ UiaTextRange* UiaTextRange::Create(_In_ IRawElementProviderSimple* const pProvid } // degenerate range constructor. -UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider) : +UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ IRawElementProviderSimple* const pProvider) : _cRefs{ 1 }, _pProvider{ THROW_HR_IF_NULL(E_INVALIDARG, pProvider) }, _start{ 0 }, _end{ 0 }, - _degenerate{ true } + _degenerate{ true }, + _pData{ THROW_HR_IF_NULL(E_INVALIDARG, pData) } { _id = id; ++id; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgConstructor apiMsg; + /*ApiMsgConstructor apiMsg; apiMsg.Id = _id; - Tracing::s_TraceUia(nullptr, ApiCall::Constructor, &apiMsg); + Tracing::s_TraceUia(nullptr, ApiCall::Constructor, &apiMsg);*/ } -UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor) : - UiaTextRange(pProvider) + UiaTextRange(pData, pProvider) { _degenerate = true; - _start = _screenInfoRowToEndpoint(cursor.GetPosition().Y) + cursor.GetPosition().X; + _start = _screenInfoRowToEndpoint(_pData, cursor.GetPosition().Y) + cursor.GetPosition().X; _end = _start; #if defined(_DEBUG) && defined(UIATEXTRANGE_DEBUG_MSGS) @@ -260,11 +263,12 @@ UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, #endif } -UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate) : - UiaTextRange(pProvider) + UiaTextRange(pData, pProvider) { THROW_HR_IF(E_INVALIDARG, !degenerate && start > end); @@ -279,18 +283,17 @@ UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, } // returns a degenerate text range of the start of the row closest to the y value of point -UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, +UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point) : - UiaTextRange(pProvider) + UiaTextRange(pData, pProvider) { POINT clientPoint; clientPoint.x = static_cast(point.x); clientPoint.y = static_cast(point.y); // get row that point resides in - const IConsoleWindow* const pIConsoleWindow = _getIConsoleWindow(); - const Window* const pWindow = static_cast(pIConsoleWindow); - const RECT windowRect = pWindow->GetWindowRect(); - const SMALL_RECT viewport = _getViewport().ToInclusive(); + const RECT windowRect = _getTerminalRect(); + const SMALL_RECT viewport = _pData->GetViewport().ToInclusive(); ScreenInfoRow row; if (clientPoint.y <= windowRect.top) { @@ -306,11 +309,10 @@ UiaTextRange::UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, HWND hwnd = _getWindowHandle(); ScreenToClient(hwnd, &clientPoint); - const SCREEN_INFORMATION& _pScreenInfo = _getScreenInfo(); - const COORD currentFontSize = _pScreenInfo.GetScreenFontSize(); + const COORD currentFontSize = _getScreenFontSize(); row = (clientPoint.y / currentFontSize.Y) + viewport.Top; } - _start = _screenInfoRowToEndpoint(row); + _start = _screenInfoRowToEndpoint(_pData, row); _end = _start; _degenerate = true; @@ -325,7 +327,8 @@ UiaTextRange::UiaTextRange(const UiaTextRange& a) : _pProvider{ a._pProvider }, _start{ a._start }, _end{ a._end }, - _degenerate{ a._degenerate } + _degenerate{ a._degenerate }, + _pData{ a._pData } { (static_cast(_pProvider))->AddRef(); _id = id; @@ -368,19 +371,28 @@ const bool UiaTextRange::IsDegenerate() const return _degenerate; } +void UiaTextRange::SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) +{ + _start = start; + _end = end; + _degenerate = isDegenerate; +} + #pragma region IUnknown IFACEMETHODIMP_(ULONG) UiaTextRange::AddRef() { - Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); return InterlockedIncrement(&_cRefs); } IFACEMETHODIMP_(ULONG) UiaTextRange::Release() { - Tracing::s_TraceUia(this, ApiCall::Release, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::Release, nullptr); const long val = InterlockedDecrement(&_cRefs); if (val == 0) @@ -392,7 +404,8 @@ UiaTextRange::Release() IFACEMETHODIMP UiaTextRange::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { - Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); if (riid == __uuidof(IUnknown)) { @@ -440,20 +453,20 @@ IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider* OutputDebugString(str.c_str()); OutputDebugString(L"\n"); #endif + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgClone apiMsg; + /*ApiMsgClone apiMsg; apiMsg.CloneId = static_cast(*ppRetVal)->GetId(); - Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/ return S_OK; } IFACEMETHODIMP UiaTextRange::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _pData->UnlockConsole(); }); *pRetVal = FALSE; @@ -464,11 +477,12 @@ IFACEMETHODIMP UiaTextRange::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ _end == other->GetEnd() && _degenerate == other->IsDegenerate()); } + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgCompare apiMsg; + /*ApiMsgCompare apiMsg; apiMsg.OtherId = other == nullptr ? InvalidId : other->GetId(); apiMsg.Equal = !!*pRetVal; - Tracing::s_TraceUia(this, ApiCall::Compare, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::Compare, &apiMsg);*/ return S_OK; } @@ -510,22 +524,23 @@ IFACEMETHODIMP UiaTextRange::CompareEndpoints(_In_ TextPatternRangeEndpoint endp // compare them *pRetVal = std::clamp(static_cast(ourValue) - static_cast(theirValue), -1, 1); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgCompareEndpoints apiMsg; + /*ApiMsgCompareEndpoints apiMsg; apiMsg.OtherId = range->GetId(); apiMsg.Endpoint = endpoint; apiMsg.TargetEndpoint = targetEndpoint; apiMsg.Result = *pRetVal; - Tracing::s_TraceUia(this, ApiCall::CompareEndpoints, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::CompareEndpoints, &apiMsg);*/ return S_OK; } IFACEMETHODIMP UiaTextRange::ExpandToEnclosingUnit(_In_ TextUnit unit) { - ServiceLocator::LocateGlobals().getConsoleInformation().LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - ServiceLocator::LocateGlobals().getConsoleInformation().UnlockConsole(); + _pData->UnlockConsole(); }); ApiMsgExpandToEnclosingUnit apiMsg; @@ -536,7 +551,7 @@ IFACEMETHODIMP UiaTextRange::ExpandToEnclosingUnit(_In_ TextUnit unit) try { const ScreenInfoRow topRow = _getFirstScreenInfoRowIndex(); - const ScreenInfoRow bottomRow = _getLastScreenInfoRowIndex(); + const ScreenInfoRow bottomRow = _getLastScreenInfoRowIndex(_pData); if (unit == TextUnit::TextUnit_Character) { @@ -545,20 +560,21 @@ IFACEMETHODIMP UiaTextRange::ExpandToEnclosingUnit(_In_ TextUnit unit) else if (unit <= TextUnit::TextUnit_Line) { // expand to line - _start = _textBufferRowToEndpoint(_endpointToTextBufferRow(_start)); - _end = _start + _getLastColumnIndex(); + _start = _textBufferRowToEndpoint(_pData, _endpointToTextBufferRow(_pData, _start)); + _end = _start + _getLastColumnIndex(_pData); FAIL_FAST_IF(!(_start <= _end)); } else { // expand to document - _start = _screenInfoRowToEndpoint(topRow); - _end = _screenInfoRowToEndpoint(bottomRow) + _getLastColumnIndex(); + _start = _screenInfoRowToEndpoint(_pData, topRow); + _end = _screenInfoRowToEndpoint(_pData, bottomRow) + _getLastColumnIndex(_pData); } _degenerate = false; - Tracing::s_TraceUia(this, ApiCall::ExpandToEnclosingUnit, &apiMsg); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::ExpandToEnclosingUnit, &apiMsg); return S_OK; } @@ -571,7 +587,8 @@ IFACEMETHODIMP UiaTextRange::FindAttribute(_In_ TEXTATTRIBUTEID /*textAttributeI _In_ BOOL /*searchBackward*/, _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/) { - Tracing::s_TraceUia(this, ApiCall::FindAttribute, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::FindAttribute, nullptr); return E_NOTIMPL; } @@ -580,45 +597,26 @@ IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text, _In_ BOOL ignoreCase, _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::FindText, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::FindText, nullptr); *ppRetVal = nullptr; try { - const std::wstring wstr{ text, SysStringLen(text) }; - const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive; - - auto searchDirection = Search::Direction::Forward; - Endpoint searchAnchor = _start; - if (searchBackward) - { - searchDirection = Search::Direction::Backward; - searchAnchor = _end; - } - - Search searcher{ _getScreenInfo(), wstr, searchDirection, sensitivity, _endpointToCoord(searchAnchor) }; - - HRESULT hr = S_OK; - if (searcher.FindNext()) - { - const auto foundLocation = searcher.GetFoundLocation(); - const Endpoint start = _coordToEndpoint(foundLocation.first); - const Endpoint end = _coordToEndpoint(foundLocation.second); - // make sure what was found is within the bounds of the current range - if ((searchDirection == Search::Direction::Forward && end < _end) || - (searchDirection == Search::Direction::Backward && start > _start)) - { - hr = Clone(ppRetVal); - if (SUCCEEDED(hr)) - { - UiaTextRange& range = static_cast(**ppRetVal); - range._start = start; - range._end = end; - range._degenerate = false; - } - } - } - return hr; + // TODO GitHub #605: Search functionality + // For now, just adding it here to make UiaTextRange easier to create (Accessibility) + // We should actually abstract this out better once Windows Terminal has Search + + std::function Clone = std::bind(&UiaTextRange::Clone, this, std::placeholders::_1); + return _pData->SearchForText(text, + searchBackward, + ignoreCase, + ppRetVal, + _start, + _end, + _coordToEndpoint, + _endpointToCoord, + Clone); } CATCH_RETURN(); } @@ -626,7 +624,8 @@ IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text, IFACEMETHODIMP UiaTextRange::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, _Out_ VARIANT* pRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetAttributeValue, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetAttributeValue, nullptr); if (textAttributeId == UIA_IsReadOnlyAttributeId) { pRetVal->vt = VT_BOOL; @@ -642,10 +641,9 @@ IFACEMETHODIMP UiaTextRange::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttribut IFACEMETHODIMP UiaTextRange::GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _pData->UnlockConsole(); }); *ppRetVal = nullptr; @@ -656,23 +654,23 @@ IFACEMETHODIMP UiaTextRange::GetBoundingRectangles(_Outptr_result_maybenull_ SAF // order: left, top, width, height. each line will have its own // set of coords. std::vector coords; - const TextBufferRow startRow = _endpointToTextBufferRow(_start); + const TextBufferRow startRow = _endpointToTextBufferRow(_pData, _start); - if (_degenerate && _isScreenInfoRowInViewport(startRow)) + if (_degenerate && _isScreenInfoRowInViewport(_pData, startRow)) { - _addScreenInfoRowBoundaries(_textBufferRowToScreenInfoRow(startRow), coords); + _addScreenInfoRowBoundaries(_pData, _textBufferRowToScreenInfoRow(_pData, startRow), coords); } else { - const unsigned int totalRowsInRange = _rowCountInRange(); + const unsigned int totalRowsInRange = _rowCountInRange(_pData); for (unsigned int i = 0; i < totalRowsInRange; ++i) { - ScreenInfoRow screenInfoRow = _textBufferRowToScreenInfoRow(startRow + i); - if (!_isScreenInfoRowInViewport(screenInfoRow)) + ScreenInfoRow screenInfoRow = _textBufferRowToScreenInfoRow(_pData, startRow + i); + if (!_isScreenInfoRowInViewport(_pData, screenInfoRow)) { continue; } - _addScreenInfoRowBoundaries(screenInfoRow, coords); + _addScreenInfoRowBoundaries(_pData, screenInfoRow, coords); } } @@ -696,23 +694,23 @@ IFACEMETHODIMP UiaTextRange::GetBoundingRectangles(_Outptr_result_maybenull_ SAF } CATCH_RETURN(); - Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangles, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangles, nullptr); return S_OK; } IFACEMETHODIMP UiaTextRange::GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangles, nullptr); + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangles, nullptr); return _pProvider->QueryInterface(IID_PPV_ARGS(ppRetVal)); } IFACEMETHODIMP UiaTextRange::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _pData->UnlockConsole(); }); std::wstring wstr = L""; @@ -730,12 +728,12 @@ IFACEMETHODIMP UiaTextRange::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) { try { - const ScreenInfoRow startScreenInfoRow = _endpointToScreenInfoRow(_start); - const Column startColumn = _endpointToColumn(_start); - const ScreenInfoRow endScreenInfoRow = _endpointToScreenInfoRow(_end); - const Column endColumn = _endpointToColumn(_end); - const unsigned int totalRowsInRange = _rowCountInRange(); - const TextBuffer& textBuffer = _getTextBuffer(); + const ScreenInfoRow startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); + const Column startColumn = _endpointToColumn(_pData, _start); + const ScreenInfoRow endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); + const Column endColumn = _endpointToColumn(_pData, _end); + const unsigned int totalRowsInRange = _rowCountInRange(_pData); + const TextBuffer& textBuffer = _pData->GetTextBuffer(); #if defined(_DEBUG) && defined(UIATEXTRANGE_DEBUG_MSGS) std::wstringstream ss; @@ -791,10 +789,11 @@ IFACEMETHODIMP UiaTextRange::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) *pRetVal = SysAllocString(wstr.c_str()); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgGetText apiMsg; + /*ApiMsgGetText apiMsg; apiMsg.Text = wstr.c_str(); - Tracing::s_TraceUia(this, ApiCall::GetText, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::GetText, &apiMsg);*/ #if defined(_DEBUG) && defined(UIATEXTRANGE_DEBUG_MSGS) std::wstringstream ss; @@ -809,9 +808,9 @@ IFACEMETHODIMP UiaTextRange::Move(_In_ TextUnit unit, _In_ int count, _Out_ int* pRetVal) { - ServiceLocator::LocateGlobals().getConsoleInformation().LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - ServiceLocator::LocateGlobals().getConsoleInformation().UnlockConsole(); + _pData->UnlockConsole(); }); *pRetVal = 0; @@ -852,8 +851,9 @@ IFACEMETHODIMP UiaTextRange::Move(_In_ TextUnit unit, try { - MoveState moveState{ *this, moveDirection }; - newEndpoints = moveFunc(count, + MoveState moveState{ _pData, *this, moveDirection }; + newEndpoints = moveFunc(_pData, + count, moveState, pRetVal); } @@ -866,9 +866,10 @@ IFACEMETHODIMP UiaTextRange::Move(_In_ TextUnit unit, // moved. _degenerate = false; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - apiMsg.MovedCount = *pRetVal; - Tracing::s_TraceUia(this, ApiCall::Move, &apiMsg); + /*apiMsg.MovedCount = *pRetVal; + Tracing::s_TraceUia(this, ApiCall::Move, &apiMsg);*/ return S_OK; } @@ -878,9 +879,9 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint en _In_ int count, _Out_ int* pRetVal) { - ServiceLocator::LocateGlobals().getConsoleInformation().LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - ServiceLocator::LocateGlobals().getConsoleInformation().UnlockConsole(); + _pData->UnlockConsole(); }); *pRetVal = 0; @@ -923,8 +924,8 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint en std::tuple moveResults; try { - MoveState moveState{ *this, moveDirection }; - moveResults = moveFunc(count, endpoint, moveState, pRetVal); + MoveState moveState{ _pData, *this, moveDirection }; + moveResults = moveFunc(_pData, count, endpoint, moveState, pRetVal); } CATCH_RETURN(); @@ -932,9 +933,10 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint en _end = std::get<1>(moveResults); _degenerate = std::get<2>(moveResults); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - apiMsg.MovedCount = *pRetVal; - Tracing::s_TraceUia(this, ApiCall::MoveEndpointByUnit, &apiMsg); + /*apiMsg.MovedCount = *pRetVal; + Tracing::s_TraceUia(this, ApiCall::MoveEndpointByUnit, &apiMsg);*/ return S_OK; } @@ -943,9 +945,9 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e _In_ ITextRangeProvider* pTargetRange, _In_ TextPatternRangeEndpoint targetEndpoint) { - ServiceLocator::LocateGlobals().getConsoleInformation().LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - ServiceLocator::LocateGlobals().getConsoleInformation().UnlockConsole(); + _pData->UnlockConsole(); }); UiaTextRange* range = static_cast(pTargetRange); @@ -1011,12 +1013,12 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e Column targetColumn; try { - startScreenInfoRow = _endpointToScreenInfoRow(_start); - startColumn = _endpointToColumn(_start); - endScreenInfoRow = _endpointToScreenInfoRow(_end); - endColumn = _endpointToColumn(_end); - targetScreenInfoRow = _endpointToScreenInfoRow(targetEndpointValue); - targetColumn = _endpointToColumn(targetEndpointValue); + startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); + startColumn = _endpointToColumn(_pData, _start); + endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); + endColumn = _endpointToColumn(_pData, _end); + targetScreenInfoRow = _endpointToScreenInfoRow(_pData, targetEndpointValue); + targetColumn = _endpointToColumn(_pData, targetEndpointValue); } CATCH_RETURN(); @@ -1025,7 +1027,7 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { _start = targetEndpointValue; - if (_compareScreenCoords(endScreenInfoRow, endColumn, targetScreenInfoRow, targetColumn) == -1) + if (_compareScreenCoords(_pData, endScreenInfoRow, endColumn, targetScreenInfoRow, targetColumn) == -1) { // endpoints were crossed _end = _start; @@ -1035,7 +1037,7 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e else { _end = targetEndpointValue; - if (_compareScreenCoords(startScreenInfoRow, startColumn, targetScreenInfoRow, targetColumn) == 1) + if (_compareScreenCoords(_pData, startScreenInfoRow, startColumn, targetScreenInfoRow, targetColumn) == 1) { // endpoints were crossed _start = _end; @@ -1044,61 +1046,63 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e } _degenerate = crossedEndpoints; - Tracing::s_TraceUia(this, ApiCall::MoveEndpointByRange, &apiMsg); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::MoveEndpointByRange, &apiMsg); return S_OK; } IFACEMETHODIMP UiaTextRange::Select() { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _pData->UnlockConsole(); }); if (_degenerate) { // calling Select on a degenerate range should clear any current selections - Selection::Instance().ClearSelection(); + _pData->ClearSelection(); } else { COORD coordStart; COORD coordEnd; - coordStart.X = static_cast(_endpointToColumn(_start)); - coordStart.Y = static_cast(_endpointToScreenInfoRow(_start)); + coordStart.X = static_cast(_endpointToColumn(_pData, _start)); + coordStart.Y = static_cast(_endpointToScreenInfoRow(_pData, _start)); - coordEnd.X = static_cast(_endpointToColumn(_end)); - coordEnd.Y = static_cast(_endpointToScreenInfoRow(_end)); + coordEnd.X = static_cast(_endpointToColumn(_pData, _end)); + coordEnd.Y = static_cast(_endpointToScreenInfoRow(_pData, _end)); - Selection::Instance().SelectNewRegion(coordStart, coordEnd); + _pData->SelectNewRegion(coordStart, coordEnd); } - Tracing::s_TraceUia(this, ApiCall::Select, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::Select, nullptr); return S_OK; } // we don't support this IFACEMETHODIMP UiaTextRange::AddToSelection() { - Tracing::s_TraceUia(this, ApiCall::AddToSelection, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::AddToSelection, nullptr); return E_NOTIMPL; } // we don't support this IFACEMETHODIMP UiaTextRange::RemoveFromSelection() { - Tracing::s_TraceUia(this, ApiCall::RemoveFromSelection, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::RemoveFromSelection, nullptr); return E_NOTIMPL; } IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) { - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.LockConsole(); + _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { - gci.UnlockConsole(); + _pData->UnlockConsole(); }); SMALL_RECT oldViewport; @@ -1111,14 +1115,14 @@ IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) ScreenInfoRow bottomRow; try { - oldViewport = _getViewport().ToInclusive(); + oldViewport = _pData->GetViewport().ToInclusive(); viewportHeight = _getViewportHeight(oldViewport); // range rows - startScreenInfoRow = _endpointToScreenInfoRow(_start); - endScreenInfoRow = _endpointToScreenInfoRow(_end); + startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); + endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); // screen buffer rows topRow = _getFirstScreenInfoRowIndex(); - bottomRow = _getLastScreenInfoRowIndex(); + bottomRow = _getLastScreenInfoRowIndex(_pData); } CATCH_RETURN(); @@ -1168,22 +1172,25 @@ IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) try { - IConsoleWindow* pIConsoleWindow = _getIConsoleWindow(); - pIConsoleWindow->ChangeViewport(newViewport); + auto provider = static_cast(_pProvider); + provider->ChangeViewport(newViewport); } CATCH_RETURN(); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree // tracing - ApiMsgScrollIntoView apiMsg; + /*ApiMsgScrollIntoView apiMsg; apiMsg.AlignToTop = !!alignToTop; - Tracing::s_TraceUia(this, ApiCall::ScrollIntoView, &apiMsg); + Tracing::s_TraceUia(this, ApiCall::ScrollIntoView, &apiMsg);*/ return S_OK; } IFACEMETHODIMP UiaTextRange::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { - Tracing::s_TraceUia(this, ApiCall::GetChildren, nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetChildren, nullptr); + // we don't have any children *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 0); if (*ppRetVal == nullptr) @@ -1195,65 +1202,20 @@ IFACEMETHODIMP UiaTextRange::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** p #pragma endregion -// Routine Description: -// - Gets the current viewport -// Arguments: -// - -// Return Value: -// - The screen info's current viewport -const Microsoft::Console::Types::Viewport& UiaTextRange::_getViewport() +const COORD UiaTextRange::_getScreenBufferCoords(Microsoft::Console::Render::IRenderData* pData) { - return _getScreenInfo().GetViewport(); + return pData->GetTextBuffer().GetSize().Dimensions(); } -// Routine Description: -// - Gets the current window -// Arguments: -// - -// Return Value: -// - The current window. May return nullptr if there is no current -// window. -Microsoft::Console::Interactivity::IConsoleWindow* const UiaTextRange::_getIConsoleWindow() +COORD UiaTextRange::_getScreenFontSize() const { - using namespace Microsoft::Console::Interactivity; - IConsoleWindow* const pIConsoleWindow = ServiceLocator::LocateConsoleWindow(); - THROW_HR_IF_NULL(E_POINTER, pIConsoleWindow); - return pIConsoleWindow; -} + COORD coordRet = _pData->GetFontInfo().GetSize(); -// Routine Description: -// - gets the current window handle -// Arguments: -// - -// Return Value -// - the current window handle -HWND UiaTextRange::_getWindowHandle() -{ - return _getIConsoleWindow()->GetWindowHandle(); -} - -// Routine Description: -// - gets the current screen info -// Arguments: -// - -// Return Value -// - the current screen info. May return nullptr. -SCREEN_INFORMATION& UiaTextRange::_getScreenInfo() -{ - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - THROW_HR_IF(E_POINTER, !gci.HasActiveOutputBuffer()); - return gci.GetActiveOutputBuffer().GetActiveBuffer(); -} + // For sanity's sake, make sure not to leak 0 out as a possible value. These values are used in division operations. + coordRet.X = std::max(coordRet.X, 1i16); + coordRet.Y = std::max(coordRet.Y, 1i16); -// Routine Description: -// - gets the current output text buffer -// Arguments: -// - -// Return Value -// - the current output text buffer. May return nullptr. -TextBuffer& UiaTextRange::_getTextBuffer() -{ - return _getScreenInfo().GetTextBuffer(); + return coordRet; } // Routine Description: @@ -1262,20 +1224,9 @@ TextBuffer& UiaTextRange::_getTextBuffer() // - // Return Value: // - The number of rows -const unsigned int UiaTextRange::_getTotalRows() -{ - return _getTextBuffer().TotalRowCount(); -} - -// Routine Description: -// - gets the current screen buffer size. -// Arguments: -// - -// Return Value: -// - The screen buffer size -const COORD UiaTextRange::_getScreenBufferCoords() +const unsigned int UiaTextRange::_getTotalRows(Microsoft::Console::Render::IRenderData* pData) { - return _getScreenInfo().GetBufferSize().Dimensions(); + return pData->GetTextBuffer().TotalRowCount(); } // Routine Description: @@ -1284,10 +1235,10 @@ const COORD UiaTextRange::_getScreenBufferCoords() // - // Return Value: // - The row width -const unsigned int UiaTextRange::_getRowWidth() +const unsigned int UiaTextRange::_getRowWidth(Microsoft::Console::Render::IRenderData* pData) { // make sure that we can't leak a 0 - return std::max(static_cast(_getScreenBufferCoords().X), 1u); + return std::max(static_cast(_getScreenBufferCoords(pData).X), 1u); } // Routine Description: @@ -1296,9 +1247,9 @@ const unsigned int UiaTextRange::_getRowWidth() // - endpoint - the endpoint to translate // Return Value: // - the column value -const Column UiaTextRange::_endpointToColumn(const Endpoint endpoint) +const Column UiaTextRange::_endpointToColumn(Microsoft::Console::Render::IRenderData* pData, const Endpoint endpoint) { - return endpoint % _getRowWidth(); + return endpoint % _getRowWidth(pData); } // Routine Description: @@ -1307,9 +1258,10 @@ const Column UiaTextRange::_endpointToColumn(const Endpoint endpoint) // - endpoint - the endpoint to convert // Return Value: // - the text buffer row value -const TextBufferRow UiaTextRange::_endpointToTextBufferRow(const Endpoint endpoint) +const TextBufferRow UiaTextRange::_endpointToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint) { - return endpoint / _getRowWidth(); + return endpoint / _getRowWidth(pData); } // Routine Description: @@ -1319,19 +1271,19 @@ const TextBufferRow UiaTextRange::_endpointToTextBufferRow(const Endpoint endpoi // - // Return Value: // - The number of rows in the range. -const unsigned int UiaTextRange::_rowCountInRange() const +const unsigned int UiaTextRange::_rowCountInRange(Microsoft::Console::Render::IRenderData* pData) const { if (_degenerate) { return 0; } - const ScreenInfoRow startScreenInfoRow = _endpointToScreenInfoRow(_start); - const Column startColumn = _endpointToColumn(_start); - const ScreenInfoRow endScreenInfoRow = _endpointToScreenInfoRow(_end); - const Column endColumn = _endpointToColumn(_end); + const ScreenInfoRow startScreenInfoRow = _endpointToScreenInfoRow(pData, _start); + const Column startColumn = _endpointToColumn(pData, _start); + const ScreenInfoRow endScreenInfoRow = _endpointToScreenInfoRow(pData, _end); + const Column endColumn = _endpointToColumn(pData, _end); - FAIL_FAST_IF(!(_compareScreenCoords(startScreenInfoRow, startColumn, endScreenInfoRow, endColumn) <= 0)); + FAIL_FAST_IF(!(_compareScreenCoords(pData, startScreenInfoRow, startColumn, endScreenInfoRow, endColumn) <= 0)); // + 1 to balance subtracting ScreenInfoRows from each other return endScreenInfoRow - startScreenInfoRow + 1; @@ -1343,10 +1295,11 @@ const unsigned int UiaTextRange::_rowCountInRange() const // - row - the TextBufferRow to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(const TextBufferRow row) +const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + const TextBufferRow row) { - const int firstRowIndex = _getTextBuffer().GetFirstRowIndex(); - return _normalizeRow(row - firstRowIndex); + const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); + return _normalizeRow(pData, row - firstRowIndex); } // Routine Description: @@ -1356,9 +1309,9 @@ const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(const TextBuffer // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(const ScreenInfoRow row) +const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(Microsoft::Console::Render::IRenderData* pData, const ScreenInfoRow row) { - const SMALL_RECT viewport = _getViewport().ToInclusive(); + const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); } @@ -1383,9 +1336,9 @@ const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(const ScreenInfoRow // - the non-normalized row index // Return Value: // - the normalized row index -const Row UiaTextRange::_normalizeRow(const Row row) +const Row UiaTextRange::_normalizeRow(Microsoft::Console::Render::IRenderData* pData, const Row row) { - const unsigned int totalRows = _getTotalRows(); + const unsigned int totalRows = _getTotalRows(pData); return ((row + totalRows) % totalRows); } @@ -1425,9 +1378,10 @@ const unsigned int UiaTextRange::_getViewportWidth(const SMALL_RECT viewport) // - row - the screen info row to check // Return Value: // - true if the row is within the bounds of the viewport -const bool UiaTextRange::_isScreenInfoRowInViewport(const ScreenInfoRow row) +const bool UiaTextRange::_isScreenInfoRowInViewport(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row) { - return _isScreenInfoRowInViewport(row, _getViewport().ToInclusive()); + return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); } // Routine Description: @@ -1451,10 +1405,11 @@ const bool UiaTextRange::_isScreenInfoRowInViewport(const ScreenInfoRow row, // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent TextBufferRow. -const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(const ScreenInfoRow row) +const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row) { - const TextBufferRow firstRowIndex = _getTextBuffer().GetFirstRowIndex(); - return _normalizeRow(row + firstRowIndex); + const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); + return _normalizeRow(pData, row + firstRowIndex); } // Routine Description: @@ -1463,9 +1418,9 @@ const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(const ScreenInfo // - row - the TextBufferRow to convert // Return Value: // - the equivalent Endpoint, starting at the beginning of the TextBufferRow. -const Endpoint UiaTextRange::_textBufferRowToEndpoint(const TextBufferRow row) +const Endpoint UiaTextRange::_textBufferRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, const TextBufferRow row) { - return _getRowWidth() * row; + return _getRowWidth(pData) * row; } // Routine Description: @@ -1474,9 +1429,10 @@ const Endpoint UiaTextRange::_textBufferRowToEndpoint(const TextBufferRow row) // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent Endpoint. -const Endpoint UiaTextRange::_screenInfoRowToEndpoint(const ScreenInfoRow row) +const Endpoint UiaTextRange::_screenInfoRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row) { - return _textBufferRowToEndpoint(_screenInfoRowToTextBufferRow(row)); + return _textBufferRowToEndpoint(pData, _screenInfoRowToTextBufferRow(pData, row)); } // Routine Description: @@ -1485,9 +1441,10 @@ const Endpoint UiaTextRange::_screenInfoRowToEndpoint(const ScreenInfoRow row) // - endpoint - the endpoint to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(const Endpoint endpoint) +const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint) { - return _textBufferRowToScreenInfoRow(_endpointToTextBufferRow(endpoint)); + return _textBufferRowToScreenInfoRow(pData, _endpointToTextBufferRow(pData, endpoint)); } // Routine Description: @@ -1499,19 +1456,19 @@ const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(const Endpoint endpoi // - // Notes: // - alters coords. may throw an exception. -void UiaTextRange::_addScreenInfoRowBoundaries(const ScreenInfoRow screenInfoRow, +void UiaTextRange::_addScreenInfoRowBoundaries(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const { - const SCREEN_INFORMATION& screenInfo = _getScreenInfo(); - const COORD currentFontSize = screenInfo.GetScreenFontSize(); + const COORD currentFontSize = _getScreenFontSize(); POINT topLeft; POINT bottomRight; - if (_endpointToScreenInfoRow(_start) == screenInfoRow) + if (_endpointToScreenInfoRow(pData, _start) == screenInfoRow) { // start is somewhere in this row so we start from its position - topLeft.x = _endpointToColumn(_start) * currentFontSize.X; + topLeft.x = _endpointToColumn(pData, _start) * currentFontSize.X; } else { @@ -1519,17 +1476,17 @@ void UiaTextRange::_addScreenInfoRowBoundaries(const ScreenInfoRow screenInfoRow topLeft.x = 0; } - topLeft.y = _screenInfoRowToViewportRow(screenInfoRow) * currentFontSize.Y; + topLeft.y = _screenInfoRowToViewportRow(pData, screenInfoRow) * currentFontSize.Y; - if (_endpointToScreenInfoRow(_end) == screenInfoRow) + if (_endpointToScreenInfoRow(pData, _end) == screenInfoRow) { // the endpoints are on the same row - bottomRight.x = (_endpointToColumn(_end) + 1) * currentFontSize.X; + bottomRight.x = (_endpointToColumn(pData, _end) + 1) * currentFontSize.X; } else { // _end is not on this row so span to the end of the row - bottomRight.x = _getViewportWidth(_getViewport().ToInclusive()) * currentFontSize.X; + bottomRight.x = _getViewportWidth(_pData->GetViewport().ToInclusive()) * currentFontSize.X; } // we add the font height only once here because we are adding each line individually @@ -1568,9 +1525,9 @@ const unsigned int UiaTextRange::_getFirstScreenInfoRowIndex() // - // Return Value: // - the index of the last row (0-indexed) of the screen info -const unsigned int UiaTextRange::_getLastScreenInfoRowIndex() +const unsigned int UiaTextRange::_getLastScreenInfoRowIndex(Microsoft::Console::Render::IRenderData* pData) { - return _getTotalRows() - 1; + return _getTotalRows(pData) - 1; } // Routine Description: @@ -1590,9 +1547,9 @@ const Column UiaTextRange::_getFirstColumnIndex() // - // Return Value: // - the index of the last column (0-indexed) of the screen info rows -const Column UiaTextRange::_getLastColumnIndex() +const Column UiaTextRange::_getLastColumnIndex(Microsoft::Console::Render::IRenderData* pData) { - return _getRowWidth() - 1; + return _getRowWidth(pData) - 1; } // Routine Description: @@ -1606,22 +1563,23 @@ const Column UiaTextRange::_getLastColumnIndex() // -1 if A < B // 1 if A > B // 0 if A == B -const int UiaTextRange::_compareScreenCoords(const ScreenInfoRow rowA, +const int UiaTextRange::_compareScreenCoords(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, const Column colB) { FAIL_FAST_IF(!(rowA >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(rowA <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(rowA <= _getLastScreenInfoRowIndex(pData))); FAIL_FAST_IF(!(colA >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(colA <= _getLastColumnIndex())); + FAIL_FAST_IF(!(colA <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(rowB >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(rowB <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(rowB <= _getLastScreenInfoRowIndex(pData))); FAIL_FAST_IF(!(colB >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(colB <= _getLastColumnIndex())); + FAIL_FAST_IF(!(colB <= _getLastColumnIndex(pData))); if (rowA < rowB) { @@ -1654,21 +1612,23 @@ const int UiaTextRange::_compareScreenCoords(const ScreenInfoRow rowA, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByCharacter(const int moveCount, +std::pair UiaTextRange::_moveByCharacter(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) { if (moveState.Direction == MovementDirection::Forward) { - return _moveByCharacterForward(moveCount, moveState, pAmountMoved); + return _moveByCharacterForward(pData, moveCount, moveState, pAmountMoved); } else { - return _moveByCharacterBackward(moveCount, moveState, pAmountMoved); + return _moveByCharacterBackward(pData, moveCount, moveState, pAmountMoved); } } -std::pair UiaTextRange::_moveByCharacterForward(const int moveCount, +std::pair UiaTextRange::_moveByCharacterForward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) { @@ -1680,7 +1640,7 @@ std::pair UiaTextRange::_moveByCharacterForward(const int mo for (int i = 0; i < abs(count); ++i) { // get the current row's right - const ROW& row = _getTextBuffer().GetRowByOffset(currentScreenInfoRow); + const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); // check if we're at the edge of the screen info buffer @@ -1703,17 +1663,18 @@ std::pair UiaTextRange::_moveByCharacterForward(const int mo *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex())); + FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } - Endpoint start = _screenInfoRowToEndpoint(currentScreenInfoRow) + currentColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; Endpoint end = start; return std::make_pair(std::move(start), std::move(end)); } -std::pair UiaTextRange::_moveByCharacterBackward(const int moveCount, +std::pair UiaTextRange::_moveByCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) { @@ -1737,7 +1698,7 @@ std::pair UiaTextRange::_moveByCharacterBackward(const int m currentScreenInfoRow += static_cast(moveState.Increment); // get the right cell for the next row - const ROW& row = _getTextBuffer().GetRowByOffset(currentScreenInfoRow); + const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); currentColumn = static_cast((right == 0) ? 0 : right - 1); } @@ -1749,12 +1710,12 @@ std::pair UiaTextRange::_moveByCharacterBackward(const int m *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex())); + FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } - Endpoint start = _screenInfoRowToEndpoint(currentScreenInfoRow) + currentColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; Endpoint end = start; return std::make_pair(std::move(start), std::move(end)); } @@ -1769,13 +1730,14 @@ std::pair UiaTextRange::_moveByCharacterBackward(const int m // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByLine(const int moveCount, +std::pair UiaTextRange::_moveByLine(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) { *pAmountMoved = 0; - Endpoint start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - Endpoint end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; // we don't want to move the range if we're already in the // limiting row and trying to move off the end of the screen buffer @@ -1796,10 +1758,10 @@ std::pair UiaTextRange::_moveByLine(const int moveCount, *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } - start = _screenInfoRowToEndpoint(currentScreenInfoRow); - end = start + _getLastColumnIndex(); + start = _screenInfoRowToEndpoint(pData, currentScreenInfoRow); + end = start + _getLastColumnIndex(pData); } return std::make_pair(std::move(start), std::move(end)); @@ -1815,7 +1777,8 @@ std::pair UiaTextRange::_moveByLine(const int moveCount, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByDocument(const int /*moveCount*/, +std::pair UiaTextRange::_moveByDocument(Microsoft::Console::Render::IRenderData* pData, + const int /*moveCount*/, const MoveState moveState, _Out_ int* const pAmountMoved) { @@ -1824,8 +1787,8 @@ std::pair UiaTextRange::_moveByDocument(const int /*moveCoun *pAmountMoved = 0; // We then have to return the same endpoints as what we initially had so nothing happens. - Endpoint start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - Endpoint end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; return std::make_pair(std::move(start), std::move(end)); } @@ -1841,23 +1804,25 @@ std::pair UiaTextRange::_moveByDocument(const int /*moveCoun // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitCharacter(const int moveCount, +std::tuple UiaTextRange::_moveEndpointByUnitCharacter(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved) { if (moveState.Direction == MovementDirection::Forward) { - return _moveEndpointByUnitCharacterForward(moveCount, endpoint, moveState, pAmountMoved); + return _moveEndpointByUnitCharacterForward(pData, moveCount, endpoint, moveState, pAmountMoved); } else { - return _moveEndpointByUnitCharacterBackward(moveCount, endpoint, moveState, pAmountMoved); + return _moveEndpointByUnitCharacterBackward(pData, moveCount, endpoint, moveState, pAmountMoved); } } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterForward(const int moveCount, +UiaTextRange::_moveEndpointByUnitCharacterForward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1882,7 +1847,7 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(const int moveCount, for (int i = 0; i < abs(count); ++i) { // get the current row's right - const ROW& row = _getTextBuffer().GetRowByOffset(currentScreenInfoRow); + const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); // check if we're at the edge of the screen info buffer @@ -1905,20 +1870,21 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(const int moveCount, *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex())); + FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(currentScreenInfoRow) + currentColumn; - Endpoint start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - Endpoint end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; bool degenerate = false; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { start = convertedEndpoint; - if (_compareScreenCoords(currentScreenInfoRow, + if (_compareScreenCoords(pData, + currentScreenInfoRow, currentColumn, moveState.EndScreenInfoRow, moveState.EndColumn) == 1) @@ -1930,7 +1896,8 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(const int moveCount, else { end = convertedEndpoint; - if (_compareScreenCoords(currentScreenInfoRow, + if (_compareScreenCoords(pData, + currentScreenInfoRow, currentColumn, moveState.StartScreenInfoRow, moveState.StartColumn) == -1) @@ -1943,7 +1910,8 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(const int moveCount, } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterBackward(const int moveCount, +UiaTextRange::_moveEndpointByUnitCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1980,7 +1948,7 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(const int moveCount, currentScreenInfoRow += static_cast(moveState.Increment); // get the right cell for the next row - const ROW& row = _getTextBuffer().GetRowByOffset(currentScreenInfoRow); + const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); currentColumn = static_cast((right == 0) ? 0 : right - 1); } @@ -1992,20 +1960,21 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(const int moveCount, *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex())); + FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(currentScreenInfoRow) + currentColumn; - Endpoint start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - Endpoint end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; bool degenerate = false; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { start = convertedEndpoint; - if (_compareScreenCoords(currentScreenInfoRow, + if (_compareScreenCoords(pData, + currentScreenInfoRow, currentColumn, moveState.EndScreenInfoRow, moveState.EndColumn) == 1) @@ -2017,7 +1986,8 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(const int moveCount, else { end = convertedEndpoint; - if (_compareScreenCoords(currentScreenInfoRow, + if (_compareScreenCoords(pData, + currentScreenInfoRow, currentColumn, moveState.StartScreenInfoRow, moveState.StartColumn) == -1) @@ -2040,7 +2010,8 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(const int moveCount, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitLine(const int moveCount, +std::tuple UiaTextRange::_moveEndpointByUnitLine(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -2050,8 +2021,8 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const ScreenInfoRow currentScreenInfoRow; Column currentColumn; bool forceDegenerate = false; - Endpoint start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - Endpoint end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; bool degenerate = false; if (moveCount == 0) @@ -2087,7 +2058,7 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const // the very end. move to the end of the last row count -= static_cast(moveState.Increment); *pAmountMoved += static_cast(moveState.Increment); - currentColumn = _getLastColumnIndex(); + currentColumn = _getLastColumnIndex(pData); forceDegenerate = true; } if (moveState.StartColumn != _getFirstColumnIndex()) @@ -2115,13 +2086,13 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const else if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_End && moveDirection == MovementDirection::Forward) { - if (moveState.EndColumn != _getLastColumnIndex()) + if (moveState.EndColumn != _getLastColumnIndex(pData)) { // _end is not at the last column in a row, so we move // forward to it with a partial movement count -= static_cast(moveState.Increment); *pAmountMoved += static_cast(moveState.Increment); - currentColumn = _getLastColumnIndex(); + currentColumn = _getLastColumnIndex(pData); } } else @@ -2136,21 +2107,21 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const currentColumn = _getFirstColumnIndex(); forceDegenerate = true; } - else if (moveState.EndColumn != _getLastColumnIndex()) + else if (moveState.EndColumn != _getLastColumnIndex(pData)) { // _end is not at the last column in a row, so we move it // backwards to it with a partial move count -= static_cast(moveState.Increment); *pAmountMoved += static_cast(moveState.Increment); - currentColumn = _getLastColumnIndex(); + currentColumn = _getLastColumnIndex(pData); currentScreenInfoRow += static_cast(moveState.Increment); } } FAIL_FAST_IF(!(currentColumn >= _getFirstColumnIndex())); - FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex())); + FAIL_FAST_IF(!(currentColumn <= _getLastColumnIndex(pData))); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); // move the row that the endpoint corresponds to while (count != 0 && currentScreenInfoRow != moveState.LimitingRow) @@ -2160,11 +2131,11 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const *pAmountMoved += static_cast(moveState.Increment); FAIL_FAST_IF(!(currentScreenInfoRow >= _getFirstScreenInfoRowIndex())); - FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex())); + FAIL_FAST_IF(!(currentScreenInfoRow <= _getLastScreenInfoRowIndex(pData))); } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(currentScreenInfoRow) + currentColumn; + Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { start = convertedEndpoint; @@ -2198,7 +2169,8 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(const // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitDocument(const int moveCount, +std::tuple UiaTextRange::_moveEndpointByUnitDocument(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -2213,8 +2185,8 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(c if (moveCount < 0) { // moving _start backwards - start = _screenInfoRowToEndpoint(_getFirstScreenInfoRowIndex()) + _getFirstColumnIndex(); - end = _screenInfoRowToEndpoint(moveState.EndScreenInfoRow) + moveState.EndColumn; + start = _screenInfoRowToEndpoint(pData, _getFirstScreenInfoRowIndex()) + _getFirstColumnIndex(); + end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; if (!(moveState.StartScreenInfoRow == _getFirstScreenInfoRowIndex() && moveState.StartColumn == _getFirstColumnIndex())) { @@ -2224,11 +2196,11 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(c else { // moving _start forwards - start = _screenInfoRowToEndpoint(_getLastScreenInfoRowIndex()) + _getLastColumnIndex(); + start = _screenInfoRowToEndpoint(pData, _getLastScreenInfoRowIndex(pData)) + _getLastColumnIndex(pData); end = start; degenerate = true; - if (!(moveState.StartScreenInfoRow == _getLastScreenInfoRowIndex() && - moveState.StartColumn == _getLastColumnIndex())) + if (!(moveState.StartScreenInfoRow == _getLastScreenInfoRowIndex(pData) && + moveState.StartColumn == _getLastColumnIndex(pData))) { *pAmountMoved += static_cast(moveState.Increment); } @@ -2239,7 +2211,7 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(c if (moveCount < 0) { // moving _end backwards - end = _screenInfoRowToEndpoint(_getFirstScreenInfoRowIndex()) + _getFirstColumnIndex(); + end = _screenInfoRowToEndpoint(pData, _getFirstScreenInfoRowIndex()) + _getFirstColumnIndex(); start = end; degenerate = true; if (!(moveState.EndScreenInfoRow == _getFirstScreenInfoRowIndex() && @@ -2251,10 +2223,10 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(c else { // moving _end forwards - end = _screenInfoRowToEndpoint(_getLastScreenInfoRowIndex()) + _getLastColumnIndex(); - start = _screenInfoRowToEndpoint(moveState.StartScreenInfoRow) + moveState.StartColumn; - if (!(moveState.EndScreenInfoRow == _getLastScreenInfoRowIndex() && - moveState.EndColumn == _getLastColumnIndex())) + end = _screenInfoRowToEndpoint(pData, _getLastScreenInfoRowIndex(pData)) + _getLastColumnIndex(pData); + start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; + if (!(moveState.EndScreenInfoRow == _getLastScreenInfoRowIndex(pData) && + moveState.EndColumn == _getLastColumnIndex(pData))) { *pAmountMoved += static_cast(moveState.Increment); } @@ -2264,12 +2236,35 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(c return std::make_tuple(start, end, degenerate); } -COORD UiaTextRange::_endpointToCoord(const Endpoint endpoint) +COORD UiaTextRange::_endpointToCoord(Microsoft::Console::Render::IRenderData* pData, const Endpoint endpoint) +{ + return { gsl::narrow(_endpointToColumn(pData, endpoint)), gsl::narrow(_endpointToScreenInfoRow(pData, endpoint)) }; +} + +Endpoint UiaTextRange::_coordToEndpoint(Microsoft::Console::Render::IRenderData* pData, + const COORD coord) { - return { gsl::narrow(_endpointToColumn(endpoint)), gsl::narrow(_endpointToScreenInfoRow(endpoint)) }; + return _screenInfoRowToEndpoint(pData, coord.Y) + coord.X; +} + +RECT UiaTextRange::_getTerminalRect() const +{ + UiaRect result; + + IRawElementProviderFragment* pRawElementProviderFragment; + THROW_IF_FAILED(_pProvider->QueryInterface(&pRawElementProviderFragment)); + pRawElementProviderFragment->get_BoundingRectangle(&result); + + return { + gsl::narrow(result.left), + gsl::narrow(result.top), + gsl::narrow(result.left + result.width), + gsl::narrow(result.top + result.height) + }; } -Endpoint UiaTextRange::_coordToEndpoint(const COORD coord) +HWND UiaTextRange::_getWindowHandle() const { - return _screenInfoRowToEndpoint(coord.Y) + coord.X; + const auto provider = static_cast(_pProvider); + return provider->GetWindowHandle(); } diff --git a/src/interactivity/win32/UiaTextRange.hpp b/src/types/UiaTextRange.hpp similarity index 68% rename from src/interactivity/win32/UiaTextRange.hpp rename to src/types/UiaTextRange.hpp index 7a0e716db4a..a8a100ff4cb 100644 --- a/src/interactivity/win32/UiaTextRange.hpp +++ b/src/types/UiaTextRange.hpp @@ -9,18 +9,20 @@ Module Name: - This module provides UI Automation access to the text of the console window to support both automation tests and accessibility (screen reading) applications. +- ConHost and Windows Terminal must implement their own virtual functions separately. Author(s): - Austin Diviness (AustDi) 2017 +- Carlos Zamora (CaZamor) 2019 --*/ #pragma once #include "precomp.h" -#include "../inc/IConsoleWindow.hpp" -#include "../types/inc/viewport.hpp" -#include "../../buffer/out/cursor.h" +#include "inc/viewport.hpp" +#include "../buffer/out/textBuffer.hpp" +#include "../renderer/inc/IRenderData.hpp" #include #include @@ -69,7 +71,7 @@ typedef unsigned int Endpoint; constexpr IdType InvalidId = 0; -namespace Microsoft::Console::Interactivity::Win32 +namespace Microsoft::Console::Types { class UiaTextRange final : public ITextRangeProvider { @@ -114,7 +116,8 @@ namespace Microsoft::Console::Interactivity::Win32 // direction moving MovementDirection Direction; - MoveState(const UiaTextRange& range, + MoveState(Microsoft::Console::Render::IRenderData* pData, + const UiaTextRange& range, const MovementDirection direction); private: @@ -134,23 +137,27 @@ namespace Microsoft::Console::Interactivity::Win32 }; public: - static std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider); + static std::deque GetSelectionRanges(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ IRawElementProviderSimple* pProvider); // degenerate range - static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider); + static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider); // degenerate range at cursor position - static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider, + static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor); // specific endpoint range - static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider, + static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate); // range from a UiaPoint - static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider, + static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point); ~UiaTextRange(); @@ -160,6 +167,10 @@ namespace Microsoft::Console::Interactivity::Win32 const Endpoint GetEnd() const; const bool IsDegenerate() const; + // TODO GitHub #605: + // only used for RenderData::FindText. Remove after Search added properly + void SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate); + // IUnknown methods IFACEMETHODIMP_(ULONG) AddRef(); @@ -208,28 +219,36 @@ namespace Microsoft::Console::Interactivity::Win32 protected: #if _DEBUG - void _outputRowConversions(); + void _outputRowConversions(Microsoft::Console::Render::IRenderData* pData); void _outputObjectState(); #endif + Microsoft::Console::Render::IRenderData* const _pData; IRawElementProviderSimple* const _pProvider; + RECT _getTerminalRect() const; + HWND _getWindowHandle() const; + private: // degenerate range - UiaTextRange(_In_ IRawElementProviderSimple* const pProvider); + UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider); // degenerate range at cursor position - UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, + UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor); // specific endpoint range - UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, + UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate); // range from a UiaPoint - UiaTextRange(_In_ IRawElementProviderSimple* const pProvider, + UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point); UiaTextRange(const UiaTextRange& a); @@ -264,105 +283,123 @@ namespace Microsoft::Console::Interactivity::Win32 // then both endpoints will contain the same value. bool _degenerate; - static const Microsoft::Console::Types::Viewport& _getViewport(); - static HWND _getWindowHandle(); - static IConsoleWindow* const _getIConsoleWindow(); - static SCREEN_INFORMATION& _getScreenInfo(); - static TextBuffer& _getTextBuffer(); - static const COORD _getScreenBufferCoords(); + static const COORD _getScreenBufferCoords(Microsoft::Console::Render::IRenderData* pData); + COORD _getScreenFontSize() const; - static const unsigned int _getTotalRows(); - static const unsigned int _getRowWidth(); + static const unsigned int _getTotalRows(Microsoft::Console::Render::IRenderData* pData); + static const unsigned int _getRowWidth(Microsoft::Console::Render::IRenderData* pData); static const unsigned int _getFirstScreenInfoRowIndex(); - static const unsigned int _getLastScreenInfoRowIndex(); + static const unsigned int _getLastScreenInfoRowIndex(Microsoft::Console::Render::IRenderData* pData); static const Column _getFirstColumnIndex(); - static const Column _getLastColumnIndex(); + static const Column _getLastColumnIndex(Microsoft::Console::Render::IRenderData* pData); - const unsigned int _rowCountInRange() const; + const unsigned int _rowCountInRange(Microsoft::Console::Render::IRenderData* pData) const; - static const TextBufferRow _endpointToTextBufferRow(const Endpoint endpoint); - static const ScreenInfoRow _textBufferRowToScreenInfoRow(const TextBufferRow row); + static const TextBufferRow _endpointToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint); + static const ScreenInfoRow _textBufferRowToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + const TextBufferRow row); - static const TextBufferRow _screenInfoRowToTextBufferRow(const ScreenInfoRow row); - static const Endpoint _textBufferRowToEndpoint(const TextBufferRow row); + static const TextBufferRow _screenInfoRowToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row); + static const Endpoint _textBufferRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, const TextBufferRow row); - static const ScreenInfoRow _endpointToScreenInfoRow(const Endpoint endpoint); - static const Endpoint _screenInfoRowToEndpoint(const ScreenInfoRow row); + static const ScreenInfoRow _endpointToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint); + static const Endpoint _screenInfoRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row); - static COORD _endpointToCoord(const Endpoint endpoint); - static Endpoint _coordToEndpoint(const COORD coord); + static COORD _endpointToCoord(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint); + static Endpoint _coordToEndpoint(Microsoft::Console::Render::IRenderData* pData, + const COORD coord); - static const Column _endpointToColumn(const Endpoint endpoint); + static const Column _endpointToColumn(Microsoft::Console::Render::IRenderData* pData, + const Endpoint endpoint); - static const Row _normalizeRow(const Row row); + static const Row _normalizeRow(Microsoft::Console::Render::IRenderData* pData, const Row row); - static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row); + static const ViewportRow _screenInfoRowToViewportRow(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row); static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, const SMALL_RECT viewport); - static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row); + static const bool _isScreenInfoRowInViewport(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow row); static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row, const SMALL_RECT viewport); static const unsigned int _getViewportHeight(const SMALL_RECT viewport); static const unsigned int _getViewportWidth(const SMALL_RECT viewport); - void _addScreenInfoRowBoundaries(const ScreenInfoRow screenInfoRow, + void _addScreenInfoRowBoundaries(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const; - static const int _compareScreenCoords(const ScreenInfoRow rowA, + static const int _compareScreenCoords(Microsoft::Console::Render::IRenderData* pData, + const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, const Column colB); - static std::pair _moveByCharacter(const int moveCount, + static std::pair _moveByCharacter(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByCharacterForward(const int moveCount, + static std::pair _moveByCharacterForward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByCharacterBackward(const int moveCount, + static std::pair _moveByCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByLine(const int moveCount, + static std::pair _moveByLine(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByDocument(const int moveCount, + static std::pair _moveByDocument(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacter(const int moveCount, + _moveEndpointByUnitCharacter(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterForward(const int moveCount, + _moveEndpointByUnitCharacterForward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterBackward(const int moveCount, + _moveEndpointByUnitCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitLine(const int moveCount, + _moveEndpointByUnitLine(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitDocument(const int moveCount, + _moveEndpointByUnitDocument(Microsoft::Console::Render::IRenderData* pData, + const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp new file mode 100644 index 00000000000..5b175b14e1b --- /dev/null +++ b/src/types/WindowUiaProviderBase.cpp @@ -0,0 +1,238 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "IUiaWindow.h" +#include "WindowUiaProviderBase.hpp" +#include "ScreenInfoUiaProvider.h" + +using namespace Microsoft::Console::Types; + +WindowUiaProviderBase::WindowUiaProviderBase(IUiaWindow* baseWindow) : + _signalEventFiring{}, + _baseWindow{ baseWindow }, + _cRefs(1) +{ +} + +#pragma region IUnknown + +IFACEMETHODIMP_(ULONG) +WindowUiaProviderBase::AddRef() +{ + return InterlockedIncrement(&_cRefs); +} + +IFACEMETHODIMP_(ULONG) +WindowUiaProviderBase::Release() +{ + long val = InterlockedDecrement(&_cRefs); + if (val == 0) + { + delete this; + } + return val; +} + +IFACEMETHODIMP WindowUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) +{ + if (riid == __uuidof(IUnknown)) + { + *ppInterface = static_cast(this); + } + else if (riid == __uuidof(IRawElementProviderSimple)) + { + *ppInterface = static_cast(this); + } + else if (riid == __uuidof(IRawElementProviderFragment)) + { + *ppInterface = static_cast(this); + } + else if (riid == __uuidof(IRawElementProviderFragmentRoot)) + { + *ppInterface = static_cast(this); + } + else + { + *ppInterface = nullptr; + return E_NOINTERFACE; + } + + (static_cast(*ppInterface))->AddRef(); + + return S_OK; +} + +#pragma endregion + +#pragma region IRawElementProviderSimple + +// Implementation of IRawElementProviderSimple::get_ProviderOptions. +// Gets UI Automation provider options. +IFACEMETHODIMP WindowUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + *pOptions = ProviderOptions_ServerSideProvider; + return S_OK; +} + +// Implementation of IRawElementProviderSimple::get_PatternProvider. +// Gets the object that supports ISelectionPattern. +IFACEMETHODIMP WindowUiaProviderBase::GetPatternProvider(_In_ PATTERNID /*patternId*/, + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) +{ + *ppInterface = nullptr; + RETURN_IF_FAILED(_EnsureValidHwnd()); + + return S_OK; +} + +// Implementation of IRawElementProviderSimple::get_PropertyValue. +// Gets custom properties. +IFACEMETHODIMP WindowUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyId, _Out_ VARIANT* pVariant) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + pVariant->vt = VT_EMPTY; + + // Returning the default will leave the property as the default + // so we only really need to touch it for the properties we want to implement + if (propertyId == UIA_ControlTypePropertyId) + { + pVariant->vt = VT_I4; + pVariant->lVal = UIA_WindowControlTypeId; + } + else if (propertyId == UIA_AutomationIdPropertyId) + { + pVariant->bstrVal = SysAllocString(AutomationIdPropertyName); + if (pVariant->bstrVal != nullptr) + { + pVariant->vt = VT_BSTR; + } + } + else if (propertyId == UIA_IsControlElementPropertyId) + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = VARIANT_TRUE; + } + else if (propertyId == UIA_IsContentElementPropertyId) + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = VARIANT_TRUE; + } + else if (propertyId == UIA_IsKeyboardFocusablePropertyId) + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = VARIANT_TRUE; + } + else if (propertyId == UIA_HasKeyboardFocusPropertyId) + { + pVariant->vt = VT_BOOL; + pVariant->boolVal = VARIANT_TRUE; + } + else if (propertyId == UIA_ProviderDescriptionPropertyId) + { + pVariant->bstrVal = SysAllocString(ProviderDescriptionPropertyName); + if (pVariant->bstrVal != nullptr) + { + pVariant->vt = VT_BSTR; + } + } + + return S_OK; +} + +// Implementation of IRawElementProviderSimple::get_HostRawElementProvider. +// Gets the default UI Automation provider for the host window. This provider +// supplies many properties. +IFACEMETHODIMP WindowUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) +{ + try + { + const HWND hwnd = GetWindowHandle(); + return UiaHostProviderFromHwnd(hwnd, ppProvider); + } + catch (...) + { + return static_cast(UIA_E_ELEMENTNOTAVAILABLE); + } +} +#pragma endregion + +#pragma region IRawElementProviderFragment + +IFACEMETHODIMP WindowUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + // Root defers this to host, others must implement it... + *ppRuntimeId = nullptr; + + return S_OK; +} + +IFACEMETHODIMP WindowUiaProviderBase::get_BoundingRectangle(_Out_ UiaRect* pRect) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + const IUiaWindow* const pConsoleWindow = _baseWindow; + RETURN_HR_IF_NULL((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, pConsoleWindow); + + RECT const rc = pConsoleWindow->GetWindowRect(); + + pRect->left = rc.left; + pRect->top = rc.top; + pRect->width = rc.right - rc.left; + pRect->height = rc.bottom - rc.top; + + return S_OK; +} + +IFACEMETHODIMP WindowUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + *ppRoots = nullptr; + return S_OK; +} + +IFACEMETHODIMP WindowUiaProviderBase::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) +{ + RETURN_IF_FAILED(_EnsureValidHwnd()); + + *ppProvider = this; + AddRef(); + return S_OK; +} + +#pragma endregion + +HWND WindowUiaProviderBase::GetWindowHandle() const +{ + IUiaWindow* const pConsoleWindow = _baseWindow; + THROW_HR_IF_NULL(E_POINTER, pConsoleWindow); + + return pConsoleWindow->GetWindowHandle(); +} + +[[nodiscard]] HRESULT WindowUiaProviderBase::_EnsureValidHwnd() const +{ + try + { + HWND const hwnd = GetWindowHandle(); + RETURN_HR_IF((HRESULT)UIA_E_ELEMENTNOTAVAILABLE, !(IsWindow(hwnd))); + } + CATCH_RETURN(); + return S_OK; +} + +void WindowUiaProviderBase::ChangeViewport(const SMALL_RECT NewWindow) +{ + _baseWindow->ChangeViewport(NewWindow); +} + +RECT WindowUiaProviderBase::GetWindowRect() const noexcept +{ + return _baseWindow->GetWindowRect(); +} diff --git a/src/types/WindowUiaProviderBase.hpp b/src/types/WindowUiaProviderBase.hpp new file mode 100644 index 00000000000..ac2d3838ff6 --- /dev/null +++ b/src/types/WindowUiaProviderBase.hpp @@ -0,0 +1,139 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- WindowUiaProviderBase.hpp + +Abstract: +- This module provides UI Automation access to the console window to + support both automation tests and accessibility (screen reading) + applications. +- Based on examples, sample code, and guidance from + https://msdn.microsoft.com/en-us/library/windows/desktop/ee671596(v=vs.85).aspx + +Author(s): +- Michael Niksa (MiNiksa) 2017 +- Austin Diviness (AustDi) 2017 +- Carlos Zamora (cazamor) 2019 +--*/ + +#pragma once + +#include "precomp.h" + +namespace Microsoft::Console::Types +{ + class IUiaWindow; + class ScreenInfoUiaProvider; + + class WindowUiaProviderBase : + public IRawElementProviderSimple, + public IRawElementProviderFragment, + public IRawElementProviderFragmentRoot + { + public: + [[nodiscard]] virtual HRESULT Signal(_In_ EVENTID id) = 0; + [[nodiscard]] virtual HRESULT SetTextAreaFocus() = 0; + + // IUnknown methods + IFACEMETHODIMP_(ULONG) + AddRef(); + IFACEMETHODIMP_(ULONG) + Release(); + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, + _COM_Outptr_result_maybenull_ void** ppInterface); + + // IRawElementProviderSimple methods + IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions); + IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, + _COM_Outptr_result_maybenull_ IUnknown** ppInterface); + IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, + _Out_ VARIANT* pVariant); + IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider); + + // IRawElementProviderFragment methods + virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; + IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId); + IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect); + IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots); + virtual IFACEMETHODIMP SetFocus() = 0; + IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider); + + // IRawElementProviderFragmentRoot methods + virtual IFACEMETHODIMP ElementProviderFromPoint(_In_ double x, + _In_ double y, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; + virtual IFACEMETHODIMP GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; + + WindowUiaProviderBase(IUiaWindow* baseWindow); + + RECT GetWindowRect() const noexcept; + HWND GetWindowHandle() const; + void ChangeViewport(const SMALL_RECT NewWindow); + + protected: + // this is used to prevent the object from + // signaling an event while it is already in the + // process of signalling another event. + // This fixes a problem with JAWS where it would + // call a public method that calls + // UiaRaiseAutomationEvent to signal something + // happened, which JAWS then detects the signal + // and calls the same method in response, + // eventually overflowing the stack. + // We aren't using this as a cheap locking + // mechanism for multi-threaded code. + std::map _signalEventFiring; + + [[nodiscard]] HRESULT _EnsureValidHwnd() const; + + const OLECHAR* AutomationIdPropertyName = L"Console Window"; + const OLECHAR* ProviderDescriptionPropertyName = L"Microsoft Console Host Window"; + + private: + // Ref counter for COM object + ULONG _cRefs; + + IUiaWindow* _baseWindow; + }; + + namespace WindowUiaProviderTracing + { + enum class ApiCall + { + Create, + Signal, + AddRef, + Release, + QueryInterface, + GetProviderOptions, + GetPatternProvider, + GetPropertyValue, + GetHostRawElementProvider, + Navigate, + GetRuntimeId, + GetBoundingRectangle, + GetEmbeddedFragmentRoots, + SetFocus, + GetFragmentRoot, + ElementProviderFromPoint, + GetFocus + }; + + struct IApiMsg + { + }; + + struct ApiMessageSignal : public IApiMsg + { + EVENTID Signal; + }; + + struct ApiMsgNavigate : public IApiMsg + { + NavigateDirection Direction; + }; + } +} diff --git a/src/types/lib/types.vcxproj b/src/types/lib/types.vcxproj index 1233c952312..5459cd572b7 100644 --- a/src/types/lib/types.vcxproj +++ b/src/types/lib/types.vcxproj @@ -1,4 +1,4 @@ - + @@ -11,6 +11,8 @@ + + @@ -19,8 +21,10 @@ Create + + @@ -28,8 +32,12 @@ + + + + {18D09A24-8240-42D6-8CB6-236EEE820263} @@ -41,4 +49,4 @@ - + \ No newline at end of file diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index ec5098ad2b7..11b565e87f6 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -1,96 +1,42 @@  - - {4FC737F1-C7A5-4376-A066-2A32D752A3FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52ECFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {77DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - + - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/types/precomp.h b/src/types/precomp.h index 972fde4ea73..b8b77ae4d6c 100644 --- a/src/types/precomp.h +++ b/src/types/precomp.h @@ -29,39 +29,14 @@ Module Name: // Windows Header Files: #include +#include +#include #include #include // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" -typedef long NTSTATUS; -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define STATUS_SHARING_VIOLATION ((NTSTATUS)0xC0000043L) -#define STATUS_INSUFFICIENT_RESOURCES ((DWORD)0xC000009AL) -#define STATUS_ILLEGAL_FUNCTION ((DWORD)0xC00000AFL) -#define STATUS_PIPE_DISCONNECTED ((DWORD)0xC00000B0L) -#define STATUS_BUFFER_TOO_SMALL ((DWORD)0xC0000023L) - -// -// Map a WIN32 error value into an NTSTATUS -// Note: This assumes that WIN32 errors fall in the range -32k to 32k. -// - -#define FACILITY_NTWIN32 0x7 - -#define __NTSTATUS_FROM_WIN32(x) ((NTSTATUS)(x) <= 0 ? ((NTSTATUS)(x)) : ((NTSTATUS) (((x) & 0x0000FFFF) | (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_ERROR))) - -#ifdef INLINE_NTSTATUS_FROM_WIN32 -#ifndef __midl -__inline NTSTATUS_FROM_WIN32(long x) { return x <= 0 ? (NTSTATUS)x : (NTSTATUS)(((x) & 0x0000FFFF) | (FACILITY_NTWIN32 << 16) | ERROR_SEVERITY_ERROR); } -#else -#define NTSTATUS_FROM_WIN32(x) __NTSTATUS_FROM_WIN32(x) -#endif -#else -#define NTSTATUS_FROM_WIN32(x) __NTSTATUS_FROM_WIN32(x) -#endif - #include #pragma prefast(push) #pragma prefast(disable:26071, "Range violation in Intsafe. Not ours.") @@ -81,11 +56,4 @@ __inline NTSTATUS_FROM_WIN32(long x) { return x <= 0 ? (NTSTATUS)x : (NTSTATUS)( #include #include -// TODO: MSFT 9355094 Find a better way of doing this. http://osgvsowi/9355094 -[[nodiscard]] -constexpr NTSTATUS NTSTATUS_FROM_HRESULT(HRESULT hr) noexcept -{ - return NTSTATUS_FROM_WIN32(HRESULT_CODE(hr)); -} - // clang-format on From 3f62c8b47037418fb7bdff7539fb89e09a2401d1 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Mon, 29 Jul 2019 17:24:20 -0700 Subject: [PATCH 002/154] Add some ETL around profile, control and connection creation (#2125) This commit adds some tracelogging (and telemetry) to answer the following questions: * Do people use padding? If so, what is the common range of values? * Are people turning off showTabsInTitlebar? * How many different profiles are in use, and how do they break down between custom and default? * Are people manually launching specific profiles, or using "default" fairly often? * Are people using the Azure Cloud Shell connection? * Are people leveraging the feature added in #2108 (autogenerating GUIDs)? --- src/cascadia/TerminalApp/App.cpp | 41 +++++++++++-------- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalApp/Profile.cpp | 7 ++++ src/cascadia/TerminalApp/TerminalApp.vcxproj | 4 ++ src/cascadia/TerminalApp/init.cpp | 31 ++++++++++++++ .../TerminalApp/lib/TerminalAppLib.vcxproj | 1 + src/cascadia/TerminalApp/lib/pch.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 19 ++++++++- .../TerminalControl/TerminalControl.vcxproj | 1 + src/cascadia/TerminalControl/init.cpp | 31 ++++++++++++++ src/cascadia/TerminalControl/pch.h | 4 ++ 11 files changed, 122 insertions(+), 21 deletions(-) create mode 100644 src/cascadia/TerminalApp/init.cpp create mode 100644 src/cascadia/TerminalControl/init.cpp diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 37bbad4640d..a822f7b6f13 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -19,14 +19,6 @@ using namespace winrt::Microsoft::Terminal::TerminalControl; using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace ::TerminalApp; -// Note: Generate GUID using TlgGuid.exe tool -TRACELOGGING_DEFINE_PROVIDER( - g_hTerminalAppProvider, - "Microsoft.Windows.Terminal.App", - // {24a1622f-7da7-5c77-3303-d850bd1ab2ed} - (0x24a1622f, 0x7da7, 0x5c77, 0x33, 0x03, 0xd8, 0x50, 0xbd, 0x1a, 0xb2, 0xed), - TraceLoggingOptionMicrosoftTelemetry()); - namespace winrt { namespace MUX = Microsoft::UI::Xaml; @@ -67,7 +59,6 @@ namespace winrt::TerminalApp::implementation // Assert that we've already loaded our settings. We have to do // this as a MTA, before the app is Create()'d WINRT_ASSERT(_loadedInitialSettings); - TraceLoggingRegister(g_hTerminalAppProvider); /* !!! TODO This is not the correct way to host a XAML page. This exists today because we valued @@ -112,14 +103,14 @@ namespace winrt::TerminalApp::implementation _tabContent.SizeChanged({ this, &App::_OnContentSizeChanged }); _ApplyTheme(_settings->GlobalSettings().GetRequestedTheme()); - } - App::~App() - { - if (g_hTerminalAppProvider) - { - TraceLoggingUnregister(g_hTerminalAppProvider); - } + TraceLoggingWrite( + g_hTerminalAppProvider, + "AppCreated", + TraceLoggingDescription("Event emitted when the application is started"), + TraceLoggingBool(_settings->GlobalSettings().GetShowTabsInTitlebar(), "TabsInTitlebar"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); } // Method Description: @@ -827,6 +818,8 @@ namespace winrt::TerminalApp::implementation "TabInformation", TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"), TraceLoggingInt32(tabCount, "TabCount", "Count of tabs curently opened in TerminalApp"), + TraceLoggingBool(profileIndex.has_value(), "ProfileSpecified", "Whether the new tab specified a profile explicitly"), + TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); } @@ -1470,8 +1463,13 @@ namespace winrt::TerminalApp::implementation TerminalConnection::ITerminalConnection connection{ nullptr }; // The Azure connection has a boost dependency, and boost does not support ARM64 // so we make sure that we do not try to compile the Azure connection code if we are in ARM64 (we would get build errors otherwise) + GUID connectionType{ 0 }; + if (profile->HasConnectionType()) + { + connectionType = profile->GetConnectionType(); + } #ifndef _M_ARM64 - if (profile->HasConnectionType() && profile->GetConnectionType() == AzureConnectionType) + if (connectionType == AzureConnectionType) { connection = TerminalConnection::AzureConnection(settings.InitialRows(), settings.InitialCols()); } @@ -1480,6 +1478,15 @@ namespace winrt::TerminalApp::implementation { connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), settings.InitialRows(), settings.InitialCols(), winrt::guid()); } + + TraceLoggingWrite( + g_hTerminalAppProvider, + "ConnectionCreated", + TraceLoggingDescription("Event emitted upon the creation of a connection"), + TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + return connection; } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 390f195507c..473f220d12c 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -34,7 +34,7 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); bool GetShowTabsInTitlebar(); - ~App(); + ~App() = default; hstring GetTitle(); diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index b73baf20609..cf605eef53b 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -353,6 +353,13 @@ Profile Profile::FromJson(const Json::Value& json) else { result._guid = Utils::CreateGuid(); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "SynthesizedGuidForProfile", + TraceLoggingDescription("Event emitted when a profile is deserialized without a GUID"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); } // Core Settings diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 05957cdb670..0cf14d335af 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -99,6 +99,10 @@ User32.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) + + + /INCLUDE:_DllMain@12 + /INCLUDE:DllMain diff --git a/src/cascadia/TerminalApp/init.cpp b/src/cascadia/TerminalApp/init.cpp new file mode 100644 index 00000000000..d6b7eb06ac5 --- /dev/null +++ b/src/cascadia/TerminalApp/init.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" + +// Note: Generate GUID using TlgGuid.exe tool +TRACELOGGING_DEFINE_PROVIDER( + g_hTerminalAppProvider, + "Microsoft.Windows.Terminal.App", + // {24a1622f-7da7-5c77-3303-d850bd1ab2ed} + (0x24a1622f, 0x7da7, 0x5c77, 0x33, 0x03, 0xd8, 0x50, 0xbd, 0x1a, 0xb2, 0xed), + TraceLoggingOptionMicrosoftTelemetry()); + +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + TraceLoggingRegister(g_hTerminalAppProvider); + break; + case DLL_PROCESS_DETACH: + if (g_hTerminalAppProvider) + { + TraceLoggingUnregister(g_hTerminalAppProvider); + } + break; + } + + return TRUE; +} diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj index ea47ae07b21..08b58cc53e6 100644 --- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj @@ -77,6 +77,7 @@ + ../MinMaxCloseControl.xaml diff --git a/src/cascadia/TerminalApp/lib/pch.h b/src/cascadia/TerminalApp/lib/pch.h index ee26a863b0e..f42dce36c69 100644 --- a/src/cascadia/TerminalApp/lib/pch.h +++ b/src/cascadia/TerminalApp/lib/pch.h @@ -45,7 +45,7 @@ // Including TraceLogging essentials for the binary #include #include -TRACELOGGING_DECLARE_PROVIDER(g_hTerminalWin32Provider); +TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider); #include #include diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index c7e90c826a1..14e3b776c40 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -185,8 +185,23 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _BackgroundColorChanged(bg); // Apply padding as swapChainPanel's margin - auto thickness = _ParseThicknessFromPadding(_settings.Padding()); - _swapChainPanel.Margin(thickness); + auto newMargin = _ParseThicknessFromPadding(_settings.Padding()); + auto existingMargin = _swapChainPanel.Margin(); + _swapChainPanel.Margin(newMargin); + + if (newMargin != existingMargin && newMargin != Thickness{ 0 }) + { + TraceLoggingWrite(g_hTerminalControlProvider, + "NonzeroPaddingApplied", + TraceLoggingDescription("An event emitted when a control has padding applied to it"), + TraceLoggingStruct(4, "Padding"), + TraceLoggingFloat64(newMargin.Left, "Left"), + TraceLoggingFloat64(newMargin.Top, "Top"), + TraceLoggingFloat64(newMargin.Right, "Right"), + TraceLoggingFloat64(newMargin.Bottom, "Bottom"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + } // Initialize our font information. const auto* fontFace = _settings.FontFace().c_str(); diff --git a/src/cascadia/TerminalControl/TerminalControl.vcxproj b/src/cascadia/TerminalControl/TerminalControl.vcxproj index 1fd40b42984..8dca317e41e 100644 --- a/src/cascadia/TerminalControl/TerminalControl.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControl.vcxproj @@ -25,6 +25,7 @@ Create + TermControl.idl diff --git a/src/cascadia/TerminalControl/init.cpp b/src/cascadia/TerminalControl/init.cpp new file mode 100644 index 00000000000..92b5b3c83b1 --- /dev/null +++ b/src/cascadia/TerminalControl/init.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" + +// Note: Generate GUID using TlgGuid.exe tool +TRACELOGGING_DEFINE_PROVIDER( + g_hTerminalControlProvider, + "Microsoft.Windows.Terminal.Control", + // {28c82e50-57af-5a86-c25b-e39cd990032b} + (0x28c82e50, 0x57af, 0x5a86, 0xc2, 0x5b, 0xe3, 0x9c, 0xd9, 0x90, 0x03, 0x2b), + TraceLoggingOptionMicrosoftTelemetry()); + +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + TraceLoggingRegister(g_hTerminalControlProvider); + break; + case DLL_PROCESS_DETACH: + if (g_hTerminalControlProvider) + { + TraceLoggingUnregister(g_hTerminalControlProvider); + } + break; + } + + return TRUE; +} diff --git a/src/cascadia/TerminalControl/pch.h b/src/cascadia/TerminalControl/pch.h index c807bc1f2a4..a70b22fdbb1 100644 --- a/src/cascadia/TerminalControl/pch.h +++ b/src/cascadia/TerminalControl/pch.h @@ -30,3 +30,7 @@ #include #include + +#include +TRACELOGGING_DECLARE_PROVIDER(g_hTerminalControlProvider); +#include From 56589c0aac93e0e996989f6dc04a262739c55051 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 30 Jul 2019 14:32:23 -0700 Subject: [PATCH 003/154] Fixes crash when specifying invalid font (#2153) * Stop the crash with fonts by trying a few fallback/backup fonts if we can't find what was selected. * Create fallback pattern for finding a font. Resolve and pass the locale name. Retrieve the font name while retrieving the font object. Use retrieved data in the _GetProposedFont methods instead of re-resolving it. * Add details to schema about fallback. Finish comment explaining fallback pattern to doc comment on method. --- doc/cascadia/SettingsSchema.md | 2 +- src/renderer/dx/DxRenderer.cpp | 185 ++++++++++++++++++++++++++++----- src/renderer/dx/DxRenderer.hpp | 20 +++- 3 files changed, 177 insertions(+), 30 deletions(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index 4ee2aa9cd15..d07d68562c6 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -25,7 +25,7 @@ Properties listed below are specific to each unique profile. | `commandline` | _Required_ | String | `powershell.exe` | Executable used in the profile. | | `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. | | `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) | -| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. | +| `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. | | `fontSize` | _Required_ | Integer | `10` | Sets the font size. | | `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. | | `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. | diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index f59f02c9603..9d372090724 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -15,6 +15,8 @@ #pragma hdrstop static constexpr float POINTS_PER_INCH = 72.0f; +static std::wstring FALLBACK_FONT_FACE = L"Consolas"; +static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Types; @@ -133,14 +135,14 @@ DxEngine::~DxEngine() const DWORD DeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | // clang-format off - // This causes problems for folks who do not have the whole DirectX SDK installed - // when they try to run the rest of the project in debug mode. - // As such, I'm leaving this flag here for people doing DX-specific work to toggle it - // only when they need it and shutting it off otherwise. - // Find out more about the debug layer here: - // https://docs.microsoft.com/en-us/windows/desktop/direct3d11/overviews-direct3d-11-devices-layers - // You can find out how to install it here: - // https://docs.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features +// This causes problems for folks who do not have the whole DirectX SDK installed +// when they try to run the rest of the project in debug mode. +// As such, I'm leaving this flag here for people doing DX-specific work to toggle it +// only when they need it and shutting it off otherwise. +// Find out more about the debug layer here: +// https://docs.microsoft.com/en-us/windows/desktop/direct3d11/overviews-direct3d-11-devices-layers +// You can find out how to install it here: +// https://docs.microsoft.com/en-us/windows/uwp/gaming/use-the-directx-runtime-and-visual-studio-graphics-diagnostic-features // clang-format on // D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED; @@ -1354,6 +1356,46 @@ float DxEngine::GetScaling() const noexcept return PostMessageW(_hwndTarget, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL; } +// Routine Description: +// - Attempts to locate the font given, but then begins falling back if we cannot find it. +// - We'll try to fall back to Consolas with the given weight/stretch/style first, +// then try Consolas again with normal weight/stretch/style, +// and if nothing works, then we'll throw an error. +// Arguments: +// - familyName - The font name we should be looking for +// - weight - The weight (bold, light, etc.) +// - stretch - The stretch of the font is the spacing between each letter +// - style - Normal, italic, etc. +// Return Value: +// - Smart pointer holding interface reference for queryable font data. +[[nodiscard]] Microsoft::WRL::ComPtr DxEngine::_ResolveFontFaceWithFallback(std::wstring& familyName, + DWRITE_FONT_WEIGHT& weight, + DWRITE_FONT_STRETCH& stretch, + DWRITE_FONT_STYLE& style, + std::wstring& localeName) const +{ + auto face = _FindFontFace(familyName, weight, stretch, style, localeName); + + if (!face) + { + familyName = FALLBACK_FONT_FACE; + face = _FindFontFace(familyName, weight, stretch, style, localeName); + } + + if (!face) + { + familyName = FALLBACK_FONT_FACE; + weight = DWRITE_FONT_WEIGHT_NORMAL; + stretch = DWRITE_FONT_STRETCH_NORMAL; + style = DWRITE_FONT_STYLE_NORMAL; + face = _FindFontFace(familyName, weight, stretch, style, localeName); + } + + THROW_IF_NULL_ALLOC(face); + + return face; +} + // Routine Description: // - Locates a suitable font face from the given information // Arguments: @@ -1363,10 +1405,11 @@ float DxEngine::GetScaling() const noexcept // - style - Normal, italic, etc. // Return Value: // - Smart pointer holding interface reference for queryable font data. -[[nodiscard]] Microsoft::WRL::ComPtr DxEngine::_FindFontFace(const std::wstring& familyName, - DWRITE_FONT_WEIGHT weight, - DWRITE_FONT_STRETCH stretch, - DWRITE_FONT_STYLE style) const +[[nodiscard]] Microsoft::WRL::ComPtr DxEngine::_FindFontFace(std::wstring& familyName, + DWRITE_FONT_WEIGHT& weight, + DWRITE_FONT_STRETCH& stretch, + DWRITE_FONT_STYLE& style, + std::wstring& localeName) const { Microsoft::WRL::ComPtr fontFace; @@ -1375,7 +1418,7 @@ float DxEngine::GetScaling() const noexcept UINT32 familyIndex; BOOL familyExists; - THROW_IF_FAILED(fontCollection->FindFamilyName(familyName.c_str(), &familyIndex, &familyExists)); + THROW_IF_FAILED(fontCollection->FindFamilyName(familyName.data(), &familyIndex, &familyExists)); if (familyExists) { @@ -1389,11 +1432,107 @@ float DxEngine::GetScaling() const noexcept THROW_IF_FAILED(font->CreateFontFace(&fontFace0)); THROW_IF_FAILED(fontFace0.As(&fontFace)); + + // Dig the family name out at the end to return it. + familyName = _GetFontFamilyName(fontFamily.Get(), localeName); } return fontFace; } +// Routine Description: +// - Helper to retrieve the user's locale preference or fallback to the default. +// Arguments: +// - +// Return Value: +// - A locale that can be used on construction of assorted DX objects that want to know one. +[[nodiscard]] std::wstring DxEngine::_GetLocaleName() const +{ + std::array localeName; + + const auto returnCode = GetUserDefaultLocaleName(localeName.data(), gsl::narrow(localeName.size())); + if (returnCode) + { + return { localeName.data() }; + } + else + { + return { FALLBACK_LOCALE.data(), FALLBACK_LOCALE.size() }; + } +} + +// Routine Description: +// - Retrieves the font family name out of the given object in the given locale. +// - If we can't find a valid name for the given locale, we'll fallback and report it back. +// Arguments: +// - fontFamily - DirectWrite font family object +// - localeName - The locale in which the name should be retrieved. +// - If fallback occurred, this is updated to what we retrieved instead. +// Return Value: +// - Localized string name of the font family +[[nodiscard]] std::wstring DxEngine::_GetFontFamilyName(IDWriteFontFamily* const fontFamily, + std::wstring& localeName) const +{ + // See: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontcollection + Microsoft::WRL::ComPtr familyNames; + THROW_IF_FAILED(fontFamily->GetFamilyNames(&familyNames)); + + // First we have to find the right family name for the locale. We're going to bias toward what the caller + // requested, but fallback if we need to and reply with the locale we ended up choosing. + UINT32 index = 0; + BOOL exists = false; + + // This returns S_OK whether or not it finds a locale name. Check exists field instead. + // If it returns an error, it's a real problem, not an absence of this locale name. + // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-findlocalename + THROW_IF_FAILED(familyNames->FindLocaleName(localeName.data(), &index, &exists)); + + // If we tried and it still doesn't exist, try with the fallback locale. + if (!exists) + { + localeName = FALLBACK_LOCALE; + THROW_IF_FAILED(familyNames->FindLocaleName(localeName.data(), &index, &exists)); + } + + // If it still doesn't exist, we're going to try index 0. + if (!exists) + { + index = 0; + + // Get the locale name out so at least the caller knows what locale this name goes with. + UINT32 length = 0; + THROW_IF_FAILED(familyNames->GetLocaleNameLength(index, &length)); + + // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getlocalenamelength + // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getlocalename + // GetLocaleNameLength does not include space for null terminator, but GetLocaleName needs it so add one. + length++; + + localeName.resize(length); + + THROW_IF_FAILED(familyNames->GetLocaleName(index, localeName.data(), length)); + } + + // OK, now that we've decided which family name and the locale that it's in... let's go get it. + UINT32 length = 0; + THROW_IF_FAILED(familyNames->GetStringLength(index, &length)); + + // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getstringlength + // https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-idwritelocalizedstrings-getstring + // Once again, GetStringLength is without the null, but GetString needs the null. So add one. + length++; + + // Make our output buffer and resize it so it is allocated. + std::wstring retVal; + retVal.resize(length); + + // FINALLY, go fetch the string name. + THROW_IF_FAILED(familyNames->GetString(index, retVal.data(), length)); + + // and return it. + return retVal; +} + // Routine Description: // - Updates the font used for drawing // Arguments: @@ -1411,13 +1550,13 @@ float DxEngine::GetScaling() const noexcept { try { - const std::wstring fontName(desired.GetFaceName()); - const DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; - const DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; - const DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_NORMAL; + std::wstring fontName(desired.GetFaceName()); + DWRITE_FONT_WEIGHT weight = DWRITE_FONT_WEIGHT_NORMAL; + DWRITE_FONT_STYLE style = DWRITE_FONT_STYLE_NORMAL; + DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_NORMAL; + std::wstring localeName = _GetLocaleName(); - const auto face = _FindFontFace(fontName, weight, stretch, style); - THROW_IF_NULL_ALLOC_MSG(face, "Failed to find the requested font"); + const auto face = _ResolveFontFaceWithFallback(fontName, weight, stretch, style, localeName); DWRITE_FONT_METRICS1 fontMetrics; face->GetMetrics(&fontMetrics); @@ -1508,7 +1647,7 @@ float DxEngine::GetScaling() const noexcept style, stretch, fontSize, - L"", + localeName.data(), &format)); THROW_IF_FAILED(format.As(&textFormat)); @@ -1529,10 +1668,6 @@ float DxEngine::GetScaling() const noexcept coordSize.X = gsl::narrow(widthExact); coordSize.Y = gsl::narrow(lineSpacing.height); - const auto familyNameLength = textFormat->GetFontFamilyNameLength() + 1; // 1 for space for null - const auto familyNameBuffer = std::make_unique(familyNameLength); - THROW_IF_FAILED(textFormat->GetFontFamilyName(familyNameBuffer.get(), familyNameLength)); - const DWORD weightDword = static_cast(textFormat->GetFontWeight()); // Unscaled is for the purposes of re-communicating this font back to the renderer again later. @@ -1542,7 +1677,7 @@ float DxEngine::GetScaling() const noexcept COORD scaled = coordSize; - actual.SetFromEngine(familyNameBuffer.get(), + actual.SetFromEngine(fontName.data(), desired.GetFamily(), weightDword, false, diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 8a9335401ea..c6127b8ed7d 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -178,10 +178,22 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT _EnableDisplayAccess(const bool outputEnabled) noexcept; - [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(const std::wstring& familyName, - DWRITE_FONT_WEIGHT weight, - DWRITE_FONT_STRETCH stretch, - DWRITE_FONT_STYLE style) const; + [[nodiscard]] ::Microsoft::WRL::ComPtr _ResolveFontFaceWithFallback(std::wstring& familyName, + DWRITE_FONT_WEIGHT& weight, + DWRITE_FONT_STRETCH& stretch, + DWRITE_FONT_STYLE& style, + std::wstring& localeName) const; + + [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(std::wstring& familyName, + DWRITE_FONT_WEIGHT& weight, + DWRITE_FONT_STRETCH& stretch, + DWRITE_FONT_STYLE& style, + std::wstring& localeName) const; + + [[nodiscard]] std::wstring _GetLocaleName() const; + + [[nodiscard]] std::wstring _GetFontFamilyName(IDWriteFontFamily* const fontFamily, + std::wstring& localeName) const; [[nodiscard]] HRESULT _GetProposedFont(const FontInfoDesired& desired, FontInfo& actual, From c6c51fbb0e0df27cde5a8c37390752e32160762b Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Tue, 30 Jul 2019 14:36:15 -0700 Subject: [PATCH 004/154] Change our manifest from depending on Windows.Universal to Windows.Desktop (#2155) --- src/cascadia/CascadiaPackage/Package-Dev.appxmanifest | 2 +- src/cascadia/CascadiaPackage/Package.appxmanifest | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest index 59a2c75e2ca..a8ccfe6c73c 100644 --- a/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package-Dev.appxmanifest @@ -21,7 +21,7 @@ - + diff --git a/src/cascadia/CascadiaPackage/Package.appxmanifest b/src/cascadia/CascadiaPackage/Package.appxmanifest index 347b1962eac..63a5f1b381a 100644 --- a/src/cascadia/CascadiaPackage/Package.appxmanifest +++ b/src/cascadia/CascadiaPackage/Package.appxmanifest @@ -21,7 +21,7 @@ - + From 7abcc35fdf7d98bc98016cda8f4b90398c65692e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 30 Jul 2019 17:01:27 -0500 Subject: [PATCH 005/154] Fix a crash on restore down (#2149) * Don't trigger a frame due to circling when in the middle of a resize operation This fixes #1795, and shined quite a bit of light on the whole conpty resize process. * Move the Begin/End to ResizeScreenBuffer, to catch more cases. --- src/host/VtIo.cpp | 34 +++++++++++++++++++++++ src/host/VtIo.hpp | 3 ++ src/host/screenInfo.cpp | 13 +++++++++ src/renderer/vt/invalidate.cpp | 16 ++++++++--- src/renderer/vt/state.cpp | 29 +++++++++++++++++++ src/renderer/vt/vtrenderer.hpp | 3 ++ src/terminal/adapter/InteractDispatch.cpp | 2 ++ 7 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/host/VtIo.cpp b/src/host/VtIo.cpp index 3ea8d7fb386..cf7ab1ecbe5 100644 --- a/src/host/VtIo.cpp +++ b/src/host/VtIo.cpp @@ -397,3 +397,37 @@ void VtIo::_ShutdownIfNeeded() ServiceLocator::RundownAndExit(ERROR_BROKEN_PIPE); } } + +// Method Description: +// - Tell the vt renderer to begin a resize operation. During a resize +// operation, the vt renderer should _not_ request to be repainted during a +// text buffer circling event. Any callers of this method should make sure to +// call EndResize to make sure the renderer returns to normal behavior. +// See GH#1795 for context on this method. +// Arguments: +// - +// Return Value: +// - +void VtIo::BeginResize() +{ + if (_pVtRenderEngine) + { + _pVtRenderEngine->BeginResizeRequest(); + } +} + +// Method Description: +// - Tell the vt renderer to end a resize operation. +// See BeginResize for more details. +// See GH#1795 for context on this method. +// Arguments: +// - +// Return Value: +// - +void VtIo::EndResize() +{ + if (_pVtRenderEngine) + { + _pVtRenderEngine->EndResizeRequest(); + } +} diff --git a/src/host/VtIo.hpp b/src/host/VtIo.hpp index dc037b9372a..0516d9ab0ea 100644 --- a/src/host/VtIo.hpp +++ b/src/host/VtIo.hpp @@ -36,6 +36,9 @@ namespace Microsoft::Console::VirtualTerminal void CloseInput() override; void CloseOutput() override; + void BeginResize(); + void EndResize(); + private: // After CreateIoHandlers is called, these will be invalid. wil::unique_hfile _hInput; diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index c8bee162166..07233506777 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -1670,6 +1670,19 @@ bool SCREEN_INFORMATION::IsMaximizedY() const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); NTSTATUS status = STATUS_SUCCESS; + // If we're in conpty mode, suppress any immediate painting we might do + // during the resize. + if (gci.IsInVtIoMode()) + { + gci.GetVtIo()->BeginResize(); + } + auto endResize = wil::scope_exit([&] { + if (gci.IsInVtIoMode()) + { + gci.GetVtIo()->EndResize(); + } + }); + // cancel any active selection before resizing or it will not necessarily line up with the new buffer positions Selection::Instance().ClearSelection(); diff --git a/src/renderer/vt/invalidate.cpp b/src/renderer/vt/invalidate.cpp index 1272615baff..6d9caea7a63 100644 --- a/src/renderer/vt/invalidate.cpp +++ b/src/renderer/vt/invalidate.cpp @@ -102,11 +102,19 @@ using namespace Microsoft::Console::Render; // - S_OK [[nodiscard]] HRESULT VtEngine::InvalidateCircling(_Out_ bool* const pForcePaint) noexcept { - *pForcePaint = true; + // If we're in the middle of a resize request, don't try to immediately start a frame. + if (_inResizeRequest) + { + *pForcePaint = false; + } + else + { + *pForcePaint = true; - // Keep track of the fact that we circled, we'll need to do some work on - // end paint to specifically handle this. - _circled = true; + // Keep track of the fact that we circled, we'll need to do some work on + // end paint to specifically handle this. + _circled = true; + } return S_OK; } diff --git a/src/renderer/vt/state.cpp b/src/renderer/vt/state.cpp index d2b26bc2f5d..31cca05d4a2 100644 --- a/src/renderer/vt/state.cpp +++ b/src/renderer/vt/state.cpp @@ -54,6 +54,7 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe, _terminalOwner{ nullptr }, _newBottomLine{ false }, _deferredCursorPos{ INVALID_COORDS }, + _inResizeRequest{ false }, _trace{} { #ifndef UNIT_TESTING @@ -417,3 +418,31 @@ HRESULT VtEngine::RequestCursor() noexcept RETURN_IF_FAILED(_Flush()); return S_OK; } + +// Method Description: +// - Tell the vt renderer to begin a resize operation. During a resize +// operation, the vt renderer should _not_ request to be repainted during a +// text buffer circling event. Any callers of this method should make sure to +// call EndResize to make sure the renderer returns to normal behavior. +// See GH#1795 for context on this method. +// Arguments: +// - +// Return Value: +// - +void VtEngine::BeginResizeRequest() +{ + _inResizeRequest = true; +} + +// Method Description: +// - Tell the vt renderer to end a resize operation. +// See BeginResize for more details. +// See GH#1795 for context on this method. +// Arguments: +// - +// Return Value: +// - +void VtEngine::EndResizeRequest() +{ + _inResizeRequest = false; +} diff --git a/src/renderer/vt/vtrenderer.hpp b/src/renderer/vt/vtrenderer.hpp index 77a9c6349c2..7ca3aa37af3 100644 --- a/src/renderer/vt/vtrenderer.hpp +++ b/src/renderer/vt/vtrenderer.hpp @@ -94,6 +94,8 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring& str) noexcept = 0; void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner); + void BeginResizeRequest(); + void EndResizeRequest(); protected: wil::unique_hfile _hFile; @@ -132,6 +134,7 @@ namespace Microsoft::Console::Render Microsoft::Console::ITerminalOwner* _terminalOwner; Microsoft::Console::VirtualTerminal::RenderTracing _trace; + bool _inResizeRequest{ false }; [[nodiscard]] HRESULT _Write(std::string_view const str) noexcept; [[nodiscard]] HRESULT _WriteFormattedString(const std::string* const pFormat, ...) noexcept; diff --git a/src/terminal/adapter/InteractDispatch.cpp b/src/terminal/adapter/InteractDispatch.cpp index afbddc0fdf1..74c918bffe1 100644 --- a/src/terminal/adapter/InteractDispatch.cpp +++ b/src/terminal/adapter/InteractDispatch.cpp @@ -114,6 +114,8 @@ bool InteractDispatch::WindowManipulation(const DispatchTypes::WindowManipulatio } break; case DispatchTypes::WindowManipulationType::ResizeWindowInCharacters: + // TODO:GH#1765 We should introduce a better `ResizeConpty` function to + // the ConGetSet interface, that specifically handles a conpty resize. if (cParams == 2) { fSuccess = DispatchCommon::s_ResizeWindow(*_pConApi, rgusParams[1], rgusParams[0]); From 2d3e271a4f831f61e45087960fe419e2805a591f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 30 Jul 2019 17:04:48 -0500 Subject: [PATCH 006/154] Fix the terminal snapping across DPI boundaries strangely When we snap across a DPI boundary, we'll get the DPI changed message _after_ the resize message. So when we try to calculate the new terminal position, we'll use the _old_ DPI to calculate the size. When snapping to a lower DPI, this means the terminal will be smaller, with "padding" all around the actual app. Instead, when we get a new DPI, force us to update out UI layout for the new DPI. Closes #2057 --- src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index ec1f45a6e99..9d1cfa7d2d8 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -623,6 +623,12 @@ RECT NonClientIslandWindow::GetMaxWindowRectInPixels(const RECT* const prcSugges break; } } + case WM_DPICHANGED: + { + auto lprcNewScale = reinterpret_cast(lParam); + OnSize(RECT_WIDTH(lprcNewScale), RECT_HEIGHT(lprcNewScale)); + break; + } } return IslandWindow::MessageHandler(message, wParam, lParam); From 63df881f315fe70be70559252253c9fec80bcc55 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Tue, 30 Jul 2019 16:28:28 -0700 Subject: [PATCH 007/154] VT sequence support for EraseInLine, EraseInDisplay, DeleteCharacter and InsertCharacter (#2144) * We now support EraseInLine, EraseInDisplay, DeleteCharacter and InsertCharacter --- src/buffer/out/textBuffer.cpp | 25 +- src/buffer/out/textBuffer.hpp | 1 + src/cascadia/TerminalCore/ITerminalApi.hpp | 4 + src/cascadia/TerminalCore/Terminal.hpp | 4 + src/cascadia/TerminalCore/TerminalApi.cpp | 228 +++++++++++++++++- .../TerminalCore/TerminalDispatch.cpp | 58 ++++- .../TerminalCore/TerminalDispatch.hpp | 7 +- 7 files changed, 316 insertions(+), 11 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 094ede52006..b3c1fa7ecf3 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -558,22 +558,37 @@ bool TextBuffer::IncrementCircularBuffer() //Routine Description: // - Retrieves the position of the last non-space character on the final line of the text buffer. +// - By default, we search the entire buffer to find the last non-space character //Arguments: // - //Return Value: // - Coordinate position in screen coordinates (offset coordinates, not array index coordinates). COORD TextBuffer::GetLastNonSpaceCharacter() const { - COORD coordEndOfText; - // Always search the whole buffer, by starting at the bottom. - coordEndOfText.Y = GetSize().BottomInclusive(); + return GetLastNonSpaceCharacter(GetSize()); +} + +//Routine Description: +// - Retrieves the position of the last non-space character in the given viewport +// - This is basically an optimized version of GetLastNonSpaceCharacter(), and can be called when +// - we know the last character is within the given viewport (so we don't need to check the entire buffer) +//Arguments: +// - The viewport +//Return value: +// - Coordinate position (relative to the text buffer) +COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const +{ + COORD coordEndOfText = { 0 }; + // Search the given viewport by starting at the bottom. + coordEndOfText.Y = viewport.BottomInclusive(); const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y); // The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character) coordEndOfText.X = static_cast(pCurrRow->GetCharRow().MeasureRight()) - 1; // If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text. - bool fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > 0); // this row is empty, and we're not at the top + const auto viewportTop = viewport.Top(); + bool fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop); // this row is empty, and we're not at the top while (fDoBackUp) { coordEndOfText.Y--; @@ -581,7 +596,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter() const // We need to back up to the previous row if this line is empty, AND there are more rows coordEndOfText.X = static_cast(pCurrRow->GetCharRow().MeasureRight()) - 1; - fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > 0); + fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop); } // don't allow negative results diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 8e5aa205158..9cfd50bba0a 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -105,6 +105,7 @@ class TextBuffer final bool IncrementCircularBuffer(); COORD GetLastNonSpaceCharacter() const; + COORD GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const; Cursor& GetCursor(); const Cursor& GetCursor() const; diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index 9e4e2a3767f..e85fb29c950 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -24,7 +24,11 @@ namespace Microsoft::Terminal::Core virtual bool SetCursorPosition(short x, short y) = 0; virtual COORD GetCursorPosition() = 0; + virtual bool DeleteCharacter(const unsigned int uiCount) = 0; + virtual bool InsertCharacter(const unsigned int uiCount) = 0; virtual bool EraseCharacters(const unsigned int numChars) = 0; + virtual bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0; + virtual bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) = 0; virtual bool SetWindowTitle(std::wstring_view title) = 0; diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index b41a0a9f46c..157f0155ca9 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -66,7 +66,11 @@ class Microsoft::Terminal::Core::Terminal final : bool ReverseText(bool reversed) override; bool SetCursorPosition(short x, short y) override; COORD GetCursorPosition() override; + bool DeleteCharacter(const unsigned int uiCount) override; + bool InsertCharacter(const unsigned int uiCount) override; bool EraseCharacters(const unsigned int numChars) override; + bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; + bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; bool SetWindowTitle(std::wstring_view title) override; bool SetColorTableEntry(const size_t tableIndex, const COLORREF dwColor) override; bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) override; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 27ca646b320..a0504164088 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Terminal.hpp" +#include "../src/inc/unicode.hpp" using namespace Microsoft::Terminal::Core; using namespace Microsoft::Console::Types; @@ -126,17 +127,242 @@ COORD Terminal::GetCursorPosition() return newPos; } +// Method Description: +// - deletes uiCount characters starting from the cursor's current position +// - it moves over the remaining text to 'replace' the deleted text +// - for example, if the buffer looks like this ('|' is the cursor): [abc|def] +// - calling DeleteCharacter(1) will change it to: [abc|ef], +// - i.e. the 'd' gets deleted and the 'ef' gets shifted over 1 space and **retain their previous text attributes** +// Arguments: +// - uiCount, the number of characters to delete +// Return value: +// - true if succeeded, false otherwise +bool Terminal::DeleteCharacter(const unsigned int uiCount) +{ + SHORT dist; + if (!SUCCEEDED(UIntToShort(uiCount, &dist))) + { + return false; + } + const auto cursorPos = _buffer->GetCursor().GetPosition(); + const auto copyToPos = cursorPos; + const COORD copyFromPos{ cursorPos.X + dist, cursorPos.Y }; + auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X; + SHORT width; + if (!SUCCEEDED(UIntToShort(sourceWidth, &width))) + { + return false; + } + + // Get a rectangle of the source + auto source = Viewport::FromDimensions(copyFromPos, width, 1); + + // Get a rectangle of the target + const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions()); + const auto walkDirection = Viewport::DetermineWalkDirection(source, target); + + auto sourcePos = source.GetWalkOrigin(walkDirection); + auto targetPos = target.GetWalkOrigin(walkDirection); + + // Iterate over the source cell data and copy it over to the target + do + { + const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos))); + _buffer->Write(OutputCellIterator({ &data, 1 }), targetPos); + } while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection)); + + return true; +} + +// Method Description: +// - Inserts uiCount spaces starting from the cursor's current position, moving over the existing text +// - for example, if the buffer looks like this ('|' is the cursor): [abc|def] +// - calling InsertCharacter(1) will change it to: [abc| def], +// - i.e. the 'def' gets shifted over 1 space and **retain their previous text attributes** +// Arguments: +// - uiCount, the number of spaces to insert +// Return value: +// - true if succeeded, false otherwise +bool Terminal::InsertCharacter(const unsigned int uiCount) +{ + // NOTE: the code below is _extremely_ similar to DeleteCharacter + // We will want to use this same logic and implement a helper function instead + // that does the 'move a region from here to there' operation + // TODO: Github issue #2163 + SHORT dist; + if (!SUCCEEDED(UIntToShort(uiCount, &dist))) + { + return false; + } + const auto cursorPos = _buffer->GetCursor().GetPosition(); + const auto copyFromPos = cursorPos; + const COORD copyToPos{ cursorPos.X + dist, cursorPos.Y }; + auto sourceWidth = _mutableViewport.RightExclusive() - copyFromPos.X; + SHORT width; + if (!SUCCEEDED(UIntToShort(sourceWidth, &width))) + { + return false; + } + + // Get a rectangle of the source + auto source = Viewport::FromDimensions(copyFromPos, width, 1); + const auto sourceOrigin = source.Origin(); + + // Get a rectangle of the target + const auto target = Viewport::FromDimensions(copyToPos, source.Dimensions()); + const auto walkDirection = Viewport::DetermineWalkDirection(source, target); + + auto sourcePos = source.GetWalkOrigin(walkDirection); + auto targetPos = target.GetWalkOrigin(walkDirection); + + // Iterate over the source cell data and copy it over to the target + do + { + const auto data = OutputCell(*(_buffer->GetCellDataAt(sourcePos))); + _buffer->Write(OutputCellIterator({ &data, 1 }), targetPos); + } while (source.WalkInBounds(sourcePos, walkDirection) && target.WalkInBounds(targetPos, walkDirection)); + auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), dist); + _buffer->Write(eraseIter, cursorPos); + + return true; +} + bool Terminal::EraseCharacters(const unsigned int numChars) { const auto absoluteCursorPos = _buffer->GetCursor().GetPosition(); const auto viewport = _GetMutableViewport(); const short distanceToRight = viewport.RightExclusive() - absoluteCursorPos.X; const short fillLimit = std::min(static_cast(numChars), distanceToRight); - auto eraseIter = OutputCellIterator(L' ', _buffer->GetCurrentAttributes(), fillLimit); + auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), fillLimit); _buffer->Write(eraseIter, absoluteCursorPos); return true; } +// Method description: +// - erases a line of text, either from +// 1. beginning to the cursor's position +// 2. cursor's position to end +// 3. beginning to end +// - depending on the erase type +// Arguments: +// - the erase type +// Return value: +// - true if succeeded, false otherwise +bool Terminal::EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) +{ + const auto cursorPos = _buffer->GetCursor().GetPosition(); + const auto viewport = _GetMutableViewport(); + COORD startPos = { 0 }; + startPos.Y = cursorPos.Y; + // nlength determines the number of spaces we need to write + DWORD nlength = 0; + + // Determine startPos.X and nlength by the eraseType + switch (eraseType) + { + case DispatchTypes::EraseType::FromBeginning: + nlength = cursorPos.X - viewport.Left() + 1; + break; + case DispatchTypes::EraseType::ToEnd: + startPos.X = cursorPos.X; + nlength = viewport.RightInclusive() - startPos.X; + break; + case DispatchTypes::EraseType::All: + startPos.X = viewport.Left(); + nlength = viewport.RightInclusive() - startPos.X; + break; + case DispatchTypes::EraseType::Scrollback: + return false; + } + + auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), nlength); + _buffer->Write(eraseIter, startPos); + return true; +} + +// Method description: +// - erases text in the buffer in two ways depending on erase type +// 1. 'erases' all text visible to the user (i.e. the text in the viewport) +// 2. erases all the text in the scrollback +// Arguments: +// - the erase type +// Return Value: +// - true if succeeded, false otherwise +bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType) +{ + // Store the relative cursor position so we can restore it later after we move the viewport + const auto cursorPos = _buffer->GetCursor().GetPosition(); + auto relativeCursor = cursorPos; + _mutableViewport.ConvertToOrigin(&relativeCursor); + + // Initialize the new location of the viewport + // the top and bottom parameters are determined by the eraseType + SMALL_RECT newWin; + newWin.Left = _mutableViewport.Left(); + newWin.Right = _mutableViewport.RightExclusive(); + + if (eraseType == DispatchTypes::EraseType::All) + { + // In this case, we simply move the viewport down, effectively pushing whatever text was on the screen into the scrollback + // and thus 'erasing' the text visible to the user + const auto coordLastChar = _buffer->GetLastNonSpaceCharacter(_mutableViewport); + if (coordLastChar.X == 0 && coordLastChar.Y == 0) + { + // Nothing to clear, just return + return true; + } + + short sNewTop = coordLastChar.Y + 1; + + // Increment the circular buffer only if the new location of the viewport would be 'below' the buffer + const short delta = (sNewTop + _mutableViewport.Height()) - (_buffer->GetSize().Height()); + for (auto i = 0; i < delta; i++) + { + _buffer->IncrementCircularBuffer(); + sNewTop--; + } + + newWin.Top = sNewTop; + newWin.Bottom = sNewTop + _mutableViewport.Height(); + } + else if (eraseType == DispatchTypes::EraseType::Scrollback) + { + // We only want to erase the scrollback, and leave everything else on the screen as it is + // so we grab the text in the viewport and rotate it up to the top of the buffer + COORD scrollFromPos{ 0, 0 }; + _mutableViewport.ConvertFromOrigin(&scrollFromPos); + _buffer->ScrollRows(scrollFromPos.Y, _mutableViewport.Height(), -scrollFromPos.Y); + + // Since we only did a rotation, the text that was in the scrollback is now _below_ where we are going to move the viewport + // and we have to make sure we erase that text + auto eraseStart = _mutableViewport.Height(); + auto eraseEnd = _buffer->GetLastNonSpaceCharacter(_mutableViewport).Y; + auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), _mutableViewport.RightInclusive() * (eraseEnd - eraseStart + 1)); + for (SHORT i = eraseStart; i <= eraseEnd; i++) + { + COORD erasePos{ 0, i }; + _buffer->Write(eraseIter, erasePos); + } + + // Reset the scroll offset now because there's nothing for the user to 'scroll' to + _scrollOffset = 0; + + newWin.Top = 0; + newWin.Bottom = _mutableViewport.Height(); + } + else + { + return false; + } + + // Move the viewport, adjust the scoll bar if needed, and restore the old cursor position + _mutableViewport = Viewport::FromExclusive(newWin); + Terminal::_NotifyScrollEvent(); + SetCursorPosition(relativeCursor.X, relativeCursor.Y); + + return true; +} + bool Terminal::SetWindowTitle(std::wstring_view title) { _title = title; diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index bd076961584..d5b061d2913 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -47,6 +47,20 @@ bool TerminalDispatch::CursorForward(const unsigned int uiDistance) return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); } +bool TerminalDispatch::CursorBackward(const unsigned int uiDistance) +{ + const auto cursorPos = _terminalApi.GetCursorPosition(); + const COORD newCursorPos{ cursorPos.X - gsl::narrow(uiDistance), cursorPos.Y }; + return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); +} + +bool TerminalDispatch::CursorUp(const unsigned int uiDistance) +{ + const auto cursorPos = _terminalApi.GetCursorPosition(); + const COORD newCursorPos{ cursorPos.X, cursorPos.Y + gsl::narrow(uiDistance) }; + return _terminalApi.SetCursorPosition(newCursorPos.X, newCursorPos.Y); +} + bool TerminalDispatch::EraseCharacters(const unsigned int uiNumChars) { return _terminalApi.EraseCharacters(uiNumChars); @@ -98,9 +112,45 @@ bool TerminalDispatch::SetDefaultBackground(const DWORD dwColor) } // Method Description: -// - For now, this is a hacky backspace -// - TODO: GitHub #1883 -bool TerminalDispatch::EraseInLine(const DispatchTypes::EraseType) +// - Erases characters in the buffer depending on the erase type +// Arguments: +// - eraseType: the erase type (from beginning, to end, or all) +// Return Value: +// True if handled successfully. False otherwise. +bool TerminalDispatch::EraseInLine(const DispatchTypes::EraseType eraseType) +{ + return _terminalApi.EraseInLine(eraseType); +} + +// Method Description: +// - Deletes uiCount number of characters starting from where the cursor is currently +// Arguments: +// - uiCount, the number of characters to delete +// Return Value: +// True if handled successfully. False otherwise. +bool TerminalDispatch::DeleteCharacter(const unsigned int uiCount) +{ + return _terminalApi.DeleteCharacter(uiCount); +} + +// Method Description: +// - Adds uiCount number of spaces starting from where the cursor is currently +// Arguments: +// - uiCount, the number of spaces to add +// Return Value: +// True if handled successfully, false otherwise +bool TerminalDispatch::InsertCharacter(const unsigned int uiCount) +{ + return _terminalApi.InsertCharacter(uiCount); +} + +// Method Description: +// - Moves the viewport and erases text from the buffer depending on the eraseType +// Arguments: +// - eraseType: the desired erase type +// Return Value: +// True if handled successfully. False otherwise +bool TerminalDispatch::EraseInDisplay(const DispatchTypes::EraseType eraseType) { - return _terminalApi.EraseCharacters(1); + return _terminalApi.EraseInDisplay(eraseType); } diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index e026744f0c7..5fa9758856e 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -20,6 +20,8 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc const unsigned int uiColumn) override; // CUP bool CursorForward(const unsigned int uiDistance) override; + bool CursorBackward(const unsigned int uiDistance) override; + bool CursorUp(const unsigned int uiDistance) override; bool EraseCharacters(const unsigned int uiNumChars) override; bool SetWindowTitle(std::wstring_view title) override; @@ -29,7 +31,10 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool SetDefaultForeground(const DWORD dwColor) override; bool SetDefaultBackground(const DWORD dwColor) override; - bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType /* eraseType*/) override; // ED + bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; // ED + bool DeleteCharacter(const unsigned int uiCount) override; + bool InsertCharacter(const unsigned int uiCount) override; + bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) override; private: ::Microsoft::Terminal::Core::ITerminalApi& _terminalApi; From 1afab788ab1d745159b0556a3f9d09eabe74cb9c Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Tue, 30 Jul 2019 16:35:08 -0700 Subject: [PATCH 008/154] Update the package version to v0.3 Acked-by: Pankaj Bhojwani Acked-by: Carlos Zamora --- src/cascadia/CascadiaPackage/CascadiaPackage.wapproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 665417e1d18..c5be3f34839 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -5,7 +5,7 @@ 0 - 2 + 3 10.0.18362.0 @@ -305,4 +305,4 @@ - \ No newline at end of file + From a08666b58eb65b85ddf94e69892fa3e0a084ba98 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 30 Jul 2019 16:43:10 -0700 Subject: [PATCH 009/154] Accessibility: TermControl Automation Peer (#2083) Builds on the work of #1691 and #1915 Let's start with the easy change: - `TermControl`'s `controlRoot` was removed. `TermControl` is a `UserControl` now. Ok. Now we've got a story to tell here.... ### TermControlAP - the Automation Peer Here's an in-depth guide on custom automation peers: https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers We have a custom XAML element (TermControl). So XAML can't really hold our hands and determine an accessible behavior for us. So this automation peer is responsible for enabling that interaction. We made it a FrameworkElementAutomationPeer to get as much accessibility as possible from it just being a XAML element (i.e.: where are we on the screen? what are my dimensions?). This is recommended. Any functions with "Core" at the end, are overwritten here to tweak this automation peer into what we really need. But what kind of interactions can a user expect from this XAML element? Introducing ControlPatterns! There's a ton of interfaces that just define "what can I do". Thankfully, we already know that we're supposed to be `ScreenInfoUiaProvider` and that was an `ITextProvider`, so let's just make the TermControlAP an `ITextProvider` too. So now we have a way to define what accessible actions can be performed on us, but what should those actions do? Well let's just use the automation providers from ConHost that are now in a shared space! (Note: this is a great place to stop and get some coffee. We're about to hop into the .cpp file in the next section) ### Wrapping our shared Automation Providers Unfortunately, we can't just use the automation providers from ConHost. Or, at least not just hook them up as easily as we wish. ConHost's UIA Providers were written using UIAutomationCore and ITextRangeProiuder. XAML's interfaces ITextProvider and ITextRangeProvider are lined up to be exactly the same. So we need to wrap our ConHost UIA Providers (UIAutomationCore) with the XAML ones. We had two providers, so that means we have two wrappers. #### TermControlAP (XAML) <----> ScreenInfoUiaProvider (UIAutomationCore) Each of the functions in the pragma region `ITextProvider` for TermControlAP.cpp is just wrapping what we do in `ScreenInfoUiaProvider`, and returning an acceptable version of it. Most of `ScreenInfoUiaProvider`'s functions return `UiaTextRange`s. So we need to wrap that too. That's this next section... #### XamlUiaTextRange (XAML) <----> UiaTextRange (UIAutomationCore) Same idea. We're wrapping everything that we could do with `UiaTextRange` and putting it inside of `XamlUiaTextRange`. ### Additional changes to `UiaTextRange` and `ScreenInfoUiaProvider` If you don't know what I just said, please read this background: - #1691: how accessibility works and the general responsibility of these two classes - #1915: how we pulled these Accessibility Providers into a shared area TL;DR: `ScreenInfoUiaProvider` lets you interact with the displayed text. `UiaTextRange` is specific ranges of text in the display and navigate the text. Thankfully, we didn't do many changes here. I feel like some of it is hacked together but now that we have a somewhat working system, making changes shouldn't be too hard...I hope. #### UiaTextRange We don't have access to the window handle. We really only need it to draw the bounding rects using WinUser's `ScreenToClient()` and `ClientToScreen()`. I need to figure out how to get around this. In the meantime, I made the window handle optional. And if we don't have one....well, we need to figure that out. But other than that, we have a `UiaTextRange`. #### ScreenInfoUiaProvider At some point, we need to hook up this automation provider to the WindowUiaProvider. This should help with navigation of the UIA Tree and make everything just look waaaay better. For now, let's just do the same approach and make the pUiaParent optional. This one's the one I'm not that proud of, but it works. We need the parent to get a bounding rect of the terminal. While we figure out how to attach the WindowUiaProvider, we should at the very least be able to get a bunch of info from our xaml automation peer. So, I've added a _getBoundingRect optional function. This is what's called when we don't have a WindowUiaProvider as our parent. ## Validation Steps Performed I've been using inspect.exe to see the UIA tree. I was able to interact with the terminal mostly fine. A few known issues below. Unfortunately, I tried running Narrator on this and it didn't seem to like it (by that I mean WT crashed). Then again, I don't really know how to use narrator other than "click on object" --> "listen voice". I feel like there's a way to get the other interactions with narrator, but I'll be looking into more of that soon. I bet if I fix the two issues below, Narrator will be happy. ## Miscellaneous Known Issues - `GetSelection()` and `GetVisibleRanges()` crashes. I need to debug through these. I want to include them in this PR. Fixes #1353. --- src/cascadia/TerminalApp/App.cpp | 2 +- src/cascadia/TerminalApp/Pane.cpp | 12 +- src/cascadia/TerminalApp/Tab.cpp | 4 +- src/cascadia/TerminalControl/TermControl.cpp | 39 ++-- src/cascadia/TerminalControl/TermControl.h | 6 +- src/cascadia/TerminalControl/TermControl.idl | 4 +- .../TermControlAutomationPeer.cpp | 154 ++++++++++++++ .../TermControlAutomationPeer.h | 63 ++++++ .../TermControlAutomationPeer.idl | 14 ++ .../TerminalControl/TerminalControl.vcxproj | 11 +- .../TerminalControl.vcxproj.filters | 12 +- .../TerminalControl/XamlUiaTextRange.cpp | 199 ++++++++++++++++++ .../TerminalControl/XamlUiaTextRange.h | 75 +++++++ src/cascadia/TerminalControl/pch.h | 2 + src/cascadia/inc/cppwinrt_utils.h | 32 ++- src/interactivity/win32/window.cpp | 6 +- src/types/ScreenInfoUiaProvider.cpp | 42 +++- src/types/ScreenInfoUiaProvider.h | 8 + src/types/UiaTextRange.cpp | 21 +- src/types/lib/types.vcxproj.filters | 191 ++++++++++++++--- 20 files changed, 811 insertions(+), 86 deletions(-) create mode 100644 src/cascadia/TerminalControl/TermControlAutomationPeer.cpp create mode 100644 src/cascadia/TerminalControl/TermControlAutomationPeer.h create mode 100644 src/cascadia/TerminalControl/TermControlAutomationPeer.idl create mode 100644 src/cascadia/TerminalControl/XamlUiaTextRange.cpp create mode 100644 src/cascadia/TerminalControl/XamlUiaTextRange.h diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index a822f7b6f13..aa42e859543 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -881,7 +881,7 @@ namespace winrt::TerminalApp::implementation _UpdateTitle(tab); }); - term.GetControl().GotFocus([this, weakTabPtr](auto&&, auto&&) { + term.GotFocus([this, weakTabPtr](auto&&, auto&&) { auto tab = weakTabPtr.lock(); if (!tab) { diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 173d5ef14a1..5262bf45148 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -19,7 +19,7 @@ Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocus _lastFocused{ lastFocused }, _profile{ profile } { - _root.Children().Append(_control.GetControl()); + _root.Children().Append(_control); _connectionClosedToken = _control.ConnectionClosed({ this, &Pane::_ControlClosedHandler }); // Set the background of the pane to match that of the theme's default grid @@ -426,7 +426,7 @@ bool Pane::_HasFocusedChild() const noexcept // We're intentionally making this one giant expression, so the compiler // will skip the following lookups if one of the lookups before it returns // true - return (_control && _control.GetControl().FocusState() != FocusState::Unfocused) || + return (_control && _control.FocusState() != FocusState::Unfocused) || (_firstChild && _firstChild->_HasFocusedChild()) || (_secondChild && _secondChild->_HasFocusedChild()); } @@ -445,7 +445,7 @@ void Pane::UpdateFocus() if (_IsLeaf()) { const auto controlFocused = _control && - _control.GetControl().FocusState() != FocusState::Unfocused; + _control.FocusState() != FocusState::Unfocused; _lastFocused = controlFocused; } @@ -468,7 +468,7 @@ void Pane::_FocusFirstChild() { if (_IsLeaf()) { - _control.GetControl().Focus(FocusState::Programmatic); + _control.Focus(FocusState::Programmatic); } else { @@ -564,11 +564,11 @@ void Pane::_CloseChild(const bool closeFirst) _separatorRoot = { nullptr }; // Reattach the TermControl to our grid. - _root.Children().Append(_control.GetControl()); + _root.Children().Append(_control); if (_lastFocused) { - _control.GetControl().Focus(FocusState::Programmatic); + _control.Focus(FocusState::Programmatic); } _splitState = SplitState::None; diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 94acf9feee1..ad8a938c3b1 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -124,7 +124,7 @@ void Tab::_Focus() auto lastFocusedControl = _rootPane->GetFocusedTerminalControl(); if (lastFocusedControl) { - lastFocusedControl.GetControl().Focus(FocusState::Programmatic); + lastFocusedControl.Focus(FocusState::Programmatic); } } @@ -181,7 +181,7 @@ void Tab::SetTabText(const winrt::hstring& text) void Tab::Scroll(const int delta) { auto control = GetFocusedTerminalControl(); - control.GetControl().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta]() { + control.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [control, delta]() { const auto currentOffset = control.GetScrollOffset(); control.KeyboardScrollViewport(currentOffset + delta); }); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 14e3b776c40..2f263ac4bcf 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -11,6 +11,7 @@ #include "..\..\types\inc\GlyphWidth.hpp" #include "TermControl.g.cpp" +#include "TermControlAutomationPeer.h" using namespace ::Microsoft::Console::Types; using namespace ::Microsoft::Terminal::Core; @@ -30,7 +31,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _connection{ connection }, _initializedTerminal{ false }, _root{ nullptr }, - _controlRoot{ nullptr }, _swapChainPanel{ nullptr }, _settings{ settings }, _closing{ false }, @@ -52,11 +52,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void TermControl::_Create() { - // Create a dummy UserControl to use as the "root" of our control we'll - // build manually. - Controls::UserControl myControl; - _controlRoot = myControl; - Controls::Grid container; Controls::ColumnDefinition contentColumn{}; @@ -108,20 +103,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _bgImageLayer = bgImageLayer; _swapChainPanel = swapChainPanel; - _controlRoot.Content(_root); + this->Content(_root); _ApplyUISettings(); // These are important: // 1. When we get tapped, focus us - _controlRoot.Tapped([this](auto&, auto& e) { - _controlRoot.Focus(FocusState::Pointer); + this->Tapped([this](auto&, auto& e) { + this->Focus(FocusState::Pointer); e.Handled(true); }); // 2. Make sure we can be focused (why this isn't `Focusable` I'll never know) - _controlRoot.IsTabStop(true); + this->IsTabStop(true); // 3. Actually not sure about this one. Maybe it isn't necessary either. - _controlRoot.AllowFocusOnInteraction(true); + this->AllowFocusOnInteraction(true); // DON'T CALL _InitializeTerminal here - wait until the swap chain is loaded to do that. @@ -345,14 +340,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation Close(); } - UIElement TermControl::GetRoot() + Windows::UI::Xaml::Automation::Peers::AutomationPeer TermControl::OnCreateAutomationPeer() { - return _root; + // create a custom automation peer with this code pattern: + // (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers) + return winrt::make(*this); } - Controls::UserControl TermControl::GetControl() + ::Microsoft::Console::Render::IRenderData* TermControl::GetRenderData() const { - return _controlRoot; + return _terminal.get(); } void TermControl::SwapChainChanged() @@ -506,9 +503,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // through CharacterRecieved. // I don't believe there's a difference between KeyDown and // PreviewKeyDown for our purposes - // These two handlers _must_ be on _controlRoot, not _root. - _controlRoot.PreviewKeyDown({ this, &TermControl::_KeyDownHandler }); - _controlRoot.CharacterReceived({ this, &TermControl::_CharacterHandler }); + // These two handlers _must_ be on this, not _root. + this->PreviewKeyDown({ this, &TermControl::_KeyDownHandler }); + this->CharacterReceived({ this, &TermControl::_CharacterHandler }); auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1); _terminal->SetTitleChangedCallback(pfnTitleChanged); @@ -542,14 +539,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // import value from WinUser (convert from milli-seconds to micro-seconds) _multiClickTimer = GetDoubleClickTime() * 1000; - _gotFocusRevoker = _controlRoot.GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler }); - _lostFocusRevoker = _controlRoot.LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler }); + _gotFocusRevoker = this->GotFocus(winrt::auto_revoke, { this, &TermControl::_GotFocusHandler }); + _lostFocusRevoker = this->LostFocus(winrt::auto_revoke, { this, &TermControl::_LostFocusHandler }); // Focus the control here. If we do it up above (in _Create_), then the // focus won't actually get passed to us. I believe this is because // we're not technically a part of the UI tree yet, so focusing us // becomes a no-op. - _controlRoot.Focus(FocusState::Programmatic); + this->Focus(FocusState::Programmatic); _connection.Start(); _initializedTerminal = true; diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 57892b4924a..eb4072a98ff 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -35,8 +35,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation TermControl(); TermControl(Settings::IControlSettings settings, TerminalConnection::ITerminalConnection connection); - Windows::UI::Xaml::UIElement GetRoot(); - Windows::UI::Xaml::Controls::UserControl GetControl(); void UpdateSettings(Settings::IControlSettings newSettings); hstring Title(); @@ -55,6 +53,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void SwapChainChanged(); ~TermControl(); + Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); + ::Microsoft::Console::Render::IRenderData* GetRenderData() const; + static Windows::Foundation::Point GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi); // clang-format off @@ -71,7 +72,6 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation TerminalConnection::ITerminalConnection _connection; bool _initializedTerminal; - Windows::UI::Xaml::Controls::UserControl _controlRoot; Windows::UI::Xaml::Controls::Grid _root; Windows::UI::Xaml::Controls::Image _bgImageLayer; Windows::UI::Xaml::Controls::SwapChainPanel _swapChainPanel; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 69ef35670e7..d9d23806324 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -14,15 +14,13 @@ namespace Microsoft.Terminal.TerminalControl } [default_interface] - runtimeclass TermControl + runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl { TermControl(); TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); static Windows.Foundation.Point GetProposedDimensions(Microsoft.Terminal.Settings.IControlSettings settings, UInt32 dpi); - Windows.UI.Xaml.UIElement GetRoot(); - Windows.UI.Xaml.Controls.UserControl GetControl(); void UpdateSettings(Microsoft.Terminal.Settings.IControlSettings newSettings); event TitleChangedEventArgs TitleChanged; diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp new file mode 100644 index 00000000000..ab887080784 --- /dev/null +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include +#include "TermControlAutomationPeer.h" +#include "TermControl.h" +#include "TermControlAutomationPeer.g.cpp" + +#include "XamlUiaTextRange.h" + +using namespace Microsoft::Console::Types; +using namespace winrt::Windows::UI::Xaml::Automation::Peers; + +namespace UIA +{ + using ::ITextRangeProvider; + using ::SupportedTextSelection; +} + +namespace XamlAutomation +{ + using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection; + using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple; + using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider; +} + +namespace winrt::Microsoft::Terminal::TerminalControl::implementation +{ + TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner) : + TermControlAutomationPeerT(owner), // pass owner to FrameworkElementAutomationPeer + _uiaProvider{ owner.GetRenderData(), nullptr, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; + + winrt::hstring TermControlAutomationPeer::GetClassNameCore() const + { + return L"TermControl"; + } + + AutomationControlType TermControlAutomationPeer::GetAutomationControlTypeCore() const + { + return AutomationControlType::Text; + } + + winrt::hstring TermControlAutomationPeer::GetLocalizedControlTypeCore() const + { + // TODO GitHub #2142: Localize string + return L"TerminalControl"; + } + + winrt::Windows::Foundation::IInspectable TermControlAutomationPeer::GetPatternCore(PatternInterface patternInterface) const + { + switch (patternInterface) + { + case PatternInterface::Text: + return *this; + break; + default: + return nullptr; + } + } + +#pragma region ITextProvider + winrt::com_array TermControlAutomationPeer::GetSelection() + { + SAFEARRAY* pReturnVal; + THROW_IF_FAILED(_uiaProvider.GetSelection(&pReturnVal)); + return WrapArrayOfTextRangeProviders(pReturnVal); + } + + winrt::com_array TermControlAutomationPeer::GetVisibleRanges() + { + SAFEARRAY* pReturnVal; + THROW_IF_FAILED(_uiaProvider.GetVisibleRanges(&pReturnVal)); + return WrapArrayOfTextRangeProviders(pReturnVal); + } + + XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromChild(XamlAutomation::IRawElementProviderSimple childElement) + { + UIA::ITextRangeProvider* returnVal; + // ScreenInfoUiaProvider doesn't actually use parameter, so just pass in nullptr + THROW_IF_FAILED(_uiaProvider.RangeFromChild(/* IRawElementProviderSimple */ nullptr, + &returnVal)); + + auto parentProvider = this->ProviderFromPeer(*this); + auto xutr = winrt::make_self(returnVal, parentProvider); + return xutr.as(); + } + + XamlAutomation::ITextRangeProvider TermControlAutomationPeer::RangeFromPoint(Windows::Foundation::Point screenLocation) + { + UIA::ITextRangeProvider* returnVal; + THROW_IF_FAILED(_uiaProvider.RangeFromPoint({ screenLocation.X, screenLocation.Y }, &returnVal)); + + auto parentProvider = this->ProviderFromPeer(*this); + auto xutr = winrt::make_self(returnVal, parentProvider); + return xutr.as(); + } + + XamlAutomation::ITextRangeProvider TermControlAutomationPeer::DocumentRange() + { + UIA::ITextRangeProvider* returnVal; + THROW_IF_FAILED(_uiaProvider.get_DocumentRange(&returnVal)); + + auto parentProvider = this->ProviderFromPeer(*this); + auto xutr = winrt::make_self(returnVal, parentProvider); + return xutr.as(); + } + + Windows::UI::Xaml::Automation::SupportedTextSelection TermControlAutomationPeer::SupportedTextSelection() + { + UIA::SupportedTextSelection returnVal; + THROW_IF_FAILED(_uiaProvider.get_SupportedTextSelection(&returnVal)); + return static_cast(returnVal); + } + +#pragma endregion + + RECT TermControlAutomationPeer::GetBoundingRectWrapped() + { + auto rect = GetBoundingRectangle(); + return { + gsl::narrow(rect.X), + gsl::narrow(rect.Y), + gsl::narrow(rect.X + rect.Width), + gsl::narrow(rect.Y + rect.Height) + }; + } + + // Method Description: + // - extracts the UiaTextRanges from the SAFEARRAY and converts them to Xaml ITextRangeProviders + // Arguments: + // - SAFEARRAY of UIA::UiaTextRange (ITextRangeProviders) + // Return Value: + // - com_array of Xaml Wrapped UiaTextRange (ITextRangeProviders) + winrt::com_array TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges) + { + // transfer ownership of UiaTextRanges to this new vector + auto providers = SafeArrayToOwningVector<::Microsoft::Console::Types::UiaTextRange>(textRanges); + int count = providers.size(); + + std::vector vec; + vec.reserve(count); + auto parentProvider = this->ProviderFromPeer(*this); + for (int i = 0; i < count; i++) + { + auto xutr = winrt::make_self(providers[i].detach(), parentProvider); + vec.emplace_back(xutr.as()); + } + + winrt::com_array result{ vec }; + + return result; + } +} diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.h b/src/cascadia/TerminalControl/TermControlAutomationPeer.h new file mode 100644 index 00000000000..e5db0b405da --- /dev/null +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.h @@ -0,0 +1,63 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- TermControlAutomationPeer.h + +Abstract: +- This module provides UI Automation access to the TermControl + to support both automation tests and accessibility (screen + reading) applications. This mainly interacts with ScreenInfoUiaProvider + to allow for shared code between ConHost and Windows Terminal + accessibility providers. +- Based on the Custom Automation Peers guide on msdn + (https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/custom-automation-peers) +- Wraps the UIAutomationCore ITextProvider + (https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nn-uiautomationcore-itextprovider) + with a XAML ITextProvider + (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.automation.provider.itextprovider) + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "TermControl.h" +#include "TermControlAutomationPeer.g.h" +#include +#include "../../renderer/inc/IRenderData.hpp" +#include "../types/ScreenInfoUiaProvider.h" +#include "../types/WindowUiaProviderBase.hpp" + +namespace winrt::Microsoft::Terminal::TerminalControl::implementation +{ + struct TermControlAutomationPeer : + public TermControlAutomationPeerT + { + public: + TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner); + + winrt::hstring GetClassNameCore() const; + winrt::Windows::UI::Xaml::Automation::Peers::AutomationControlType GetAutomationControlTypeCore() const; + winrt::hstring GetLocalizedControlTypeCore() const; + winrt::Windows::Foundation::IInspectable GetPatternCore(winrt::Windows::UI::Xaml::Automation::Peers::PatternInterface patternInterface) const; + +#pragma region ITextProvider Pattern + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromPoint(Windows::Foundation::Point screenLocation); + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider RangeFromChild(Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple childElement); + winrt::com_array GetVisibleRanges(); + winrt::com_array GetSelection(); + Windows::UI::Xaml::Automation::SupportedTextSelection SupportedTextSelection(); + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider DocumentRange(); +#pragma endregion + + RECT GetBoundingRectWrapped(); + + private: + ::Microsoft::Console::Types::ScreenInfoUiaProvider _uiaProvider; + + winrt::com_array WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges); + }; +} diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.idl b/src/cascadia/TerminalControl/TermControlAutomationPeer.idl new file mode 100644 index 00000000000..d979c82471f --- /dev/null +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.idl @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "TermControl.idl"; + +namespace Microsoft.Terminal.TerminalControl +{ + [default_interface] + runtimeclass TermControlAutomationPeer : + Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer, + Windows.UI.Xaml.Automation.Provider.ITextProvider + { + } +} diff --git a/src/cascadia/TerminalControl/TerminalControl.vcxproj b/src/cascadia/TerminalControl/TerminalControl.vcxproj index 8dca317e41e..c50e9c096ef 100644 --- a/src/cascadia/TerminalControl/TerminalControl.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControl.vcxproj @@ -20,6 +20,10 @@ TermControl.idl + + TermControlAutomationPeer.idl + + @@ -30,9 +34,14 @@ TermControl.idl + + TermControlAutomationPeer.idl + + + @@ -50,7 +59,7 @@ false - + false false diff --git a/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters b/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters index f2869cf5bea..d3862cb0fea 100644 --- a/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters +++ b/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters @@ -11,20 +11,20 @@ - + + - + + - - - + @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/src/cascadia/TerminalControl/XamlUiaTextRange.cpp b/src/cascadia/TerminalControl/XamlUiaTextRange.cpp new file mode 100644 index 00000000000..4640019cb83 --- /dev/null +++ b/src/cascadia/TerminalControl/XamlUiaTextRange.cpp @@ -0,0 +1,199 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "XamlUiaTextRange.h" +#include "../types/UiaTextRange.hpp" + +namespace UIA +{ + using ::ITextRangeProvider; + using ::SupportedTextSelection; + using ::TextPatternRangeEndpoint; + using ::TextUnit; +} + +namespace XamlAutomation +{ + using winrt::Windows::UI::Xaml::Automation::SupportedTextSelection; + using winrt::Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple; + using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider; + using winrt::Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint; + using winrt::Windows::UI::Xaml::Automation::Text::TextUnit; +} + +namespace winrt::Microsoft::Terminal::TerminalControl::implementation +{ + XamlAutomation::ITextRangeProvider XamlUiaTextRange::Clone() const + { + UIA::ITextRangeProvider* pReturn; + THROW_IF_FAILED(_uiaProvider->Clone(&pReturn)); + auto xutr = winrt::make_self(pReturn, _parentProvider); + return xutr.as(); + } + + bool XamlUiaTextRange::Compare(XamlAutomation::ITextRangeProvider pRange) const + { + auto self = winrt::get_self(pRange); + + BOOL returnVal; + THROW_IF_FAILED(_uiaProvider->Compare(self->_uiaProvider.get(), &returnVal)); + return returnVal; + } + + int32_t XamlUiaTextRange::CompareEndpoints(XamlAutomation::TextPatternRangeEndpoint endpoint, + XamlAutomation::ITextRangeProvider pTargetRange, + XamlAutomation::TextPatternRangeEndpoint targetEndpoint) + { + auto self = winrt::get_self(pTargetRange); + + int32_t returnVal; + THROW_IF_FAILED(_uiaProvider->CompareEndpoints(static_cast(endpoint), + self->_uiaProvider.get(), + static_cast(targetEndpoint), + &returnVal)); + return returnVal; + } + + void XamlUiaTextRange::ExpandToEnclosingUnit(XamlAutomation::TextUnit unit) const + { + THROW_IF_FAILED(_uiaProvider->ExpandToEnclosingUnit(static_cast(unit))); + } + + XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindAttribute(int32_t textAttributeId, + winrt::Windows::Foundation::IInspectable val, + bool searchBackward) + { + // TODO GitHub #2161: potential accessibility improvement + // we don't support this currently + throw winrt::hresult_not_implemented(); + } + + XamlAutomation::ITextRangeProvider XamlUiaTextRange::FindText(winrt::hstring text, + bool searchBackward, + bool ignoreCase) + { + // TODO GitHub #605: Search functionality + // we need to wrap this around the UiaTextRange FindText() function + // but right now it returns E_NOTIMPL, so let's just return nullptr for now. + throw winrt::hresult_not_implemented(); + } + + winrt::Windows::Foundation::IInspectable XamlUiaTextRange::GetAttributeValue(int32_t textAttributeId) const + { + // Copied functionality from Types::UiaTextRange.cpp + if (textAttributeId == UIA_IsReadOnlyAttributeId) + { + return winrt::box_value(false); + } + else + { + return nullptr; + } + } + + void XamlUiaTextRange::GetBoundingRectangles(com_array& returnValue) const + { + returnValue = {}; + try + { + SAFEARRAY* pReturnVal; + THROW_IF_FAILED(_uiaProvider->GetBoundingRectangles(&pReturnVal)); + + double* pVals; + THROW_IF_FAILED(SafeArrayAccessData(pReturnVal, (void**)&pVals)); + + long lBound, uBound; + THROW_IF_FAILED(SafeArrayGetLBound(pReturnVal, 1, &lBound)); + THROW_IF_FAILED(SafeArrayGetUBound(pReturnVal, 1, &uBound)); + + long count = uBound - lBound + 1; + + std::vector vec; + vec.reserve(count); + for (int i = 0; i < count; i++) + { + double element = pVals[i]; + vec.push_back(element); + } + + winrt::com_array result{ vec }; + returnValue = std::move(result); + } + catch (...) + { + } + } + + XamlAutomation::IRawElementProviderSimple XamlUiaTextRange::GetEnclosingElement() + { + return _parentProvider; + } + + winrt::hstring XamlUiaTextRange::GetText(int32_t maxLength) const + { + BSTR returnVal; + THROW_IF_FAILED(_uiaProvider->GetText(maxLength, &returnVal)); + return winrt::to_hstring(returnVal); + } + + int32_t XamlUiaTextRange::Move(XamlAutomation::TextUnit unit, + int32_t count) + { + int returnVal; + THROW_IF_FAILED(_uiaProvider->Move(static_cast(unit), + count, + &returnVal)); + return returnVal; + } + + int32_t XamlUiaTextRange::MoveEndpointByUnit(XamlAutomation::TextPatternRangeEndpoint endpoint, + XamlAutomation::TextUnit unit, + int32_t count) const + { + int returnVal; + THROW_IF_FAILED(_uiaProvider->MoveEndpointByUnit(static_cast(endpoint), + static_cast(unit), + count, + &returnVal)); + return returnVal; + } + + void XamlUiaTextRange::MoveEndpointByRange(XamlAutomation::TextPatternRangeEndpoint endpoint, + XamlAutomation::ITextRangeProvider pTargetRange, + XamlAutomation::TextPatternRangeEndpoint targetEndpoint) const + { + auto self = winrt::get_self(pTargetRange); + THROW_IF_FAILED(_uiaProvider->MoveEndpointByRange(static_cast(endpoint), + /*pTargetRange*/ self->_uiaProvider.get(), + static_cast(targetEndpoint))); + } + + void XamlUiaTextRange::Select() const + { + THROW_IF_FAILED(_uiaProvider->Select()); + } + + void XamlUiaTextRange::AddToSelection() const + { + // we don't support this + throw winrt::hresult_not_implemented(); + } + + void XamlUiaTextRange::RemoveFromSelection() const + { + // we don't support this + throw winrt::hresult_not_implemented(); + } + + void XamlUiaTextRange::ScrollIntoView(bool alignToTop) const + { + THROW_IF_FAILED(_uiaProvider->ScrollIntoView(alignToTop)); + } + + winrt::com_array XamlUiaTextRange::GetChildren() const + { + // we don't have any children + return {}; + } +} diff --git a/src/cascadia/TerminalControl/XamlUiaTextRange.h b/src/cascadia/TerminalControl/XamlUiaTextRange.h new file mode 100644 index 00000000000..6560f997bbb --- /dev/null +++ b/src/cascadia/TerminalControl/XamlUiaTextRange.h @@ -0,0 +1,75 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- XamlUiaTextRange.h + +Abstract: +- This module is a wrapper for the UiaTextRange + (a text range accessibility provider). It allows + for UiaTextRange to be used in Windows Terminal. +- Wraps the UIAutomationCore ITextRangeProvider + (https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcore/nn-uiautomationcore-itextrangeprovider) + with a XAML ITextRangeProvider + (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.automation.provider.itextrangeprovider) + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "TermControlAutomationPeer.h" +#include +#include "../types/UiaTextRange.hpp" + +namespace winrt::Microsoft::Terminal::TerminalControl::implementation +{ + class XamlUiaTextRange : + public winrt::implements + { + public: + XamlUiaTextRange(::ITextRangeProvider* uiaProvider, Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple parentProvider) : + _parentProvider{ parentProvider } + { + _uiaProvider.attach(uiaProvider); + } + +#pragma region ITextRangeProvider + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider Clone() const; + bool Compare(Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pRange) const; + int32_t CompareEndpoints(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange, + Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint); + void ExpandToEnclosingUnit(Windows::UI::Xaml::Automation::Text::TextUnit unit) const; + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindAttribute(int32_t textAttributeId, + winrt::Windows::Foundation::IInspectable val, + bool searchBackward); + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider FindText(winrt::hstring text, + bool searchBackward, + bool ignoreCase); + winrt::Windows::Foundation::IInspectable GetAttributeValue(int32_t textAttributeId) const; + void GetBoundingRectangles(winrt::com_array& returnValue) const; + Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple GetEnclosingElement(); + winrt::hstring GetText(int32_t maxLength) const; + int32_t Move(Windows::UI::Xaml::Automation::Text::TextUnit unit, + int32_t count); + int32_t MoveEndpointByUnit(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, + Windows::UI::Xaml::Automation::Text::TextUnit unit, + int32_t count) const; + void MoveEndpointByRange(Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint endpoint, + Windows::UI::Xaml::Automation::Provider::ITextRangeProvider pTargetRange, + Windows::UI::Xaml::Automation::Text::TextPatternRangeEndpoint targetEndpoint) const; + void Select() const; + void AddToSelection() const; + void RemoveFromSelection() const; + void ScrollIntoView(bool alignToTop) const; + winrt::com_array GetChildren() const; +#pragma endregion ITextRangeProvider + + private: + wil::com_ptr<::ITextRangeProvider> _uiaProvider; + Windows::UI::Xaml::Automation::Provider::IRawElementProviderSimple _parentProvider; + }; +} diff --git a/src/cascadia/TerminalControl/pch.h b/src/cascadia/TerminalControl/pch.h index a70b22fdbb1..c8e879242d4 100644 --- a/src/cascadia/TerminalControl/pch.h +++ b/src/cascadia/TerminalControl/pch.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index 8a36d12c39f..f088734b2d5 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -55,4 +55,34 @@ private: // Use this if you have a Windows.Foundation.TypedEventHandler #define DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(className, name, eventHandler, sender, args) \ winrt::event_token className::name(Windows::Foundation::TypedEventHandler const& handler) { return eventHandler.add(handler); } \ - void className::name(winrt::event_token const& token) noexcept { eventHandler.remove(token); } \ No newline at end of file + void className::name(winrt::event_token const& token) noexcept { eventHandler.remove(token); } + +// This is a helper method for deserializing a SAFEARRAY of +// COM objects and converting it to a vector that +// owns the extracted COM objects +template +std::vector> SafeArrayToOwningVector(SAFEARRAY* safeArray) +{ + T** pVals; + THROW_IF_FAILED(SafeArrayAccessData(safeArray, (void**)&pVals)); + + THROW_HR_IF(E_UNEXPECTED, SafeArrayGetDim(safeArray) != 1); + + long lBound, uBound; + THROW_IF_FAILED(SafeArrayGetLBound(safeArray, 1, &lBound)); + THROW_IF_FAILED(SafeArrayGetUBound(safeArray, 1, &uBound)); + + long count = uBound - lBound + 1; + + // If any of the above fail, we cannot destruct/release + // any of the elements in the SAFEARRAY because we + // cannot identify how many elements there are. + + std::vector> result{ gsl::narrow(count) }; + for (int i = 0; i < count; i++) + { + result[i].attach(pVals[i]); + } + + return result; +} diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 6e4f5e2cfde..a8451f3b0c9 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -314,16 +314,16 @@ void Window::_UpdateSystemMetrics() const if (useDx) { - status = NTSTATUS_FROM_HRESULT(pDxEngine->SetHwnd(hWnd)); + status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->SetHwnd(hWnd)))); if (NT_SUCCESS(status)) { - status = NTSTATUS_FROM_HRESULT(pDxEngine->Enable()); + status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->Enable()))); } } else { - status = NTSTATUS_FROM_HRESULT(pGdiEngine->SetHwnd(hWnd)); + status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pGdiEngine->SetHwnd(hWnd)))); } if (NT_SUCCESS(status)) diff --git a/src/types/ScreenInfoUiaProvider.cpp b/src/types/ScreenInfoUiaProvider.cpp index e6dd03b016f..fbc4c190a33 100644 --- a/src/types/ScreenInfoUiaProvider.cpp +++ b/src/types/ScreenInfoUiaProvider.cpp @@ -31,9 +31,22 @@ SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int return psa; } +ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ WindowUiaProviderBase* const pUiaParent, + _In_ std::function GetBoundingRect) : + _pUiaParent(pUiaParent), + _signalFiringMapping{}, + _cRefs(1), + _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)), + _getBoundingRect(GetBoundingRect) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); +} + ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ WindowUiaProviderBase* const pUiaParent) : - _pUiaParent(THROW_HR_IF_NULL(E_INVALIDARG, pUiaParent)), + _pUiaParent(pUiaParent), _signalFiringMapping{}, _cRefs(1), _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)) @@ -253,6 +266,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_HostRawElementProvider(_COM_Outptr_res IFACEMETHODIMP ScreenInfoUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { + // TODO GitHub 2120: _pUiaParent should not be allowed to be null + RETURN_HR_IF(E_NOTIMPL, _pUiaParent == nullptr); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree /*ApiMsgNavigate apiMsg; apiMsg.Direction = direction; @@ -299,7 +315,16 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); - RECT rc = _pUiaParent->GetWindowRect(); + RECT rc; + // TODO GitHub 2120: _pUiaParent should not be allowed to be null + if (_pUiaParent == nullptr) + { + rc = _getBoundingRect(); + } + else + { + rc = _pUiaParent->GetWindowRect(); + } pRect->left = rc.left; pRect->top = rc.top; @@ -328,6 +353,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::SetFocus() IFACEMETHODIMP ScreenInfoUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) { + // TODO GitHub 2120: _pUiaParent should not be allowed to be null + RETURN_HR_IF(E_NOTIMPL, _pUiaParent == nullptr); + //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); try { @@ -657,10 +685,20 @@ void ScreenInfoUiaProvider::_UnlockConsole() noexcept HWND ScreenInfoUiaProvider::GetWindowHandle() const { + // TODO GitHub 2120: _pUiaParent should not be allowed to be null + if (_pUiaParent == nullptr) + { + return nullptr; + } return _pUiaParent->GetWindowHandle(); } void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow) { + // TODO GitHub 2120: _pUiaParent should not be allowed to be null + if (_pUiaParent == nullptr) + { + return; + } _pUiaParent->ChangeViewport(NewWindow); } diff --git a/src/types/ScreenInfoUiaProvider.h b/src/types/ScreenInfoUiaProvider.h index 707bd0428bd..7d4cb374f1f 100644 --- a/src/types/ScreenInfoUiaProvider.h +++ b/src/types/ScreenInfoUiaProvider.h @@ -36,6 +36,11 @@ namespace Microsoft::Console::Types public ITextProvider { public: + ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + _In_ WindowUiaProviderBase* const pUiaParent, + _In_ std::function GetBoundingRect); + + // TODO GitHub 2120: pUiaParent should not be allowed to be null ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ WindowUiaProviderBase* const pUiaParent); virtual ~ScreenInfoUiaProvider(); @@ -108,6 +113,9 @@ namespace Microsoft::Console::Types const Viewport _getViewport() const; void _LockConsole() noexcept; void _UnlockConsole() noexcept; + + // these functions are reserved for Windows Terminal + std::function _getBoundingRect; }; namespace ScreenInfoUiaProviderTracing diff --git a/src/types/UiaTextRange.cpp b/src/types/UiaTextRange.cpp index aa96f4db59a..5439c9a8841 100644 --- a/src/types/UiaTextRange.cpp +++ b/src/types/UiaTextRange.cpp @@ -307,7 +307,14 @@ UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, { // change point coords to pixels relative to window HWND hwnd = _getWindowHandle(); - ScreenToClient(hwnd, &clientPoint); + if (hwnd == nullptr) + { + // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT() + } + else + { + ScreenToClient(hwnd, &clientPoint); + } const COORD currentFontSize = _getScreenFontSize(); row = (clientPoint.y / currentFontSize.Y) + viewport.Top; @@ -1495,8 +1502,16 @@ void UiaTextRange::_addScreenInfoRowBoundaries(Microsoft::Console::Render::IRend // convert the coords to be relative to the screen instead of // the client window HWND hwnd = _getWindowHandle(); - ClientToScreen(hwnd, &topLeft); - ClientToScreen(hwnd, &bottomRight); + + if (hwnd == nullptr) + { + // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF CLIENTTOSCREEN() + } + else + { + ClientToScreen(hwnd, &topLeft); + ClientToScreen(hwnd, &bottomRight); + } const LONG width = bottomRight.x - topLeft.x; const LONG height = bottomRight.y - topLeft.y; diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index 11b565e87f6..35b6f908f1a 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -1,42 +1,165 @@  - + + {4FC737F1-C7A5-4376-A066-2A32D752A3FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52ECFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {77DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + - - - - - - - - - - - - - - - - - - + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + - - - - - - - - - - - - - - + - \ No newline at end of file + From 66044ca6052ebb375b784906acad677ceb84c8d4 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 31 Jul 2019 16:58:16 -0700 Subject: [PATCH 010/154] Try to turn audit mode back on without building test/utilities (#2179) * Attempt to remove all test and utility projects from audit mode (and turn it back on) to see if that keeps it within the disk space boundaries. * drop x86 and arm configs for the test projects too. --- OpenConsole.sln | 63 ------------------------------------------ build/pipelines/ci.yml | 8 ++---- 2 files changed, 3 insertions(+), 68 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index 788474d06c8..efac4b8f7a2 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -464,11 +464,8 @@ Global {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x86.ActiveCfg = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954747}.Release|x86.Build.0 = Release|Win32 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|ARM64.Build.0 = Release|ARM64 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x64.Build.0 = Release|x64 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8AAD-04164CB628C9}.AuditMode|x86.Build.0 = Release|Win32 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|ARM64.Build.0 = Debug|ARM64 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Debug|x64.ActiveCfg = Debug|x64 @@ -482,11 +479,8 @@ Global {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x86.ActiveCfg = Release|Win32 {531C23E7-4B76-4C08-8AAD-04164CB628C9}.Release|x86.Build.0 = Release|Win32 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|ARM64.Build.0 = Release|ARM64 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x64.ActiveCfg = Release|x64 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x64.Build.0 = Release|x64 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x86.ActiveCfg = Release|Win32 - {531C23E7-4B76-4C08-8BBD-04164CB628C9}.AuditMode|x86.Build.0 = Release|Win32 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|ARM64.Build.0 = Debug|ARM64 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Debug|x64.ActiveCfg = Debug|x64 @@ -500,11 +494,8 @@ Global {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x86.ActiveCfg = Release|Win32 {531C23E7-4B76-4C08-8BBD-04164CB628C9}.Release|x86.Build.0 = Release|Win32 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|ARM64.Build.0 = Release|ARM64 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x64.ActiveCfg = Release|x64 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x64.Build.0 = Release|x64 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x86.ActiveCfg = Release|Win32 - {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.AuditMode|x86.Build.0 = Release|Win32 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|ARM64.ActiveCfg = Debug|ARM64 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|ARM64.Build.0 = Debug|ARM64 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Debug|x64.ActiveCfg = Debug|x64 @@ -516,11 +507,8 @@ Global {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|x64.Build.0 = Release|x64 {8CDB8850-7484-4EC7-B45B-181F85B2EE54}.Release|x86.ActiveCfg = Release|Win32 {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|ARM64.Build.0 = Release|ARM64 {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x64.ActiveCfg = Release|x64 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x64.Build.0 = Release|x64 {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x86.ActiveCfg = Release|Win32 - {12144E07-FE63-4D33-9231-748B8D8C3792}.AuditMode|x86.Build.0 = Release|Win32 {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|ARM64.ActiveCfg = Debug|ARM64 {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|ARM64.Build.0 = Debug|ARM64 {12144E07-FE63-4D33-9231-748B8D8C3792}.Debug|x64.ActiveCfg = Debug|x64 @@ -534,11 +522,8 @@ Global {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x86.ActiveCfg = Release|Win32 {12144E07-FE63-4D33-9231-748B8D8C3792}.Release|x86.Build.0 = Release|Win32 {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|ARM64.Build.0 = Release|ARM64 {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x64.ActiveCfg = Release|x64 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x64.Build.0 = Release|x64 {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x86.ActiveCfg = Release|Win32 - {6AF01638-84CF-4B65-9870-484DFFCAC772}.AuditMode|x86.Build.0 = Release|Win32 {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|ARM64.Build.0 = Debug|ARM64 {6AF01638-84CF-4B65-9870-484DFFCAC772}.Debug|x64.ActiveCfg = Debug|x64 @@ -552,11 +537,8 @@ Global {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x86.ActiveCfg = Release|Win32 {6AF01638-84CF-4B65-9870-484DFFCAC772}.Release|x86.Build.0 = Release|Win32 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|ARM64.Build.0 = Release|ARM64 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x64.ActiveCfg = Release|x64 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x64.Build.0 = Release|x64 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x86.ActiveCfg = Release|Win32 - {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.AuditMode|x86.Build.0 = Release|Win32 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|ARM64.ActiveCfg = Debug|ARM64 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|ARM64.Build.0 = Debug|ARM64 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Debug|x64.ActiveCfg = Debug|x64 @@ -570,11 +552,8 @@ Global {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x86.ActiveCfg = Release|Win32 {96927B31-D6E8-4ABD-B03E-A5088A30BEBE}.Release|x86.Build.0 = Release|Win32 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|ARM64.Build.0 = Release|ARM64 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x64.ActiveCfg = Release|x64 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x64.Build.0 = Release|x64 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x86.ActiveCfg = Release|Win32 - {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.AuditMode|x86.Build.0 = Release|Win32 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|ARM64.Build.0 = Debug|ARM64 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Debug|x64.ActiveCfg = Debug|x64 @@ -624,11 +603,8 @@ Global {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x86.ActiveCfg = Release|Win32 {18D09A24-8240-42D6-8CB6-236EEE820262}.Release|x86.Build.0 = Release|Win32 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|ARM64.ActiveCfg = Debug|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|ARM64.Build.0 = Debug|Win32 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x64.ActiveCfg = Release|x64 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x64.Build.0 = Release|x64 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x86.ActiveCfg = Release|Win32 - {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.AuditMode|x86.Build.0 = Release|Win32 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|ARM64.ActiveCfg = Debug|Win32 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x64.ActiveCfg = Debug|x64 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Debug|x64.Build.0 = Debug|x64 @@ -640,11 +616,8 @@ Global {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x86.ActiveCfg = Release|Win32 {C17E1BF3-9D34-4779-9458-A8EF98CC5662}.Release|x86.Build.0 = Release|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|ARM64.ActiveCfg = Debug|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|ARM64.Build.0 = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x64.ActiveCfg = Release|x64 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x64.Build.0 = Release|x64 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x86.ActiveCfg = Release|Win32 - {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.AuditMode|x86.Build.0 = Release|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|ARM64.ActiveCfg = Debug|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x64.ActiveCfg = Debug|x64 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Debug|x64.Build.0 = Debug|x64 @@ -656,11 +629,8 @@ Global {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x86.ActiveCfg = Release|Win32 {099193A0-1E43-4BBC-BA7F-7B351E1342DF}.Release|x86.Build.0 = Release|Win32 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|ARM64.Build.0 = Release|ARM64 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x64.ActiveCfg = Release|x64 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x64.Build.0 = Release|x64 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x86.ActiveCfg = Release|Win32 - {FC802440-AD6A-4919-8F2C-7701F2B38D79}.AuditMode|x86.Build.0 = Release|Win32 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|ARM64.Build.0 = Debug|ARM64 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Debug|x64.ActiveCfg = Debug|x64 @@ -674,11 +644,8 @@ Global {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x86.ActiveCfg = Release|Win32 {FC802440-AD6A-4919-8F2C-7701F2B38D79}.Release|x86.Build.0 = Release|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|ARM64.Build.0 = Release|ARM64 {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x64.ActiveCfg = Release|x64 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x64.Build.0 = Release|x64 {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x86.ActiveCfg = Release|Win32 - {919544AC-D39B-463F-8414-3C3C67CF727C}.AuditMode|x86.Build.0 = Release|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|ARM64.ActiveCfg = Debug|ARM64 {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|ARM64.Build.0 = Debug|ARM64 {919544AC-D39B-463F-8414-3C3C67CF727C}.Debug|x64.ActiveCfg = Debug|x64 @@ -692,11 +659,8 @@ Global {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x86.ActiveCfg = Release|Win32 {919544AC-D39B-463F-8414-3C3C67CF727C}.Release|x86.Build.0 = Release|Win32 {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|ARM64.Build.0 = Release|ARM64 {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x64.Build.0 = Release|x64 {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B36-F480018ED064}.AuditMode|x86.Build.0 = Release|Win32 {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|ARM64.ActiveCfg = Debug|ARM64 {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|ARM64.Build.0 = Debug|ARM64 {ED82003F-FC5D-4E94-8B36-F480018ED064}.Debug|x64.ActiveCfg = Debug|x64 @@ -728,11 +692,8 @@ Global {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x86.ActiveCfg = Release|Win32 {06EC74CB-9A12-429C-B551-8532EC964726}.Release|x86.Build.0 = Release|Win32 {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|ARM64.Build.0 = Release|ARM64 {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x64.ActiveCfg = Release|x64 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x64.Build.0 = Release|x64 {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x86.ActiveCfg = Release|Win32 - {ED82003F-FC5D-4E94-8B47-F480018ED064}.AuditMode|x86.Build.0 = Release|Win32 {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|ARM64.ActiveCfg = Debug|ARM64 {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|ARM64.Build.0 = Debug|ARM64 {ED82003F-FC5D-4E94-8B47-F480018ED064}.Debug|x64.ActiveCfg = Debug|x64 @@ -764,11 +725,8 @@ Global {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x86.ActiveCfg = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC964846}.Release|x86.Build.0 = Release|Win32 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|ARM64.Build.0 = Release|ARM64 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x64.ActiveCfg = Release|x64 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x64.Build.0 = Release|x64 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x86.ActiveCfg = Release|Win32 - {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.AuditMode|x86.Build.0 = Release|Win32 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|ARM64.Build.0 = Debug|ARM64 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Debug|x64.ActiveCfg = Debug|x64 @@ -782,11 +740,8 @@ Global {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x86.ActiveCfg = Release|Win32 {D3B92829-26CB-411A-BDA2-7F5DA3D25DD4}.Release|x86.Build.0 = Release|Win32 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|ARM64.Build.0 = Release|ARM64 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x64.ActiveCfg = Release|x64 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x64.Build.0 = Release|x64 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x86.ActiveCfg = Release|Win32 - {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.AuditMode|x86.Build.0 = Release|Win32 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|ARM64.ActiveCfg = Debug|ARM64 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|ARM64.Build.0 = Debug|ARM64 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Debug|x64.ActiveCfg = Debug|x64 @@ -818,11 +773,8 @@ Global {990F2657-8580-4828-943F-5DD657D11842}.Release|x86.ActiveCfg = Release|Win32 {990F2657-8580-4828-943F-5DD657D11842}.Release|x86.Build.0 = Release|Win32 {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.Build.0 = Release|ARM64 {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x64.ActiveCfg = Release|x64 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x64.Build.0 = Release|x64 {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x86.ActiveCfg = Release|Win32 - {814DBDDE-894E-4327-A6E1-740504850098}.AuditMode|x86.Build.0 = Release|Win32 {814DBDDE-894E-4327-A6E1-740504850098}.Debug|ARM64.ActiveCfg = Debug|ARM64 {814DBDDE-894E-4327-A6E1-740504850098}.Debug|ARM64.Build.0 = Debug|ARM64 {814DBDDE-894E-4327-A6E1-740504850098}.Debug|x64.ActiveCfg = Debug|x64 @@ -836,11 +788,8 @@ Global {814DBDDE-894E-4327-A6E1-740504850098}.Release|x86.ActiveCfg = Release|Win32 {814DBDDE-894E-4327-A6E1-740504850098}.Release|x86.Build.0 = Release|Win32 {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|ARM64.Build.0 = Release|ARM64 {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x64.ActiveCfg = Release|x64 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x64.Build.0 = Release|x64 {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x86.ActiveCfg = Release|Win32 - {814CBEEE-894E-4327-A6E1-740504850098}.AuditMode|x86.Build.0 = Release|Win32 {814CBEEE-894E-4327-A6E1-740504850098}.Debug|ARM64.ActiveCfg = Debug|ARM64 {814CBEEE-894E-4327-A6E1-740504850098}.Debug|ARM64.Build.0 = Debug|ARM64 {814CBEEE-894E-4327-A6E1-740504850098}.Debug|x64.ActiveCfg = Debug|x64 @@ -872,11 +821,8 @@ Global {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.ActiveCfg = Release|Win32 {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.Build.0 = Release|Win32 {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|ARM64.Build.0 = Release|ARM64 {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|x64.ActiveCfg = Release|x64 - {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|x64.Build.0 = Release|x64 {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|x86.ActiveCfg = Release|Win32 - {990F2657-8580-4828-943F-5DD657D11843}.AuditMode|x86.Build.0 = Release|Win32 {990F2657-8580-4828-943F-5DD657D11843}.Debug|ARM64.ActiveCfg = Debug|ARM64 {990F2657-8580-4828-943F-5DD657D11843}.Debug|ARM64.Build.0 = Debug|ARM64 {990F2657-8580-4828-943F-5DD657D11843}.Debug|x64.ActiveCfg = Debug|x64 @@ -1061,11 +1007,8 @@ Global {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.Release|x86.Build.0 = Release|x86 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.Release|x86.Deploy.0 = Release|x86 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.Build.0 = AuditMode|x64 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.Build.0 = AuditMode|Win32 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.Build.0 = Debug|ARM64 {2C2BEEF4-9333-4D05-B12A-1905CBF112F9}.Debug|x64.ActiveCfg = Debug|x64 @@ -1097,11 +1040,8 @@ Global {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.ActiveCfg = Release|Win32 {EF3E32A7-5FF6-42B4-B6E2-96CD7D033F00}.Release|x86.Build.0 = Release|Win32 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x64.Build.0 = AuditMode|x64 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.AuditMode|x86.Build.0 = AuditMode|Win32 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.ActiveCfg = Debug|ARM64 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|ARM64.Build.0 = Debug|ARM64 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Debug|x64.ActiveCfg = Debug|x64 @@ -1115,11 +1055,8 @@ Global {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.ActiveCfg = Release|Win32 {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x64.Build.0 = AuditMode|x64 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.AuditMode|x86.Build.0 = AuditMode|Win32 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml index 237656e5a03..ed2925b248a 100644 --- a/build/pipelines/ci.yml +++ b/build/pipelines/ci.yml @@ -19,11 +19,9 @@ pr: name: 0.0.$(Date:yyMM).$(Date:dd)$(Rev:rr) jobs: -# This is disabled because the build agents were running out of disk space. -# We're pursuing that in the background, but the spice must flow in the meantime. -# - template: ./templates/build-console-audit-job.yml -# parameters: -# platform: x64 + - template: ./templates/build-console-audit-job.yml + parameters: + platform: x64 - template: ./templates/build-console-ci.yml parameters: From 6749ab03b81b5944267ce5eb0515d02a2ac5fd84 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 1 Aug 2019 14:23:10 +0100 Subject: [PATCH 011/154] First draft of a spec for VT52 escape sequences (#2017) * First draft of a spec for splitting off the existing VT52 escape sequences, and extending the VT52 support. * Make the issue ID visible on GitHub. * Added suggested mappings for the Graphics Mode character set. * Add escape sequences for all the commands and clarify the use of the ESC < sequence when switching back to ANSI mode. * Add details about the differing boundary rules of the VT100 CUP command and the VT52 Direct Cursor Address command. * Specify the identifying sequence that the Identify command should return. * Add details of the print commands. * Add a list of keyboard sequences that are different in the VT52 mode, and make the description of the Keypad Mode commands a little clearer. * Add a section describing the testing needed to cover the new functionality. --- doc/specs/#976 - VT52 escape sequences.md | 255 ++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 doc/specs/#976 - VT52 escape sequences.md diff --git a/doc/specs/#976 - VT52 escape sequences.md b/doc/specs/#976 - VT52 escape sequences.md new file mode 100644 index 00000000000..90b2ef60591 --- /dev/null +++ b/doc/specs/#976 - VT52 escape sequences.md @@ -0,0 +1,255 @@ +--- +author: James Holderness @j4james +created on: 2019-07-17 +last updated: 2019-07-28 +issue id: 976 +--- + +# VT52 Escape Sequences + +## Abstract + +This spec outlines the work required to split off the existing VT52 commands from the VT100 implementation, and extend the VT52 support to cover all of the core commands. + +## Inspiration + +The existing VT52 commands aren't currently implemented as a separate mode, so they conflict with sequences defined in the VT100 specification. This is blocking us from adding support for the VT100 Index (IND) escape sequence, which is one of the missing commands required to pass the test of cursor movements in Vttest. + +## Solution Design + +The basic idea is to add support for the [DECANM private mode sequence](https://vt100.net/docs/vt100-ug/chapter3.html#DECANM), which can then be used to switch from the default _ANSI_ mode, to a new _VT52_ mode. Once in _VT52_ mode, there is a separate [_Enter ANSI Mode_ sequence](https://vt100.net/docs/vt100-ug/chapter3.html#VT52ANSI) (`ESC <`) to switch back again. + +In terms of implementation, there are a number of areas of the system that would need to be updated. + +### The State Machine + +In order to implement the VT52 compatibility mode correctly, we'll need to introduce a flag in the `StateMachine` class that indicates the mode that is currently active. When in VT52 mode, certain paths in the state diagram should not be followed - for example, you can't have CSI, OSC, or SS3 escape sequences. There would also need to be an additional state to handle VT52 parameters (for the _Direct Cursor Address_ command). These parameters take a different form to the typical VT100 parameters, as they follow the command character instead of preceding it. + +It would probably be best to introduce a new dispatch method in the `IStateMachineEngine` interface to handle the parsed VT52 sequences, since the existing `ActionEscDispatch` does not support parameters (which are required for the _Direct Cursor Address_ command). I think it would also make for a cleaner implementation to have the VT52 commands separate from the VT100 code, and would likely have less impact on the performance that way. + +### The Terminal Input + +The escape sequences generated by the keyboard for function keys, cursor keys, and the numeric keypad, are not the same in VT52 mode as they are in ANSI mode. So there would need to be a flag in the `TerminalInput` class to keep track of the current mode, and thus be able to generate the appropriate sequences for that mode. + +Technically the VT52 keyboard doesn't map directly to a typical PC keyboard, so we can't always work from the specs in deciding what sequences are required for each key. When in doubt, we should probably be trying to match the key sequences generated by XTerm. The sequences below are based on the default XTerm mappings. + +**Function Keys** + +The functions keys F1 to F4 generate a simple ESC prefix instead of SS3 (or CSI). These correspond with the four function keys on the VT100 keypad. In V52 mode they are not affected by modifiers. + +Key | ANSI mode | VT52 mode +---------------|-----------|----------- +F1 | `SS3 P` | `ESC P` +F2 | `SS3 Q` | `ESC Q` +F3 | `SS3 R` | `ESC R` +F4 | `SS3 S` | `ESC S` + +The function keys F5 to F12 generate the same sequences as they do in ANSI mode, except that they are not affected by modifiers. These correspond with a subset of the top-row functions keys on the VT220, along with the Windows Menu key mapping to the VT220 DO key. + +Key | Sequence +----------------|------------- +F5 | `CSI 1 5 ~` +F6 | `CSI 1 7 ~` +F7 | `CSI 1 8 ~` +F8 | `CSI 1 9 ~` +F9 | `CSI 2 0 ~` +F10 | `CSI 2 1 ~` +F11 | `CSI 2 3 ~` +F12 | `CSI 2 4 ~` +Menu | `CSI 2 9 ~` + +**Cursor and Editing Keys** + +The cursor keys generate a simple ESC prefix instead of CSI or SS3. These correspond with the cursor keys on the VT100, except for Home and End, which are XTerm extensions. In V52 mode, they are not affected by modifiers, nor are they affected by the DECCKM _Cursor Keys_ mode. + +Key | ANSI mode | VT52 mode +-----------------|-----------|----------- +Up | `CSI A` | `ESC A` +Down | `CSI B` | `ESC B` +Right | `CSI C` | `ESC C` +Left | `CSI D` | `ESC D` +End | `CSI F` | `ESC F` +Home | `CSI H` | `ESC H` + +The "editing" keys generate the same sequences as they do in ANSI mode, except that they are not affected by modifiers. These correspond with a subset of the editing keys on the VT220. + +Key | Sequence +----------------|----------- +Ins | `CSI 2 ~` +Del | `CSI 3 ~` +PgUp | `CSI 5 ~` +PgDn | `CSI 6 ~` + +**Numeric Keypad** + +With Num Lock disabled, most of the keys on the numeric keypad function the same as cursor keys or editing keys, but with the addition of a center 5 key. As a described above, the cursor keys generate a simple ESC prefix instead of CSI or SS3, while the editing keys remain unchanged (with the exception of modifiers). + +In V52 mode, most modifiers are ignored, except for Shift, which is the equivalent of enabling Num Lock (i.e. the keys just generate their corresponding digit characters or `.`). With Num Lock enabled, it's the other way arround - the digits are generated by default, while Shift enables the cursor/editing functionality. + +Key | Alias | ANSI mode | VT52 mode +-------------|-------|-----------|----------- +. | Del | `CSI 3 ~` | `CSI 3 ~` +0 | Ins | `CSI 2 ~` | `CSI 2 ~` +1 | End | `CSI F` | `ESC F` +2 | Down | `CSI B` | `ESC B` +3 | PgDn | `CSI 6 ~` | `CSI 6 ~` +4 | Left | `CSI D` | `ESC D` +4 | Clear | `CSI E` | `ESC E` +6 | Right | `CSI C` | `ESC C` +7 | Home | `CSI H` | `ESC H` +8 | Up | `CSI A` | `ESC A` +9 | PgUp | `CSI 5 ~` | `CSI 5 ~` + +When the DECKPAM _Alternate/Application Keypad Mode_ is set, though, the Shift modifier has a different affect on the numeric keypad. The sequences generated now correspond with the VT100/V52 numeric keypad keys. In VT52 mode, these sequences are not affected by any other modifiers, and this mode only applies when Num Lock is disabled. + +Key | Alias | ANSI mode | VT52 mode +-------------|-------|-----------|----------- +. | Del | `SS3 2 n` | `ESC ? n` +0 | Ins | `SS3 2 p` | `ESC ? p` +1 | End | `SS3 2 q` | `ESC ? q ` +2 | Down | `SS3 2 r` | `ESC ? r` +3 | PgDn | `SS3 2 s` | `ESC ? s` +4 | Left | `SS3 2 t` | `ESC ? t` +4 | Clear | `SS3 2 u` | `ESC ? u` +6 | Right | `SS3 2 v` | `ESC ? v` +7 | Home | `SS3 2 w` | `ESC ? w` +8 | Up | `SS3 2 x` | `ESC ? x` +9 | PgUp | `SS3 2 y` | `ESC ? y` + +When the DECKPAM _Alternate/Application Keypad Mode_ is set, the "arithmetic" keys on the numeric keypad are also affected (this includes the Enter key). The sequences generated again correspond with the VT100/VT52 numeric keys (more or less), but this mapping is active even without the Shift modifier (and in VT52 mode all other modifiers are ignored too). As above, the mode only applies when Num Lock is disabled. + +Key | ANSI mode | VT52 mode +-----------------|-----------|----------- +* | `SS3 j` | `ESC ? j` ++ | `SS3 k` | `ESC ? k` +- | `SS3 m` | `ESC ? m` +/ | `SS3 o` | `ESC ? o` +Enter | `SS3 M` | `ESC ? M` + +Note that the DECKPAM _Application Keypad Mode_ is not currently implemented in ANSI mode, so perhaps that needs to be addressed first, before trying to add support for the VT52 _Alternate Keypad Mode_. + +### Changing Modes + +The `_PrivateModeParamsHelper` method in the `AdaptDispatch` class would need to be extended to handle the DECANM mode parameter, and trigger a function to switch to VT52 mode. The typical pattern for this seems to be through a `PrivateXXX` method in the `ConGetSet` interface. Then the `ConhostInternalGetSet` implementation can pass that flag on to the active output buffer's `StateMachine`, and the active input buffer's `TerminalInput` instance. + +Changing back from VT52 mode to ANSI mode would need to be achieved with a separate VT52 command (`ESC <`), since the VT100 CSI mode sequences would no longer be active. This would be handled in the same place as the other VT52 commands, in the `OutputStateMachineEngine`, and then passed on to the mode selection method in the `AdaptDispatch` class described above (essentially the equivalent of the DECANM private mode being set). + +### Additional VT52 Commands + +Most of the missing VT52 functionality can be implemented in terms of existing VT100 methods. + +* The _Cursor Up_ (`ESC A`), _Cursor Down_ (`ESC B`), _Cursor Left_ (`ESC D`), and _Cursor Right_ (`ESC C`) commands are already implemented. +* The _Enter Graphics Mode_ (`ESC F`) and _Exit Graphics Mode_ (`ESC G`) commands can probably use the existing `DesignateCharset` method, although this would require a new `VTCharacterSets` option with a corresponding table of characters (see below). +* The _Reverse Line Feed_ (`ESC I`) command can use the existing `ReverseLineFeed` method. +* The _Erase to End of Display_ (`ESC J`) and _Erase to End of Line_ (`ESC K`) commands can use the existing `EraseInDisplay` and `EraseInLine` methods. +* The _Cursor Home_ (`ESC H`) and _Direct Cursor Address_ (`ESC Y`) commands can probably be implemented using the `CursorPosition` method. Technically the _Direct Cursor Address_ has different rules for the boundary conditions (the CUP command clamps out of range coordinates, while the _Direct Cursor Address_ command ignores them, judged individually - one may be ignored while the other is interpreted). Nobody seems to get that right, though, so it's probably not that big a deal. +* The _Identify_ (`ESC Z`) command may be the only one that doesn't build on existing functionality, but it should be a fairly trivial addition to the `AdaptDispatch` class. For a terminal emulating VT52, the identifying sequence should be `ESC / Z`. +* The _Enter Keypad Mode_ (`ESC =`) and _Exit Keypad Mode_ (`ESC >`) commands can use the existing `SetKeypadMode` method, assuming the `TerminalInput` class already knows to generate different sequences when in VT52 mode (as described in the _Terminal Input_ section above). +* The _Enter ANSI Mode_ (`ESC <`) command can just call through to the new mode selection method in the `AdaptDispatch` class as discussed in the _Changing Modes_ section above. + +There are also a few VT52 print commands, but those are not technically part of the core command set, and considering we don't yet support any of the VT102 print commands, I think they can probably be considered out of scope for now. Briefly they are: + +* _Auto Print_ on (`ESC ^`) and off (`ESC _`) commands. In auto print mode, a display line prints after you move the cursor off the line, or during an auto wrap. +* _Print Controller_ on (`ESC W`) and off (`ESC X`) commands. When enabled, the terminal transmits received characters to the printer without displaying them. +* The _Print Cursor Line_ (`ESC V`) command prints the display line with the cursor. +* The _Print Screen_ (`ESC ]`) command prints the screen (or at least the scrolling region). + +I suspect most, if not all of these, would be direct equivalents of the VT102 print commands, if we ever implemented those. + +### Graphic Mode Character Set + +The table below lists suggested mappings for the _Graphics Mode_ character set, based on the descriptions in the [VT102 User Guide](https://vt100.net/docs/vt102-ug/table5-15.html). + +Note that there is only the one _fraction numerator_ character in Unicode, so superscript digits have instead been used for the numerators 3, 5, and 7. There are also not enough _horizontal scan line_ characters (for the _bar at scan x_ characters), so each of them is used twice to cover the full range. + +ASCII Character |Mapped Glyph |Unicode Value |Spec Description +----------------|---------------|---------------|---------------- +_ | |U+0020 |Blank +` | |U+0020 |Reserved +a |█ |U+2588 |Solid rectangle +b |⅟ |U+215F |1/ +c |³ |U+00B3 |3/ +d |⁵ |U+2075 |5/ +e |⁷ |U+2077 |7/ +f |° |U+00B0 |Degrees +g |± |U+00B1 |Plus or minus +h |→ |U+2192 |Right arrow +i |… |U+2026 |Ellipsis (dots) +j |÷ |U+00F7 |Divide by +k |↓ |U+2193 |Down arrow +l |⎺ |U+23BA |Bar at scan 0 +m |⎺ |U+23BA |Bar at scan 1 +n |⎻ |U+23BB |Bar at scan 2 +o |⎻ |U+23BB |Bar at scan 3 +p |⎼ |U+23BC |Bar at scan 4 +q |⎼ |U+23BC |Bar at scan 5 +r |⎽ |U+23BD |Bar at scan 6 +s |⎽ |U+23BD |Bar at scan 7 +t |₀ |U+2080 |Subscript 0 +u |₁ |U+2081 |Subscript 1 +v |₂ |U+2082 |Subscript 2 +w |₃ |U+2083 |Subscript 3 +x |₄ |U+2084 |Subscript 4 +y |₅ |U+2085 |Subscript 5 +z |₆ |U+2086 |Subscript 6 +{ |₇ |U+2087 |Subscript 7 +\| |₈ |U+2088 |Subscript 8 +} |₉ |U+2089 |Subscript 9 +\~ |¶ |U+00B6 |Paragraph + +### Testing + +A simple unit test will need to be added to the `AdapterTest` class, to confirm that calls to toggle between the ANSI and VT52 modes in the `AdaptDispatch` class are correctly forwarded to the corresponding `PrivateXXX` handler in the `ConGetSet` interface. + +The majority of the testing would be handled in the `StateMachineExternalTest` class though. These tests would confirm that the various VT52 sequences trigger the expected methods in the `ITermDispatch` interface when VT52 Mode is enabled, and also that they don't do anything when in ANSI mode. + +There shouldn't really be any need for additional tests in the `ScreenBufferTests` class, since we're relying on existing VT100 functionality which should already be tested there. + +For fuzzing support, we'll need to add the DECANM option to the `GeneratePrivateModeParamToken` method in the `VTCommandFuzzer` class, and also probably add two additional token generator methods - one specifically for the _Direct Cursor Address_ command, which requires parameters, and another to handle the remaining parameterless commands. + +In terms of manual testing, it can be useful to run the _Test of VT52 mode_ option in Vttest, and confirm that everything looks correct there. It's also worth going through some of the options in the The _Test of keyboard_ section, since those tests aren't only intended for the later VT models - they do cover the VT52 keyboard as well. + +## UI/UX Design + +There is no additional UI associated with this feature. + +## Capabilities + +### Accessibility + +This should not impact accessibility any more than the existing escape sequences. + +### Security + +This should not introduce any new security issues. + +### Reliability + +This should not introduce any new reliability issues. + +### Compatibility + +This could be a breaking change for code that relies on the few existing VT52 commands being available without a mode change. However, that functionality is non-standard, and has not been around for that long. There is almost certainly more benefit in being able to implement the missing VT100 functionality than there is in retaining that non-standard behaviour. + +### Performance, Power, and Efficiency + +The additional mode flags and associated processing in the `StateMachine` and `TerminalInput` classes could have some performance impact, but that is unlikely to be significant. + +## Potential Issues + +The only negative impacts I can think of would be the potential for breaking changes, and the possible impact on performance, as discussed in the _Compatibility_ and _Performance_ sections above. But as with any new code, there is always the possibility of new bugs being introduced as well. + +## Future considerations + +As mentioned in the _Inspiration_ section, having the VT52 functionality isolated with a new mode would enable us to implement the VT100 Index (IND) escape sequence, which currently conflicts with the VT52 _Cursor Left_ command. + +## Resources + +* [VT52 Mode Control Sequences](https://vt100.net/docs/vt100-ug/chapter3.html#S3.3.5) +* [VT100 ANSI/VT52 Mode (DECANM)](https://vt100.net/docs/vt100-ug/chapter3.html#DECANM) +* [VT100 Index Sequence (IND)](https://vt100.net/docs/vt100-ug/chapter3.html#IND) +* [VTTEST Test Utility](https://invisible-island.net/vttest/) +* [DEC STD 070 Video Systems Reference Manual](https://archive.org/details/bitsavers_decstandar0VideoSystemsReferenceManualDec91_74264381) + + + From f8f079882606247dcba6379dca20064bcffe65ef Mon Sep 17 00:00:00 2001 From: Tapasweni Pathak Date: Thu, 1 Aug 2019 19:04:18 +0530 Subject: [PATCH 012/154] Added information on WxH character (#2104) * Add information on WxH character * Add line and separate footnote --- doc/ConsoleHostSettings.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/ConsoleHostSettings.md b/doc/ConsoleHostSettings.md index 62a6a3dd27f..0a4d84847b7 100644 --- a/doc/ConsoleHostSettings.md +++ b/doc/ConsoleHostSettings.md @@ -8,9 +8,9 @@ Settings in the Windows Console Host can be a bit tricky to understand. This is |---------------------------|-----------------------|--------------------------------------| |`FontSize` |Coordinate (REG_DWORD) |Size of font in pixels | |`FontFamily` |REG_DWORD |GDI Font family | -|`ScreenBufferSize` |Coordinate (REG_DWORD) |Size of the screen buffer in WxH characters | +|`ScreenBufferSize` |Coordinate (REG_DWORD) |Size of the screen buffer in WxH characters\*\* | |`CursorSize` |REG_DWORD |Cursor height as percentage of a single character | -|`WindowSize` |Coordinate (REG_DWORD) |Initial size of the window in WxH characters | +|`WindowSize` |Coordinate (REG_DWORD) |Initial size of the window in WxH characters\*\* | |`WindowPosition` |Coordinate (REG_DWORD) |Initial position of the window in WxH pixels (if not set, use auto-positioning) | |`WindowAlpha` |REG_DWORD |Opacity of the window (valid range: 0x4D-0xFF) | |`ScreenColors` |REG_DWORD |Default foreground and background colors | @@ -39,6 +39,10 @@ Settings in the Windows Console Host can be a bit tricky to understand. This is *: Only applies to the improved version of the Windows Console Host +**: WxH stands for Width by Height, it's the fact that things like a Window size +store the Width and Height values in the high and low word in the registry's +double word values. + ## The Settings Hierarchy Settings are persisted to a variety of locations depending on how they are modified and how the Windows Console Host was invoked: From 0da13cdf2d5ba2081f12f85f6816546489d4918b Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Thu, 1 Aug 2019 13:19:22 -0700 Subject: [PATCH 013/154] Use ROW.Reset in EraseInDisplay instead of printing millions of spaces per line #2197 --- src/cascadia/TerminalCore/TerminalApi.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index a0504164088..8aad5abc010 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -337,11 +337,9 @@ bool Terminal::EraseInDisplay(const DispatchTypes::EraseType eraseType) // and we have to make sure we erase that text auto eraseStart = _mutableViewport.Height(); auto eraseEnd = _buffer->GetLastNonSpaceCharacter(_mutableViewport).Y; - auto eraseIter = OutputCellIterator(UNICODE_SPACE, _buffer->GetCurrentAttributes(), _mutableViewport.RightInclusive() * (eraseEnd - eraseStart + 1)); for (SHORT i = eraseStart; i <= eraseEnd; i++) { - COORD erasePos{ 0, i }; - _buffer->Write(eraseIter, erasePos); + _buffer->GetRowByOffset(i).Reset(_buffer->GetCurrentAttributes()); } // Reset the scroll offset now because there's nothing for the user to 'scroll' to From 42c1e58966b50c72039808b09b663b74180519ce Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Fri, 2 Aug 2019 13:27:34 -0700 Subject: [PATCH 014/154] Remove job object and startup suspended behavior because conhosts should clean themselves up. (#2198) --- .../TerminalConnection/ConhostConnection.cpp | 42 +++++-------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/src/cascadia/TerminalConnection/ConhostConnection.cpp b/src/cascadia/TerminalConnection/ConhostConnection.cpp index 027004cda53..90709bae40d 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.cpp +++ b/src/cascadia/TerminalConnection/ConhostConnection.cpp @@ -79,35 +79,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation extraEnvVars.emplace(L"WT_SESSION", pwszGuid); } - THROW_IF_FAILED( - CreateConPty(cmdline, - startingDirectory, - static_cast(_initialCols), - static_cast(_initialRows), - &_inPipe, - &_outPipe, - &_signalPipe, - &_piConhost, - CREATE_SUSPENDED, - extraEnvVars)); - - _hJob.reset(CreateJobObjectW(nullptr, nullptr)); - THROW_LAST_ERROR_IF_NULL(_hJob); - - // We want the conhost and all associated descendant processes - // to be terminated when the tab is closed. GUI applications - // spawned from the shell tend to end up in their own jobs. - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobExtendedInformation{}; - jobExtendedInformation.BasicLimitInformation.LimitFlags = - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; - - THROW_IF_WIN32_BOOL_FALSE(SetInformationJobObject(_hJob.get(), - JobObjectExtendedLimitInformation, - &jobExtendedInformation, - sizeof(jobExtendedInformation))); - - THROW_IF_WIN32_BOOL_FALSE(AssignProcessToJobObject(_hJob.get(), _piConhost.hProcess)); - // Create our own output handling thread // Each connection needs to make sure to drain the output from its backing host. _hOutputThread.reset(CreateThread(nullptr, @@ -119,8 +90,17 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation THROW_LAST_ERROR_IF_NULL(_hOutputThread); - // Wind up the conhost! We only do this after we've got everything in place. - THROW_LAST_ERROR_IF(-1 == ResumeThread(_piConhost.hThread)); + THROW_IF_FAILED( + CreateConPty(cmdline, + startingDirectory, + static_cast(_initialCols), + static_cast(_initialRows), + &_inPipe, + &_outPipe, + &_signalPipe, + &_piConhost, + 0, + extraEnvVars)); _connected = true; } From 0d8f2998d6fdfa6013854ea66ccf26ed34ba8de2 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Fri, 2 Aug 2019 14:41:46 -0700 Subject: [PATCH 015/154] Azure connector only shows up if available (#2195) The default azure connector profile only shows up if a) its a release build and b) its non-ARM64 Co-Authored-By: Dustin L. Howett (MSFT) --- src/cascadia/TerminalApp/App.cpp | 8 ++- src/cascadia/TerminalApp/CascadiaSettings.cpp | 31 ++++++------ .../AzureConnection-ARM64.cpp | 50 +++++++++++++++++++ .../AzureConnection-ARM64.h | 31 ++++++++++++ .../TerminalConnection/AzureConnection.cpp | 9 ++++ .../TerminalConnection/AzureConnection.h | 1 + .../TerminalConnection/AzureConnection.idl | 2 + .../TerminalConnection.vcxproj | 4 +- 8 files changed, 114 insertions(+), 22 deletions(-) create mode 100644 src/cascadia/TerminalConnection/AzureConnection-ARM64.cpp create mode 100644 src/cascadia/TerminalConnection/AzureConnection-ARM64.h diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index aa42e859543..65271ea62e3 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1461,20 +1461,18 @@ namespace winrt::TerminalApp::implementation { const auto* const profile = _settings->FindProfile(profileGuid); TerminalConnection::ITerminalConnection connection{ nullptr }; - // The Azure connection has a boost dependency, and boost does not support ARM64 - // so we make sure that we do not try to compile the Azure connection code if we are in ARM64 (we would get build errors otherwise) + GUID connectionType{ 0 }; if (profile->HasConnectionType()) { connectionType = profile->GetConnectionType(); } -#ifndef _M_ARM64 - if (connectionType == AzureConnectionType) + + if (profile->HasConnectionType() && profile->GetConnectionType() == AzureConnectionType && TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) { connection = TerminalConnection::AzureConnection(settings.InitialRows(), settings.InitialCols()); } else -#endif { connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), settings.InitialRows(), settings.InitialCols(), winrt::guid()); } diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 5224ad6ba94..9de05c54d4a 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -9,6 +9,7 @@ #include "CascadiaSettings.h" #include "../../types/inc/utils.hpp" #include "../../inc/DefaultSettings.h" +#include "winrt/Microsoft.Terminal.TerminalConnection.h" using namespace winrt::Microsoft::Terminal::Settings; using namespace ::TerminalApp; @@ -239,19 +240,6 @@ void CascadiaSettings::_CreateDefaultProfiles() powershellProfile.SetDefaultBackground(POWERSHELL_BLUE); powershellProfile.SetUseAcrylic(false); - // The Azure connection has a boost dependency, and boost does not support ARM64 - // so we don't create a default profile for the Azure cloud shell if we're in ARM64 -#ifndef _M_ARM64 - auto azureCloudShellProfile{ _CreateDefaultProfile(L"Azure Cloud Shell") }; - azureCloudShellProfile.SetCommandline(L"Azure"); - azureCloudShellProfile.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY); - azureCloudShellProfile.SetColorScheme({ L"Solarized Dark" }); - azureCloudShellProfile.SetAcrylicOpacity(0.85); - azureCloudShellProfile.SetUseAcrylic(true); - azureCloudShellProfile.SetCloseOnExit(false); - azureCloudShellProfile.SetConnectionType(AzureConnectionType); -#endif - // If the user has installed PowerShell Core, we add PowerShell Core as a default. // PowerShell Core default folder is "%PROGRAMFILES%\PowerShell\[Version]\". std::filesystem::path psCoreCmdline{}; @@ -274,9 +262,20 @@ void CascadiaSettings::_CreateDefaultProfiles() _profiles.emplace_back(powershellProfile); _profiles.emplace_back(cmdProfile); -#ifndef _M_ARM64 - _profiles.emplace_back(azureCloudShellProfile); -#endif + + if (winrt::Microsoft::Terminal::TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) + { + auto azureCloudShellProfile{ _CreateDefaultProfile(L"Azure Cloud Shell") }; + azureCloudShellProfile.SetCommandline(L"Azure"); + azureCloudShellProfile.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY); + azureCloudShellProfile.SetColorScheme({ L"Vintage" }); + azureCloudShellProfile.SetAcrylicOpacity(0.6); + azureCloudShellProfile.SetUseAcrylic(true); + azureCloudShellProfile.SetCloseOnExit(false); + azureCloudShellProfile.SetConnectionType(AzureConnectionType); + _profiles.emplace_back(azureCloudShellProfile); + } + try { _AppendWslProfiles(_profiles); diff --git a/src/cascadia/TerminalConnection/AzureConnection-ARM64.cpp b/src/cascadia/TerminalConnection/AzureConnection-ARM64.cpp new file mode 100644 index 00000000000..75e7bc21503 --- /dev/null +++ b/src/cascadia/TerminalConnection/AzureConnection-ARM64.cpp @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "AzureConnection-ARM64.h" +#include "AzureConnection.g.cpp" + +namespace winrt::Microsoft::Terminal::TerminalConnection::implementation +{ + bool AzureConnection::IsAzureConnectionAvailable() + { + return false; + } + AzureConnection::AzureConnection(uint32_t rows, uint32_t columns) + { + throw hresult_not_implemented(); + } + winrt::event_token AzureConnection::TerminalOutput(Microsoft::Terminal::TerminalConnection::TerminalOutputEventArgs const& handler) + { + throw hresult_not_implemented(); + } + void AzureConnection::TerminalOutput(winrt::event_token const& token) + { + throw hresult_not_implemented(); + } + winrt::event_token AzureConnection::TerminalDisconnected(Microsoft::Terminal::TerminalConnection::TerminalDisconnectedEventArgs const& handler) + { + throw hresult_not_implemented(); + } + void AzureConnection::TerminalDisconnected(winrt::event_token const& token) + { + throw hresult_not_implemented(); + } + void AzureConnection::Start() + { + throw hresult_not_implemented(); + } + void AzureConnection::WriteInput(hstring const& data) + { + throw hresult_not_implemented(); + } + void AzureConnection::Resize(uint32_t rows, uint32_t columns) + { + throw hresult_not_implemented(); + } + void AzureConnection::Close() + { + throw hresult_not_implemented(); + } +} diff --git a/src/cascadia/TerminalConnection/AzureConnection-ARM64.h b/src/cascadia/TerminalConnection/AzureConnection-ARM64.h new file mode 100644 index 00000000000..086ac775364 --- /dev/null +++ b/src/cascadia/TerminalConnection/AzureConnection-ARM64.h @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "AzureConnection.g.h" +#include "pch.h" + +namespace winrt::Microsoft::Terminal::TerminalConnection::implementation +{ + struct AzureConnection : AzureConnectionT + { + AzureConnection() = default; + + AzureConnection(uint32_t rows, uint32_t columns); + static bool IsAzureConnectionAvailable(); + winrt::event_token TerminalOutput(Microsoft::Terminal::TerminalConnection::TerminalOutputEventArgs const& handler); + void TerminalOutput(winrt::event_token const& token); + winrt::event_token TerminalDisconnected(Microsoft::Terminal::TerminalConnection::TerminalDisconnectedEventArgs const& handler); + void TerminalDisconnected(winrt::event_token const& token); + void Start(); + void WriteInput(hstring const& data); + void Resize(uint32_t rows, uint32_t columns); + void Close(); + }; +} +namespace winrt::Microsoft::Terminal::TerminalConnection::factory_implementation +{ + struct AzureConnection : AzureConnectionT + { + }; +} diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index e639371dc01..ea5ec4274ac 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -25,6 +25,15 @@ using namespace winrt::Windows::Security::Credentials; namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { + // This file only builds for non-ARM64 so we don't need to check that here + // This function exists because the clientID only gets added by the release pipelines + // and is not available on local builds, so we want to be able to make sure we don't + // try to make an Azure connection if its a local build + bool AzureConnection::IsAzureConnectionAvailable() + { + return (AzureClientID != L"0"); + } + AzureConnection::AzureConnection(const uint32_t initialRows, const uint32_t initialCols) : _initialRows{ initialRows }, _initialCols{ initialCols } diff --git a/src/cascadia/TerminalConnection/AzureConnection.h b/src/cascadia/TerminalConnection/AzureConnection.h index e747112a7b1..b6b889df714 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.h +++ b/src/cascadia/TerminalConnection/AzureConnection.h @@ -15,6 +15,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { struct AzureConnection : AzureConnectionT { + static bool IsAzureConnectionAvailable(); AzureConnection(const uint32_t rows, const uint32_t cols); winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler); diff --git a/src/cascadia/TerminalConnection/AzureConnection.idl b/src/cascadia/TerminalConnection/AzureConnection.idl index f0361c9037a..f925706c5d9 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.idl +++ b/src/cascadia/TerminalConnection/AzureConnection.idl @@ -7,6 +7,8 @@ namespace Microsoft.Terminal.TerminalConnection { [default_interface] runtimeclass AzureConnection : ITerminalConnection { + static Boolean IsAzureConnectionAvailable(); + AzureConnection(UInt32 rows, UInt32 columns); }; diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index 35c75d5a15b..65ca2bacad8 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -24,6 +24,7 @@ + @@ -35,6 +36,7 @@ + Create @@ -50,7 +52,7 @@ - + From 1b33d186f3e586934b15923f4f63cbc81fde60fa Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Mon, 5 Aug 2019 10:09:56 -0700 Subject: [PATCH 016/154] Update bug template with crash instructions (#2257) It's a doc change and the x86 CI is cranky. We're looking into it. --- .github/ISSUE_TEMPLATE/Bug_Report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.md b/.github/ISSUE_TEMPLATE/Bug_Report.md index d11fa6de851..67fe1df9438 100644 --- a/.github/ISSUE_TEMPLATE/Bug_Report.md +++ b/.github/ISSUE_TEMPLATE/Bug_Report.md @@ -26,6 +26,8 @@ This bug tracker is monitored by Windows Terminal development team and other tec **Important: When reporting BSODs or security issues, DO NOT attach memory dumps, logs, or traces to Github issues**. Instead, send dumps/traces to secure@microsoft.com, referencing this GitHub issue. +If this is an application crash, please also provide a Feedback Hub submission link so we can find your diagnostic data on the backend. Use the category "Apps > Windows Terminal (Preview)" and choose "Share My Feedback" after submission to get the link. + Please use this form and describe your issue, concisely but precisely, with as much detail as possible. --> From 3086671bc714f5a6c6dc586655c708600ef29822 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Mon, 5 Aug 2019 11:28:05 -0700 Subject: [PATCH 017/154] Update bot with new rules (#2259) We added a few more rules. Update the bot doc description. --- doc/bot.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/bot.md b/doc/bot.md index 1a215df7e36..0f1864e70d9 100644 --- a/doc/bot.md +++ b/doc/bot.md @@ -64,6 +64,12 @@ We'll be using tags, primarily, to help us understand what needs attention, what - If an issue is filed matching a pattern that happens all the time (common duplicate phrase, obvious multiple-issues-in-one pattern)... - Then close the issue automatically informing the opener that they can resolve the problem and reopen the issue. (See Bug/Feature templates for example situations.) +#### Help ask for Feedback Hub +- If an issue is tagged `Needs-Feedback-Hub` +- Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link. +- And remove the `Needs-Feedback-Hub` tag +- And add the `Needs-Author-Feedback` tag + ### PR Management #### Codeflow Link *(Disabled)* @@ -98,5 +104,14 @@ We'll be using tags, primarily, to help us understand what needs attention, what #### Add committed fix tag for completed PRs - When a PR is finished and there's no outstanding work left on a linked issue, add the `Resolution-Fix-Committed` label +#### Remove Needs-Second from completed PRs +- If a PR is closed and it has the `Needs-Second` tag, the bot will remove the tag. + +### Release Management + +When a release is created, if the PR ID number is linked inside the release description, the bot will walk through the related PR and all of its related issues and leave a message. +- PR message: "🎉{release name} {release version} has been released which incorporates this pull request.🎉 +- Issue message: 🎉This issue was addressed in #{pull request ID}, which has now been successfully released as {release name} {release version}.🎉" + ## Admin Panel [Here](https://fabric-cp.azurewebsites.net/bot/) From 4529e46d3ef4ca2d34a4cf0e95c9389622ee46e7 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 5 Aug 2019 23:58:48 +0200 Subject: [PATCH 018/154] Fixed Ctrl+Alt shortcuts conflicting with AltGr (#2235) This moves the detection of AltGr keypresses in front of the shortcut handling. This allows one to have Ctrl+Alt shortcuts, while simultaneously being able to use the AltGr key for special characters. --- src/cascadia/TerminalControl/TermControl.cpp | 79 +++++++++++++++----- src/cascadia/TerminalControl/TermControl.h | 2 + src/cascadia/TerminalCore/Terminal.cpp | 12 --- 3 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 2f263ac4bcf..a6469af3ab1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -619,35 +619,41 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } const auto modifiers = _GetPressedModifierKeys(); - const auto vkey = static_cast(e.OriginalKey()); + // AltGr key combinations don't always contain any meaningful, + // pretranslated unicode character during WM_KEYDOWN. + // E.g. on a German keyboard AltGr+Q should result in a "@" character, + // but actually results in "Q" with Alt and Ctrl modifier states. + // By returning false though, we can abort handling this WM_KEYDOWN + // event and let the WM_CHAR handler kick in, which will be + // provided with an appropriate unicode character. + // + // GH#2235: Make sure to handle AltGr before trying keybindings, + // so Ctrl+Alt keybindings won't eat an AltGr keypress. + if (modifiers.IsAltGrPressed()) + { + _HandleVoidKeyEvent(); + e.Handled(false); + return; + } + + const auto vkey = static_cast(e.OriginalKey()); bool handled = false; + auto bindings = _settings.KeyBindings(); if (bindings) { - KeyChord chord( + handled = bindings.TryKeyChord({ modifiers.IsCtrlPressed(), modifiers.IsAltPressed(), modifiers.IsShiftPressed(), - vkey); - handled = bindings.TryKeyChord(chord); + vkey, + }); } if (!handled) { - _terminal->ClearSelection(); - // If the terminal translated the key, mark the event as handled. - // This will prevent the system from trying to get the character out - // of it and sending us a CharacterRecieved event. - handled = _terminal->SendKeyEvent(vkey, modifiers); - - if (_cursorTimer.has_value()) - { - // Manually show the cursor when a key is pressed. Restarting - // the timer prevents flickering. - _terminal->SetCursorVisible(true); - _cursorTimer.value().Start(); - } + handled = _TrySendKeyEvent(vkey, modifiers); } // Manually prevent keyboard navigation with tab. We want to send tab to @@ -661,6 +667,45 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation e.Handled(handled); } + // Method Description: + // - Some key events cannot be handled (e.g. AltGr combinations) and are + // delegated to the character handler. Just like with _TrySendKeyEvent(), + // the character handler counts on us though to: + // - Clears the current selection. + // - Makes the cursor briefly visible during typing. + void TermControl::_HandleVoidKeyEvent() + { + _TrySendKeyEvent(0, {}); + } + + // Method Description: + // - Send this particular key event to the terminal. + // See Terminal::SendKeyEvent for more information. + // - Clears the current selection. + // - Makes the cursor briefly visible during typing. + // Arguments: + // - vkey: The vkey of the key pressed. + // - states: The Microsoft::Terminal::Core::ControlKeyStates representing the modifier key states. + bool TermControl::_TrySendKeyEvent(WORD vkey, const ControlKeyStates modifiers) + { + _terminal->ClearSelection(); + + // If the terminal translated the key, mark the event as handled. + // This will prevent the system from trying to get the character out + // of it and sending us a CharacterRecieved event. + const auto handled = vkey ? _terminal->SendKeyEvent(vkey, modifiers) : true; + + if (_cursorTimer.has_value()) + { + // Manually show the cursor when a key is pressed. Restarting + // the timer prevents flickering. + _terminal->SetCursorVisible(true); + _cursorTimer.value().Start(); + } + + return handled; + } + // Method Description: // - handle a mouse click event. Begin selection process. // Arguments: diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index eb4072a98ff..84104c2ed99 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -164,6 +164,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation static Windows::UI::Xaml::Thickness _ParseThicknessFromPadding(const hstring padding); ::Microsoft::Terminal::Core::ControlKeyStates _GetPressedModifierKeys() const; + void _HandleVoidKeyEvent(); + bool _TrySendKeyEvent(WORD vkey, ::Microsoft::Terminal::Core::ControlKeyStates modifiers); const COORD _GetTerminalPosition(winrt::Windows::Foundation::Point cursorPosition); const unsigned int _NumberOfClicks(winrt::Windows::Foundation::Point clickPos, Timestamp clickTime); diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index c7304b1af3a..1ff68683325 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -210,18 +210,6 @@ bool Terminal::SendKeyEvent(const WORD vkey, const ControlKeyStates states) _NotifyScrollEvent(); } - // AltGr key combinations don't always contain any meaningful, - // pretranslated unicode character during WM_KEYDOWN. - // E.g. on a German keyboard AltGr+Q should result in a "@" character, - // but actually results in "Q" with Alt and Ctrl modifier states. - // By returning false though, we can abort handling this WM_KEYDOWN - // event and let the WM_CHAR handler kick in, which will be - // provided with an appropriate unicode character. - if (states.IsAltGrPressed()) - { - return false; - } - // Alt key sequences _require_ the char to be in the keyevent. If alt is // pressed, manually get the character that's being typed, and put it in the // KeyEvent. From b495ad255ff5360dfccd91df36fec586e0e8f5d6 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 5 Aug 2019 20:18:40 -0500 Subject: [PATCH 019/154] Create bx.cmd (#2168) * Try createing a script to only build the current working directory Inspired by #2078. I wanted to use this for WindowsTerminal, but I can't generate the resources.pri from just building WindowsTerminal. Maybe @dhowett-msft has some ideas. * Cleanup for PR * fix some bugs with building outside a project directory. * PR nits --- tools/bcx.cmd | 7 ++++ tools/bcz.cmd | 91 +++++++++++++++++++++++++++++++++++++++++++++------ tools/bx.cmd | 7 ++++ tools/bx.ps1 | 28 ++++++++++++++++ tools/bz.cmd | 5 +-- 5 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 tools/bcx.cmd create mode 100644 tools/bx.cmd create mode 100644 tools/bx.ps1 diff --git a/tools/bcx.cmd b/tools/bcx.cmd new file mode 100644 index 00000000000..eeb078977b6 --- /dev/null +++ b/tools/bcx.cmd @@ -0,0 +1,7 @@ +@echo off + +rem bcx - Build only the project in this directory, cleaning it first. +rem This is another script to help Microsoft developers feel at home working on +rem the terminal project. + +call bcz exclusive %* diff --git a/tools/bcz.cmd b/tools/bcz.cmd index 22cfb111508..4167b069cb9 100644 --- a/tools/bcz.cmd +++ b/tools/bcz.cmd @@ -1,13 +1,26 @@ @echo off -rem bcz - Clean and build the project -rem This is another script to help Microsoft developers feel at home working on the openconsole project. +rem bcz - Clean and build the solution. +rem This is another script to help Microsoft developers feel at home working on the Terminal project. + +rem Args: +rem dbg: manually build the solution in the Debug configuration. If omitted, +rem we'll use whatever the last configuration build was. +rem rel: manually build the solution in the Release configuration. If +rem omitted, we'll use whatever the last configuration build was. +rem no_clean: Don't clean before building. This is a much faster build +rem typically, but leaves artifacts from previous builds around, which +rem can lead to unexpected build failures. +rem exclusive: Only build the project in the cwd. If omitted, we'll try +rem building the entire solution instead. if (%_LAST_BUILD_CONF%)==() ( set _LAST_BUILD_CONF=%DEFAULT_CONFIGURATION% ) set _MSBUILD_TARGET=Clean,Build +set _EXCLUSIVE= +set _APPX_ARGS= :ARGS_LOOP if (%1) == () goto :POST_ARGS_LOOP @@ -22,30 +35,43 @@ if (%1) == (rel) ( if (%1) == (no_clean) ( set _MSBUILD_TARGET=Build ) +if (%1) == (exclusive) ( + set _EXCLUSIVE=1 +) shift goto :ARGS_LOOP - :POST_ARGS_LOOP -echo Starting build... -nuget.exe restore %OPENCON%\OpenConsole.sln -rem /p:AppxBundle=Never prevents us from building the appxbundle from the commandline. -rem We don't want to do this from a debug build, because it takes ages, so disable it. -rem if you want the appx, build release +if "%_EXCLUSIVE%" == "1" ( + set "PROJECT_NAME=" + call :get_project +) else if (%_LAST_BUILD_CONF%) == (Debug) ( -set _APPX_ARGS= + rem /p:AppxBundle=Never prevents us from building the appxbundle from the + rem commandline. We don't want to do this from a debug build, because it + rem takes ages, so disable it. if you want the appx, build release + + rem Only do this check if we're doing a full solution build. If we're only + rem trying to build the appx, then we obviously want to build the appx. -if (%_LAST_BUILD_CONF%) == (Debug) ( echo Skipping building appx... set _APPX_ARGS=/p:AppxBundle=false ) else ( echo Building Appx... ) +if "%_EXCLUSIVE%" == "1" ( + if "%PROJECT_NAME%" == "" ( goto :eof ) else echo Building only %PROJECT_NAME% +) + +echo Performing nuget restore... +nuget.exe restore %OPENCON%\OpenConsole.sln + set _BUILD_CMDLINE="%MSBUILD%" %OPENCON%\OpenConsole.sln /t:%_MSBUILD_TARGET% /m /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% %_APPX_ARGS% echo %_BUILD_CMDLINE% +echo Starting build... %_BUILD_CMDLINE% rem Cleanup unused variables here. Note we cannot use setlocal because we need to pass modified @@ -53,3 +79,48 @@ rem _LAST_BUILD_CONF out to OpenCon.cmd later. rem set _MSBUILD_TARGET= set _BIN_=%~dp0\bin\%PLATFORM%\%_LAST_BUILD_CONF% +goto :eof + +rem ############################################################################ +rem The code to figure out what project we're building needs to be in its own +rem function. Otherwise, when cmd evaluates the if statement above `if +rem "%_EXCLUSIVE%" == "1"`, it'll evaluate the entire block with the value of +rem the the variables at the time the if was executed. So instead, make a +rem function here with `enabledelayedexpansion` set. +:get_project +setlocal enabledelayedexpansion + +rem TODO:GH#2172 Find a way to only rebuild the metaproj if the sln changed +rem First generate the metaproj file +set MSBuildEmitSolution=1 +"%msbuild%" %OPENCON%\OpenConsole.sln /t:ValidateSolutionConfiguration /m > NUL +set MSBuildEmitSolution= + +rem Use bx.ps1 to figure out which target we're looking at +set _BX_SCRIPT=powershell bx.ps1 +set _OUTPUT= +FOR /F "tokens=* USEBACKQ" %%F IN (`powershell bx.ps1 2^> NUL`) DO ( + set _OUTPUT=%%F +) +if "!_OUTPUT!" == "" ( + echo Could not find a .vcxproj file in this directory. + echo `bx.cmd` only works in directories with a vcxproj file. + echo Please navigate to directory with a project file in it, or try `bz` to build the entire solution. + goto :eof +) +set "__PROJECT_NAME=!_OUTPUT!" + +rem If we're trying to clean build, make sure to update the target here. +if "%_MSBUILD_TARGET%" == "Build" ( + set __MSBUILD_TARGET=%__PROJECT_NAME% +) else if "%_MSBUILD_TARGET%" == "Clean,Build" ( + set __MSBUILD_TARGET=%__PROJECT_NAME%:Rebuild +) +rem This statement will propogate our internal variables up to the calling +rem scope. Because they're all on one line, the value of our local variables +rem will be evaluated before we endlocal +endlocal & set "PROJECT_NAME=%__PROJECT_NAME%" & set "_MSBUILD_TARGET=%__MSBUILD_TARGET%" +rem ############################################################################ + + +:eof diff --git a/tools/bx.cmd b/tools/bx.cmd new file mode 100644 index 00000000000..798be23c073 --- /dev/null +++ b/tools/bx.cmd @@ -0,0 +1,7 @@ +@echo off + +rem bx - Build only the project in this directory without cleaning it first. +rem This is another script to help Microsoft developers feel at home working on +rem the terminal project. + +call bcz exclusive no_clean %* diff --git a/tools/bx.ps1 b/tools/bx.ps1 new file mode 100644 index 00000000000..4332c7bb643 --- /dev/null +++ b/tools/bx.ps1 @@ -0,0 +1,28 @@ +# This is a helper script to figure out which target corresponds to the project +# in this directory. Parses the solution's .metaproj file looking for the +# project file in this directory, to be able to get the project's name. + +$projects = Get-Childitem -Path .\ -Filter *.vcxproj -File +if ($projects.length -eq 0) +{ + exit -1 +} +$projectPath = $projects.FullName + +$msBuildCondition = "'%(ProjectReference.Identity)' == '$projectPath.metaproj'" + +# Parse the solution's metaproj file. +[xml]$Metaproj = Get-Content "$env:OPENCON\OpenConsole.sln.metaproj" + +$targets = $Metaproj.Project.Target + +# Filter to project targets that match out metaproj file. +# For Conhost\Server, this will match: +# [Conhost\Server, Conhost\Server:Clean, Conhost\Server:Rebuild, Conhost\Server:Publish] +$matchingTargets = $targets | Where-Object { $_.MSBuild.Condition -eq $msBuildCondition } + +# Further filter to the targets that dont have a suffix (like ":Clean") +$matchingTargets = $matchingTargets | Where-Object { $hasProperty = $_.MsBuild.PSobject.Properties.name -match "Targets" ; return -Not $hasProperty } + +Write-Host $matchingTargets.Name +exit 0 diff --git a/tools/bz.cmd b/tools/bz.cmd index f2c3c45f6e0..6f4ecda2186 100644 --- a/tools/bz.cmd +++ b/tools/bz.cmd @@ -1,6 +1,7 @@ @echo off -rem bcz - Build the project without clean it first -rem This is another script to help Microsoft developers feel at home working on the openconsole project. +rem bz - Build the entire solution without cleaning it first. +rem This is another script to help Microsoft developers feel at home working on +rem the terminal project. call bcz no_clean %* From aae938fc337f3c31611d6c597a371912082af29a Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 6 Aug 2019 04:51:50 -0700 Subject: [PATCH 020/154] Attempt to clean up PCHs as we build to leave more Hosted Agent disk space (#2271) * Cleanup PCHs as the build rolls along to leave enough space on CI agents. * Attempt to restrict pch cleanup to only CI agents. * Write message when objects are deleted. --- src/common.build.post.props | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/common.build.post.props b/src/common.build.post.props index 5539b05eed4..b4390287b0e 100644 --- a/src/common.build.post.props +++ b/src/common.build.post.props @@ -30,4 +30,12 @@ $(SolutionDir)\dep\;$(CAExcludePath) + + + + + + + + From a7877558f2b7e8c87d72acfe20e4e009b31a226c Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 6 Aug 2019 09:46:43 -0700 Subject: [PATCH 021/154] add exclusion directories to PR builds, not just rolling builds. (#2272) --- build/pipelines/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/pipelines/ci.yml b/build/pipelines/ci.yml index ed2925b248a..e623f3ab965 100644 --- a/build/pipelines/ci.yml +++ b/build/pipelines/ci.yml @@ -13,6 +13,11 @@ pr: branches: include: - master + paths: + exclude: + - doc/* + - samples/* + - tools/* # 0.0.yyMM.dd## # 0.0.1904.0900 From ff7fdbeab4539c45ef73fedd186ed3be885ac7ca Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 6 Aug 2019 18:24:00 +0100 Subject: [PATCH 022/154] Don't log an error message when _DoGetConsoleInput returns CONSOLE_STATUS_WAIT. (#2244) --- src/host/directio.cpp | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/src/host/directio.cpp b/src/host/directio.cpp index 645b28e2fb6..2eb4ce1b672 100644 --- a/src/host/directio.cpp +++ b/src/host/directio.cpp @@ -273,13 +273,18 @@ void EventsToUnicode(_Inout_ std::deque>& inEvents, { try { - RETURN_NTSTATUS(_DoGetConsoleInput(context, - outEvents, - eventsToRead, - readHandleState, - false, - true, - waiter)); + NTSTATUS Status = _DoGetConsoleInput(context, + outEvents, + eventsToRead, + readHandleState, + false, + true, + waiter); + if (CONSOLE_STATUS_WAIT == Status) + { + return HRESULT_FROM_NT(Status); + } + RETURN_NTSTATUS(Status); } CATCH_RETURN(); } @@ -308,13 +313,18 @@ void EventsToUnicode(_Inout_ std::deque>& inEvents, { try { - RETURN_NTSTATUS(_DoGetConsoleInput(context, - outEvents, - eventsToRead, - readHandleState, - true, - true, - waiter)); + NTSTATUS Status = _DoGetConsoleInput(context, + outEvents, + eventsToRead, + readHandleState, + true, + true, + waiter); + if (CONSOLE_STATUS_WAIT == Status) + { + return HRESULT_FROM_NT(Status); + } + RETURN_NTSTATUS(Status); } CATCH_RETURN(); } @@ -343,13 +353,18 @@ void EventsToUnicode(_Inout_ std::deque>& inEvents, { try { - RETURN_NTSTATUS(_DoGetConsoleInput(context, - outEvents, - eventsToRead, - readHandleState, - false, - false, - waiter)); + NTSTATUS Status = _DoGetConsoleInput(context, + outEvents, + eventsToRead, + readHandleState, + false, + false, + waiter); + if (CONSOLE_STATUS_WAIT == Status) + { + return HRESULT_FROM_NT(Status); + } + RETURN_NTSTATUS(Status); } CATCH_RETURN(); } @@ -378,13 +393,18 @@ void EventsToUnicode(_Inout_ std::deque>& inEvents, { try { - RETURN_NTSTATUS(_DoGetConsoleInput(context, - outEvents, - eventsToRead, - readHandleState, - true, - false, - waiter)); + NTSTATUS Status = _DoGetConsoleInput(context, + outEvents, + eventsToRead, + readHandleState, + true, + false, + waiter); + if (CONSOLE_STATUS_WAIT == Status) + { + return HRESULT_FROM_NT(Status); + } + RETURN_NTSTATUS(Status); } CATCH_RETURN(); } From dfb853644afb3a8830e1380eef4cafef9b71c9f4 Mon Sep 17 00:00:00 2001 From: Yves Dolce <1760825+yves-dolce@users.noreply.github.com> Date: Tue, 6 Aug 2019 11:33:32 -0700 Subject: [PATCH 023/154] use std::move() on a few more strings, other general code tidying (#1899) * - moving string parameter into data member instead of copying it. - removing noexcept from methods where an exception could be raised. If std::terminate() call is desired instead, I guess those should be left and std::move_if_noexcept() used to document the fact that it's on purpose. - std::moving local variable into argument when possible. - change maxversiontested XML element to maxVersionTested. - used of gsl::narrow_cast where appropriate to prevent warnings. - fixed bug in TerminalSettings::SetColorTableEntry() Fixes #1844 --- src/cascadia/TerminalApp/App.cpp | 15 ++++++++------- src/cascadia/TerminalApp/CascadiaSettings.cpp | 2 +- src/cascadia/TerminalApp/ColorScheme.cpp | 3 ++- src/cascadia/TerminalApp/Profile.cpp | 18 ++++++++++-------- src/cascadia/TerminalApp/Profile.h | 2 +- .../TerminalSettings/TerminalSettings.cpp | 3 ++- .../WindowsTerminal/WindowsTerminal.manifest | 2 +- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 65271ea62e3..6c0ab7b673b 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -313,7 +313,8 @@ namespace winrt::TerminalApp::implementation auto keyBindings = _settings->GetKeybindings(); const GUID defaultProfileGuid = _settings->GlobalSettings().GetDefaultProfile(); - for (int profileIndex = 0; profileIndex < _settings->GetProfiles().size(); profileIndex++) + auto const profileCount = gsl::narrow_cast(_settings->GetProfiles().size()); // the number of profiles should not change in the loop for this to work + for (int profileIndex = 0; profileIndex < profileCount; profileIndex++) { const auto& profile = _settings->GetProfiles()[profileIndex]; auto profileMenuItem = Controls::MenuFlyoutItem{}; @@ -794,7 +795,7 @@ namespace winrt::TerminalApp::implementation const auto profiles = _settings->GetProfiles(); // If we don't have that many profiles, then do nothing. - if (realIndex >= profiles.size()) + if (realIndex >= gsl::narrow(profiles.size())) { return; } @@ -1082,7 +1083,7 @@ namespace winrt::TerminalApp::implementation // - Sets focus to the desired tab. void App::_SelectTab(const int tabIndex) { - if (tabIndex >= 0 && tabIndex < _tabs.size()) + if (tabIndex >= 0 && tabIndex < gsl::narrow_cast(_tabs.size())) { _SetFocusedTabIndex(tabIndex); } @@ -1224,12 +1225,12 @@ namespace winrt::TerminalApp::implementation if (tabIndexFromControl == focusedTabIndex) { - if (focusedTabIndex >= _tabs.size()) + auto const tabCount = gsl::narrow_cast(_tabs.size()); + if (focusedTabIndex >= tabCount) { - focusedTabIndex = static_cast(_tabs.size()) - 1; + focusedTabIndex = tabCount - 1; } - - if (focusedTabIndex < 0) + else if (focusedTabIndex < 0) { focusedTabIndex = 0; } diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 9de05c54d4a..2f0602d3475 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -246,7 +246,7 @@ void CascadiaSettings::_CreateDefaultProfiles() if (_isPowerShellCoreInstalled(psCoreCmdline)) { auto pwshProfile{ _CreateDefaultProfile(L"PowerShell Core") }; - pwshProfile.SetCommandline(psCoreCmdline); + pwshProfile.SetCommandline(std::move(psCoreCmdline)); pwshProfile.SetStartingDirectory(DEFAULT_STARTING_DIRECTORY); pwshProfile.SetColorScheme({ L"Campbell" }); diff --git a/src/cascadia/TerminalApp/ColorScheme.cpp b/src/cascadia/TerminalApp/ColorScheme.cpp index cc7ffd3cc68..4fe57f7b00c 100644 --- a/src/cascadia/TerminalApp/ColorScheme.cpp +++ b/src/cascadia/TerminalApp/ColorScheme.cpp @@ -66,7 +66,8 @@ void ColorScheme::ApplyScheme(TerminalSettings terminalSettings) const terminalSettings.DefaultForeground(_defaultForeground); terminalSettings.DefaultBackground(_defaultBackground); - for (int i = 0; i < _table.size(); i++) + auto const tableCount = gsl::narrow_cast(_table.size()); + for (int i = 0; i < tableCount; i++) { terminalSettings.SetColorTableEntry(i, _table[i]); } diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index cf605eef53b..6bb744fd93b 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -150,7 +150,8 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector& TerminalSettings terminalSettings{}; // Fill in the Terminal Setting's CoreSettings from the profile - for (int i = 0; i < _colorTable.size(); i++) + auto const colorTableCount = gsl::narrow_cast(_colorTable.size()); + for (int i = 0; i < colorTableCount; i++) { terminalSettings.SetColorTableEntry(i, _colorTable[i]); } @@ -489,12 +490,12 @@ Profile Profile::FromJson(const Json::Value& json) void Profile::SetFontFace(std::wstring fontFace) noexcept { - _fontFace = fontFace; + _fontFace = std::move(fontFace); } void Profile::SetColorScheme(std::optional schemeName) noexcept { - _schemeName = schemeName; + _schemeName = std::move(schemeName); } void Profile::SetAcrylicOpacity(double opacity) noexcept @@ -504,17 +505,17 @@ void Profile::SetAcrylicOpacity(double opacity) noexcept void Profile::SetCommandline(std::wstring cmdline) noexcept { - _commandline = cmdline; + _commandline = std::move(cmdline); } void Profile::SetStartingDirectory(std::wstring startingDirectory) noexcept { - _startingDirectory = startingDirectory; + _startingDirectory = std::move(startingDirectory); } void Profile::SetName(std::wstring name) noexcept { - _name = name; + _name = std::move(name); } void Profile::SetUseAcrylic(bool useAcrylic) noexcept @@ -553,15 +554,16 @@ bool Profile::HasIcon() const noexcept // - tabTitle: the tab title void Profile::SetTabTitle(std::wstring tabTitle) noexcept { - _tabTitle = tabTitle; + _tabTitle = std::move(tabTitle); } // Method Description: // - Sets this profile's icon path. // Arguments: // - path: the path -void Profile::SetIconPath(std::wstring_view path) noexcept +void Profile::SetIconPath(std::wstring_view path) { + static_assert(!noexcept(_icon.emplace(path))); _icon.emplace(path); } diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 55054a43636..3f96c64b546 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -56,7 +56,7 @@ class TerminalApp::Profile final bool HasIcon() const noexcept; std::wstring_view GetIconPath() const noexcept; - void SetIconPath(std::wstring_view path) noexcept; + void SetIconPath(std::wstring_view path); bool GetCloseOnExit() const noexcept; diff --git a/src/cascadia/TerminalSettings/TerminalSettings.cpp b/src/cascadia/TerminalSettings/TerminalSettings.cpp index 3ac52e87320..9e751e424d2 100644 --- a/src/cascadia/TerminalSettings/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettings/TerminalSettings.cpp @@ -64,7 +64,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation void TerminalSettings::SetColorTableEntry(int32_t index, uint32_t value) { - THROW_HR_IF(E_INVALIDARG, index > _colorTable.size()); + auto const colorTableCount = gsl::narrow_cast(_colorTable.size()); + THROW_HR_IF(E_INVALIDARG, index >= colorTableCount); _colorTable[index] = value; } diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.manifest b/src/cascadia/WindowsTerminal/WindowsTerminal.manifest index ef9516047af..674a264e7c2 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.manifest +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.manifest @@ -9,7 +9,7 @@ - + From 94e5d545aa88e969a70e374d6af5f7e20f623d5c Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 6 Aug 2019 13:16:19 -0700 Subject: [PATCH 024/154] skip a few failing tests for x86 (#2262) --- .../UnitTests_TerminalCore/SelectionTest.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index ea6cb51b6f0..0fd5a433da2 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -181,6 +181,11 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyph_Trailing) { +#ifdef _X86_ + Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); + Log::Result(WEX::Logging::TestResults::Skipped); + return; +#else Terminal term; DummyRenderTarget emptyRT; term.Create({ 100, 100 }, 0, emptyRT); @@ -206,10 +211,16 @@ namespace TerminalCoreUnitTests auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 4, 10, 5, 10 })); +#endif } TEST_METHOD(SelectWideGlyph_Leading) { +#ifdef _X86_ + Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); + Log::Result(WEX::Logging::TestResults::Skipped); + return; +#else Terminal term; DummyRenderTarget emptyRT; term.Create({ 100, 100 }, 0, emptyRT); @@ -235,10 +246,16 @@ namespace TerminalCoreUnitTests auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 4, 10, 5, 10 })); +#endif } TEST_METHOD(SelectWideGlyphsInBoxSelection) { +#ifdef _X86_ + Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); + Log::Result(WEX::Logging::TestResults::Skipped); + return; +#else Terminal term; DummyRenderTarget emptyRT; term.Create({ 100, 100 }, 0, emptyRT); @@ -290,6 +307,7 @@ namespace TerminalCoreUnitTests rowValue++; } +#endif } }; } From 8fa42e09dfc6cd57d29e517a002d8c7a99e2aebd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 6 Aug 2019 15:25:43 -0500 Subject: [PATCH 025/154] Add a note about the build required to the README (#2291) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 686f151538a..fb3cc008b15 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ ## Installation +_(Note: in order to run the Windows Terminal, you'll need to be running at least Windows build 18362 or higher.)_ + ### Microsoft Store Download the Microsoft Terminal free from the Microsoft Store and it'll be continuously updated. Or, feel free to side-load [releases](https://github.com/microsoft/terminal/releases) from GitHub, but note they won't auto-update. From 89925ebe4452f035f0a4661dc318bbdcb2c4d263 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Wed, 7 Aug 2019 10:58:53 -0700 Subject: [PATCH 026/154] inbox: reflect changes from 20h1 branch (#2310) --- src/host/cmdline.h | 6 ++--- src/propslib/RegistrySerialization.hpp | 4 +-- .../parser/ft_fuzzer/fuzzing_directed.h | 26 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/host/cmdline.h b/src/host/cmdline.h index 8dd873aa9d7..6c696c67478 100644 --- a/src/host/cmdline.h +++ b/src/host/cmdline.h @@ -102,9 +102,9 @@ class CommandLine CommandLine(CommandLine const&) = delete; CommandLine& operator=(CommandLine const&) = delete; - [[nodiscard]] NTSTATUS CommandLine::_startCommandListPopup(COOKED_READ_DATA& cookedReadData); - [[nodiscard]] NTSTATUS CommandLine::_startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData); - [[nodiscard]] NTSTATUS CommandLine::_startCopyToCharPopup(COOKED_READ_DATA& cookedReadData); + [[nodiscard]] NTSTATUS _startCommandListPopup(COOKED_READ_DATA& cookedReadData); + [[nodiscard]] NTSTATUS _startCopyFromCharPopup(COOKED_READ_DATA& cookedReadData); + [[nodiscard]] NTSTATUS _startCopyToCharPopup(COOKED_READ_DATA& cookedReadData); void _processHistoryCycling(COOKED_READ_DATA& cookedReadData, const CommandHistory::SearchDirection searchDirection); void _setPromptToOldestCommand(COOKED_READ_DATA& cookedReadData); diff --git a/src/propslib/RegistrySerialization.hpp b/src/propslib/RegistrySerialization.hpp index 0f36c74fc20..80cb219eb4a 100644 --- a/src/propslib/RegistrySerialization.hpp +++ b/src/propslib/RegistrySerialization.hpp @@ -98,10 +98,10 @@ class RegistrySerialization } RegPropertyMap; static const RegPropertyMap s_PropertyMappings[]; - static const size_t RegistrySerialization::s_PropertyMappingsSize; + static const size_t s_PropertyMappingsSize; static const RegPropertyMap s_GlobalPropMappings[]; - static const size_t RegistrySerialization::s_GlobalPropMappingsSize; + static const size_t s_GlobalPropMappingsSize; [[nodiscard]] static NTSTATUS s_LoadRegDword(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings); [[nodiscard]] static NTSTATUS s_LoadRegString(const HKEY hKey, const _RegPropertyMap* const pPropMap, _In_ Settings* const pSettings); diff --git a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h index 5e816fbbd7c..9bad935e803 100644 --- a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h +++ b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h @@ -898,7 +898,7 @@ namespace fuzz _Type operator->() const throw() { - return (m_fFuzzed) ? m_t : m_tInit; + return (this->m_fFuzzed) ? this->m_t : m_tInit; } // This operator makes it possible to invoke the fuzzing map @@ -960,7 +960,7 @@ namespace fuzz __inline virtual _Type** operator&() throw() { m_ftEffectiveTraits |= TRAIT_TRANSFER_ALLOCATION; - return (m_fFuzzed) ? &m_t : &m_tInit; + return (this->m_fFuzzed) ? &(this->m_t) : &m_tInit; } private: @@ -978,11 +978,11 @@ namespace fuzz void OnFuzzedValueFromMap() { m_pszFuzzed = nullptr; - m_ftEffectiveTraits = m_traits; - m_pfnOnFuzzedValueFromMap = [&](_Type* psz, std::function dealloc) { + m_ftEffectiveTraits = this->m_traits; + this->m_pfnOnFuzzedValueFromMap = [&](_Type* psz, std::function dealloc) { FreeFuzzedString(); _Type* pszFuzzed = psz; - if (psz && psz != m_tInit) + if (psz && psz != this->m_tInit) { size_t cb = (sizeof(_Type) == sizeof(char)) ? (strlen(reinterpret_cast(psz)) + 1) * sizeof(char) : @@ -1014,8 +1014,8 @@ namespace fuzz // allocation and deallocation responsibilities. if (m_ftEffectiveTraits & TRAIT_TRANSFER_ALLOCATION) { - _Alloc::Free(m_tInit); - m_tInit = nullptr; + _Alloc::Free(this->m_tInit); + this->m_tInit = nullptr; } else { @@ -1070,11 +1070,11 @@ namespace fuzz protected: __inline virtual _Type GetValueFromMap() { - if (!m_fFuzzed) + if (!this->m_fFuzzed) { - m_t = 0; - m_fFuzzed = TRUE; - for (auto& r : m_map) + this->m_t = 0; + this->m_fFuzzed = TRUE; + for (auto& r : this->m_map) { // Generate a new random value during each map entry // and use it to evaluate if each individual fuzz map @@ -1087,12 +1087,12 @@ namespace fuzz int iLow = iHigh - (r.range.iHigh - r.range.iLow); if (iLow <= wRandom && wRandom < iHigh) { - m_t |= CallFuzzMapFunction(r.fte.pfnFuzz, m_tInit, m_tArgs); + this->m_t |= CallFuzzMapFunction(r.fte.pfnFuzz, this->m_tInit, m_tArgs); } } } - return m_t; + return this->m_t; } }; } From 6c747c565b52b377410b317469efe3ef5e0beb50 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Wed, 7 Aug 2019 16:43:49 -0700 Subject: [PATCH 027/154] Update a number of our dependencies (#2301) Microsoft.VCRTForwarders.140 1.0.0-rc -> 1.0.1-rc Microsoft.Toolkit.Win32.UI.XamlApplication 6.0.0-preview6.* -> 6.0.0-preview7 Microsoft.Windows.CppWinRT 2.0.190605.7 -> 2.0.190730.2 wil fbcd1d2a -> e8c599bc gsl b74b286d -> 1212beae We're skipping the following update: Microsoft.UI.Xaml 2.2.190611001-prerelease -> 2.2.190731001-prerelease --- dep/gsl | 2 +- dep/wil | 2 +- src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj | 4 ++-- src/cascadia/TerminalApp/packages.config | 4 ++-- src/cascadia/TerminalConnection/packages.config | 2 +- src/cascadia/TerminalControl/packages.config | 2 +- src/cascadia/TerminalCore/packages.config | 2 +- src/cascadia/TerminalSettings/packages.config | 2 +- src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj | 12 ++++++------ src/cascadia/WindowsTerminal/packages.config | 7 +++---- src/cppwinrt.build.post.props | 6 +++--- src/cppwinrt.build.pre.props | 2 +- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/dep/gsl b/dep/gsl index b74b286d5e3..1212beae777 160000 --- a/dep/gsl +++ b/dep/gsl @@ -1 +1 @@ -Subproject commit b74b286d5e333561b0f1ef1abd18de2606624455 +Subproject commit 1212beae777dba02c230ece8c0c0ec12790047ea diff --git a/dep/wil b/dep/wil index fbcd1d2abb5..e8c599bca6c 160000 --- a/dep/wil +++ b/dep/wil @@ -1 +1 @@ -Subproject commit fbcd1d2abb558da4564ce343b688f7a658f51318 +Subproject commit e8c599bca6c56c44b6730ad93f6abbc9ecd60fc1 diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj index 08b58cc53e6..2c608df6d6b 100644 --- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj @@ -253,7 +253,7 @@ x86 $(Platform) <_MUXRoot>$(OpenConsoleDir)\packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\ - <_MUXAppRoot>$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview6.2\ + <_MUXAppRoot>$(OpenConsoleDir)\packages\Microsoft.Toolkit.Win32.UI.XamlApplication.6.0.0-preview7\ @@ -283,7 +283,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config index fe0c6e184a5..efa27692017 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -1,6 +1,6 @@  - + - + diff --git a/src/cascadia/TerminalConnection/packages.config b/src/cascadia/TerminalConnection/packages.config index 5e77f0e0f5d..9bc27a32ead 100644 --- a/src/cascadia/TerminalConnection/packages.config +++ b/src/cascadia/TerminalConnection/packages.config @@ -1,5 +1,5 @@  - + diff --git a/src/cascadia/TerminalControl/packages.config b/src/cascadia/TerminalControl/packages.config index 58e49c05c1a..e345a6ccd78 100644 --- a/src/cascadia/TerminalControl/packages.config +++ b/src/cascadia/TerminalControl/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalCore/packages.config b/src/cascadia/TerminalCore/packages.config index 58e49c05c1a..e345a6ccd78 100644 --- a/src/cascadia/TerminalCore/packages.config +++ b/src/cascadia/TerminalCore/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/TerminalSettings/packages.config b/src/cascadia/TerminalSettings/packages.config index 58e49c05c1a..e345a6ccd78 100644 --- a/src/cascadia/TerminalSettings/packages.config +++ b/src/cascadia/TerminalSettings/packages.config @@ -1,4 +1,4 @@  - + diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index 9f7f13b7b0b..f602eaf99a0 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -2,7 +2,7 @@ - + Application @@ -110,17 +110,17 @@ - - - + + + - - + + From 1e4e12507dda70af4c691442659ea0046a9fd98b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 8 Aug 2019 17:02:34 -0500 Subject: [PATCH 028/154] Stop Roaming settings (#2298) * Stop Roaming settings Also migrate existing settings from RoamingState to LocalState. Fixes #1770. * * de-dupe these functions * const a pair of things * This should be in the previous commit * use `unique_hfile`'s * Make some of these wil things cleaner --- src/cascadia/TerminalApp/CascadiaSettings.h | 2 +- .../CascadiaSettingsSerialization.cpp | 86 +++++++++++++++---- 2 files changed, 71 insertions(+), 17 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 8124aa08913..6cb941dfea2 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -47,7 +47,7 @@ class TerminalApp::CascadiaSettings final Json::Value ToJson() const; static std::unique_ptr FromJson(const Json::Value& json); - static std::wstring GetSettingsPath(); + static std::wstring GetSettingsPath(const bool useRoamingPath = false); const Profile* FindProfile(GUID profileGuid) const noexcept; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index ee1d18ab93c..40e81ada29a 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -261,25 +261,77 @@ void CascadiaSettings::_WriteSettings(const std::string_view content) // from reading the file std::optional CascadiaSettings::_ReadSettings() { - auto pathToSettingsFile{ CascadiaSettings::GetSettingsPath() }; - const auto hFile = CreateFileW(pathToSettingsFile.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) + const auto pathToSettingsFile{ CascadiaSettings::GetSettingsPath() }; + wil::unique_hfile hFile{ CreateFileW(pathToSettingsFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr) }; + + if (!hFile) { - // If the file doesn't exist, that's fine. Just log the error and return - // nullopt - we'll create the defaults. - LOG_LAST_ERROR(); - return std::nullopt; + // GH#1770 - Now that we're _not_ roaming our settings, do a quick check + // to see if there's a file in the Roaming App data folder. If there is + // a file there, but not in the LocalAppData, it's likely the user is + // upgrading from a version of the terminal from before this change. + // We'll try moving the file from the Roaming app data folder to the + // local appdata folder. + + const auto pathToRoamingSettingsFile{ CascadiaSettings::GetSettingsPath(true) }; + wil::unique_hfile hRoamingFile{ CreateFileW(pathToRoamingSettingsFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr) }; + + if (hRoamingFile) + { + // Close the file handle, move it, and re-open the file in its new location. + hRoamingFile.reset(); + + // Note: We're unsure if this is unsafe. Theoretically it's possible + // that two instances of the app will try and move the settings file + // simultaneously. We don't know what might happen in that scenario, + // but we're also not sure how to safely lock the file to prevent + // that from ocurring. + THROW_LAST_ERROR_IF(!MoveFile(pathToRoamingSettingsFile.c_str(), + pathToSettingsFile.c_str())); + + hFile.reset(CreateFileW(pathToSettingsFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr)); + + // hFile shouldn't be INVALID. That's unexpected - We just moved the + // file, we should be able to open it. Throw the error so we can get + // some information here. + THROW_LAST_ERROR_IF(!hFile); + } + else + { + // If the roaming file didn't exist, and the local file doesn't exist, + // that's fine. Just log the error and return nullopt - we'll + // create the defaults. + LOG_LAST_ERROR(); + return std::nullopt; + } } // fileSize is in bytes - const auto fileSize = GetFileSize(hFile, nullptr); + const auto fileSize = GetFileSize(hFile.get(), nullptr); THROW_LAST_ERROR_IF(fileSize == INVALID_FILE_SIZE); auto utf8buffer = std::make_unique(fileSize); DWORD bytesRead = 0; - THROW_LAST_ERROR_IF(!ReadFile(hFile, utf8buffer.get(), fileSize, &bytesRead, nullptr)); - CloseHandle(hFile); + THROW_LAST_ERROR_IF(!ReadFile(hFile.get(), utf8buffer.get(), fileSize, &bytesRead, nullptr)); // convert buffer to UTF-8 string std::string utf8string(utf8buffer.get(), fileSize); @@ -289,25 +341,27 @@ std::optional CascadiaSettings::_ReadSettings() // function Description: // - Returns the full path to the settings file, either within the application -// package, or in its unpackaged location. +// package, or in its unpackaged location. This path is under the "Local +// AppData" folder, so it _doesn't_ roam to other machines. // - If the application is unpackaged, -// the file will end up under e.g. C:\Users\admin\AppData\Roaming\Microsoft\Windows Terminal\profiles.json +// the file will end up under e.g. C:\Users\admin\AppData\Local\Microsoft\Windows Terminal\profiles.json // Arguments: // - // Return Value: // - the full path to the settings file -std::wstring CascadiaSettings::GetSettingsPath() +std::wstring CascadiaSettings::GetSettingsPath(const bool useRoamingPath) { - wil::unique_cotaskmem_string roamingAppDataFolder; + wil::unique_cotaskmem_string localAppDataFolder; // KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return // the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests. // Using this flag allows us to avoid Windows.Storage.ApplicationData completely. - if (FAILED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_FORCE_APP_DATA_REDIRECTION, 0, &roamingAppDataFolder))) + const auto knowFolderId = useRoamingPath ? FOLDERID_RoamingAppData : FOLDERID_LocalAppData; + if (FAILED(SHGetKnownFolderPath(knowFolderId, KF_FLAG_FORCE_APP_DATA_REDIRECTION, 0, &localAppDataFolder))) { THROW_LAST_ERROR(); } - std::filesystem::path parentDirectoryForSettingsFile{ roamingAppDataFolder.get() }; + std::filesystem::path parentDirectoryForSettingsFile{ localAppDataFolder.get() }; if (!_IsPackaged()) { From eac29d2c676a8297b360dc327d4c8c259423a59b Mon Sep 17 00:00:00 2001 From: toby Date: Fri, 9 Aug 2019 17:33:01 +0100 Subject: [PATCH 029/154] Add list of keybindings to SettingsSchema.md (#2335) * Add list of keybindings to * Add missed copy bindings --- doc/cascadia/SettingsSchema.md | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index d07d68562c6..b1d17f658ae 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -78,3 +78,56 @@ Properties listed below are specific to each custom key binding. | -------- | ---- | ----------- | ----------- | | `command` | _Required_ | String | The command executed when the associated key bindings are pressed. | | `keys` | _Required_ | Array[String] | Defines the key combinations used to call the command. | + +### Implemented Keybindings + +Bindings listed below are per the implementation in `src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp` + +- copy +- copyTextWithoutNewlines +- paste +- newTab +- duplicateTab +- newTabProfile0 +- newTabProfile1 +- newTabProfile2 +- newTabProfile3 +- newTabProfile4 +- newTabProfile5 +- newTabProfile6 +- newTabProfile7 +- newTabProfile8 +- newWindow +- closeWindow +- closeTab +- closePane +- switchToTab +- nextTab +- prevTab +- increaseFontSize +- decreaseFontSize +- scrollUp +- scrollDown +- scrollUpPage +- scrollDownPage +- switchToTab0 +- switchToTab1 +- switchToTab2 +- switchToTab3 +- switchToTab4 +- switchToTab5 +- switchToTab6 +- switchToTab7 +- switchToTab8 +- openSettings +- splitHorizontal +- splitVertical +- resizePaneLeft +- resizePaneRight +- resizePaneUp +- resizePaneDown +- moveFocusLeft +- moveFocusRight +- moveFocusUp +- moveFocusDown + From 646d8f91b98da1bb8fe331ccc457b3119b9d2903 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 9 Aug 2019 15:21:45 -0500 Subject: [PATCH 030/154] Fix the ut_app build for VS 16.2, 16.3 (#2347) Move the hack from TerminalApp.vcxproj to a .targets file to be used by the ut_app project too. Fixes #2143 --- .../TerminalApp/FixVisualStudioBug.targets | 30 +++++++++++++++++++ src/cascadia/TerminalApp/TerminalApp.vcxproj | 30 ++----------------- .../ut_app/TerminalApp.UnitTests.vcxproj | 4 +++ 3 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 src/cascadia/TerminalApp/FixVisualStudioBug.targets diff --git a/src/cascadia/TerminalApp/FixVisualStudioBug.targets b/src/cascadia/TerminalApp/FixVisualStudioBug.targets new file mode 100644 index 00000000000..465874ad1b9 --- /dev/null +++ b/src/cascadia/TerminalApp/FixVisualStudioBug.targets @@ -0,0 +1,30 @@ + + + + + + <_TerminalAppLibProjectReference Include="@(_ResolvedProjectReferencePaths)" Condition="'%(Filename)' == 'TerminalApp'" /> + <_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" /> + <_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)"> + + + + + + + + <_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" /> + <_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)" /> + + + + diff --git a/src/cascadia/TerminalApp/TerminalApp.vcxproj b/src/cascadia/TerminalApp/TerminalApp.vcxproj index 0cf14d335af..207c8923647 100644 --- a/src/cascadia/TerminalApp/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/TerminalApp.vcxproj @@ -109,32 +109,8 @@ - - - - <_TerminalAppLibProjectReference Include="@(_ResolvedProjectReferencePaths)" Condition="'%(Filename)' == 'TerminalApp'" /> - <_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" /> - <_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)"> - - - - - - - - <_ResolvedProjectReferencePaths Remove="@(_TerminalAppLibProjectReference)" /> - <_ResolvedProjectReferencePaths Include="@(_TerminalAppLibProjectReference)" /> - - - + + diff --git a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj index d535571359b..921bfd72167 100644 --- a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj +++ b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj @@ -122,4 +122,8 @@ + + + From 0843f3cced17626b9ed2eace5f55c394fb4d5c48 Mon Sep 17 00:00:00 2001 From: Pawel Zubrycki Date: Sun, 11 Aug 2019 05:55:17 +0200 Subject: [PATCH 031/154] doc: fix typo reaons -> reasons (#2383) --- doc/user-docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user-docs/index.md b/doc/user-docs/index.md index 6ca50d1804a..5b7b27850d8 100644 --- a/doc/user-docs/index.md +++ b/doc/user-docs/index.md @@ -45,7 +45,7 @@ To choose a different shell (e.g. `cmd.exe` or WSL `bash`) then ## Starting a new PowerShell tab with admin privilege -There is no current plan to support this feature for security reaons. See issue [#623](https://github.com/microsoft/terminal/issues/632) +There is no current plan to support this feature for security reasons. See issue [#623](https://github.com/microsoft/terminal/issues/632) ## Using cut and paste in the Terminal window From 138d3b81c861dfcd5212e04ec75193c8363cff77 Mon Sep 17 00:00:00 2001 From: Mike MacCana Date: Mon, 12 Aug 2019 19:03:04 +0100 Subject: [PATCH 032/154] template: add Powershell command to get OS version (#2403) As `ver` doesn't work. --- .github/ISSUE_TEMPLATE/Bug_Report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.md b/.github/ISSUE_TEMPLATE/Bug_Report.md index 67fe1df9438..580ba8fe308 100644 --- a/.github/ISSUE_TEMPLATE/Bug_Report.md +++ b/.github/ISSUE_TEMPLATE/Bug_Report.md @@ -35,7 +35,7 @@ Please use this form and describe your issue, concisely but precisely, with as m # Environment ```none -Windows build number: [run "ver" at a command prompt] +Windows build number: [run `[Environment]::OSVersion` for powershell, or `ver` for cmd] Windows Terminal version (if applicable): Any other software? From ac97e5d0825d1f8c213de223abef766267a1a68f Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 13 Aug 2019 08:23:28 -0500 Subject: [PATCH 033/154] Add a Local Test binary, to enable local TerminalApp testing (#2294) In #1164 we learned that our CI doesn't support WinRT testing. This made us all sad. Since that merged, we haven't really added any TerminalApp tests, because it's a little too hard. You'd have to uncomment the entire file, and if the list of types changed you'd have to manually update the sxs manifest and appxmanifest. Since that was all insane, I created a new Terminal App unittesting project without those problems. 1. The project is not named *Unit*Test*, so the CI won't run it, but it will run locally. 2. The project will auto-generate its SxS manifest, using the work from #1987. 3. We'll use the SxS manifest from step 2 to generate an AppxManifest for running packaged tests. * This is the start of me trying to enable local unittesting again * We've got a new unittests project that isn't named *unit*test* * We're manually generating the SxS manifest for it. B/C we need to use it at runtime, we need to manually combine it into one manifest file * the runas:UAP thing still doesn't work. We'll investigate. * This shockingly works but I'm still stuck with: ``` Summary of Errors Outside of Tests: Error: TAEF: [HRESULT: 0x80270254] Failed to create the test host process for out of process test execution. (The IApplicationActivationManager::ActivateApplication call failed while using a default host. TAEF's ETW logs which are gathered with the /enableEtwLogging switch should contain events from relevant providers that may help to diagnose the failure.) ``` * Cleaning this all up for review. Frankly just pushing to see if it'll work in CI * Couple things I noticed in the diff from master * Apply @dhowett-msft's suggestions from code review --- OpenConsole.sln | 24 +++ .../LocalTests_TerminalApp/SettingsTests.cpp | 44 +++++ .../LocalTests_TerminalApp/TabTests.cpp | 110 +++++++++++ ...lApp.LocalTests.AppxManifest.prototype.xml | 52 ++++++ .../TerminalApp.LocalTests.manifest | 23 +++ .../TerminalApp.LocalTests.vcxproj | 176 ++++++++++++++++++ .../LocalTests_TerminalApp/precomp.cpp | 4 + src/cascadia/LocalTests_TerminalApp/precomp.h | 53 ++++++ src/cascadia/ut_app/SettingsTests.cpp | 46 ----- src/cascadia/ut_app/TabTests.cpp | 112 ----------- .../ut_app/TerminalApp.UnitTests.vcxproj | 2 - src/cascadia/ut_app/precomp.h | 21 +-- tools/GenerateAppxFromManifest.ps1 | 74 ++++++++ tools/OpenConsole.psm1 | 2 +- tools/runut.cmd | 1 + tools/tests.xml | 1 + 16 files changed, 568 insertions(+), 177 deletions(-) create mode 100644 src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp create mode 100644 src/cascadia/LocalTests_TerminalApp/TabTests.cpp create mode 100644 src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.AppxManifest.prototype.xml create mode 100644 src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest create mode 100644 src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj create mode 100644 src/cascadia/LocalTests_TerminalApp/precomp.cpp create mode 100644 src/cascadia/LocalTests_TerminalApp/precomp.h delete mode 100644 src/cascadia/ut_app/SettingsTests.cpp delete mode 100644 src/cascadia/ut_app/TabTests.cpp create mode 100644 tools/GenerateAppxFromManifest.ps1 diff --git a/OpenConsole.sln b/OpenConsole.sln index efac4b8f7a2..65a12e008e7 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -243,6 +243,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TerminalAppLib", "src\casca {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} = {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "src\cascadia\LocalTests_TerminalApp\TerminalApp.LocalTests.vcxproj", "{CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}" + ProjectSection(ProjectDependencies) = postProject + {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|ARM64 = AuditMode|ARM64 @@ -1087,6 +1092,24 @@ Global {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x64.Build.0 = AuditMode|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x86.Build.0 = AuditMode|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.Build.0 = Debug|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x64.ActiveCfg = Debug|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x64.Build.0 = Debug|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x86.ActiveCfg = Debug|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x86.Build.0 = Debug|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|ARM64.ActiveCfg = Release|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|ARM64.Build.0 = Release|ARM64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.ActiveCfg = Release|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32 + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1147,6 +1170,7 @@ Global {34DE34D3-1CD6-4EE3-8BD9-A26B5B27EC73} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} {CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {59840756-302F-44DF-AA47-441A9D673202} {CA5CAD1A-9A12-429C-B551-8562EC954746} = {59840756-302F-44DF-AA47-441A9D673202} + {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506} = {59840756-302F-44DF-AA47-441A9D673202} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp new file mode 100644 index 00000000000..82bcfbe611f --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "../TerminalApp/ColorScheme.h" + +using namespace Microsoft::Console; +using namespace TerminalApp; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +namespace TerminalAppLocalTests +{ + // Unfortunately, these tests _WILL NOT_ work in our CI, until we have a lab + // machine available that can run Windows version 18362. + + class SettingsTests + { + // Use a custom manifest to ensure that we can activate winrt types from + // our test. This property will tell taef to manually use this as the + // sxs manifest during this test class. It includes all the cppwinrt + // types we've defined, so if your test is crashing for an unknown + // reason, make sure it's included in that file. + // If you want to do anything XAML-y, you'll need to run yor test in a + // packaged context. See TabTests.cpp for more details on that. + BEGIN_TEST_CLASS(SettingsTests) + TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.LocalTests.manifest") + END_TEST_CLASS() + + TEST_METHOD(TryCreateWinRTType); + }; + + void SettingsTests::TryCreateWinRTType() + { + winrt::Microsoft::Terminal::Settings::TerminalSettings settings{}; + VERIFY_IS_NOT_NULL(settings); + auto oldFontSize = settings.FontSize(); + settings.FontSize(oldFontSize + 5); + auto newFontSize = settings.FontSize(); + VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); + } + +} diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp new file mode 100644 index 00000000000..4a1f692139e --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "../TerminalApp/ColorScheme.h" +#include "../TerminalApp/Tab.h" + +using namespace Microsoft::Console; +using namespace TerminalApp; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +namespace TerminalAppLocalTests +{ + // Unfortunately, these tests _WILL NOT_ work in our CI, until we have a lab + // machine available that can run Windows version 18362. + + class TabTests + { + // For this set of tests, we need to activate some XAML content. To do + // that, we need to be able to activate Xaml Islands(XI), using the Xaml + // Hosting APIs. Because XI looks at the manifest of the exe running, we + // can't just use the TerminalApp.Unit.Tests.manifest as our + // ActivationContext. XI is going to inspect `te.exe`s manifest to try + // and find the maxversiontested property, but te.exe hasn't set that. + // Instead, this test will run as a UAP application, as a packaged + // centenial (win32) app. We'll specify our own AppxManifest, so that + // we'll be able to also load all the dll's for the types we've defined + // (and want to use here). This does come with a minor caveat, as + // deploying the appx takes a bit, so use sparingly (though it will + // deploy once per class when used like this.) + BEGIN_TEST_CLASS(TabTests) + TEST_CLASS_PROPERTY(L"RunAs", L"UAP") + TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TerminalApp.LocalTests.AppxManifest.xml") + END_TEST_CLASS() + + // These four tests act as canary tests. If one of them fails, then they + // can help you identify if something much lower in the stack has + // failed. + TEST_METHOD(TryInitXamlIslands); + TEST_METHOD(TryCreateLocalWinRTType); + TEST_METHOD(TryCreateXamlObjects); + TEST_METHOD(TryCreateTab); + + TEST_CLASS_SETUP(ClassSetup) + { + winrt::init_apartment(winrt::apartment_type::single_threaded); + // Initialize the Xaml Hosting Manager + _manager = winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread(); + _source = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource{}; + + return true; + } + + private: + winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _manager{ nullptr }; + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source{ nullptr }; + }; + + void TabTests::TryInitXamlIslands() + { + // Ensures that XAML Islands was initialized correctly + VERIFY_IS_NOT_NULL(_manager); + VERIFY_IS_NOT_NULL(_source); + } + + void TabTests::TryCreateLocalWinRTType() + { + // Verify we can create a WinRT type we authored + // Just creating it is enough to know that everything is working. + winrt::Microsoft::Terminal::Settings::TerminalSettings settings{}; + VERIFY_IS_NOT_NULL(settings); + auto oldFontSize = settings.FontSize(); + settings.FontSize(oldFontSize + 5); + auto newFontSize = settings.FontSize(); + VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); + } + + void TabTests::TryCreateXamlObjects() + { + // Verify we can create a some XAML objects + // Just creating all of them is enough to know that everything is working. + winrt::Windows::UI::Xaml::Controls::UserControl controlRoot; + VERIFY_IS_NOT_NULL(controlRoot); + winrt::Windows::UI::Xaml::Controls::Grid root; + VERIFY_IS_NOT_NULL(root); + winrt::Windows::UI::Xaml::Controls::SwapChainPanel swapChainPanel; + VERIFY_IS_NOT_NULL(swapChainPanel); + winrt::Windows::UI::Xaml::Controls::Primitives::ScrollBar scrollBar; + VERIFY_IS_NOT_NULL(scrollBar); + } + + void TabTests::TryCreateTab() + { + // Just try creating all of: + // 1. one of our pure c++ types (Profile) + // 2. one of our c++winrt types (TermControl) + // 3. one of our types that uses MUX/Xaml (Tab). + // Just creating all of them is enough to know that everything is working. + const auto profileGuid{ Utils::CreateGuid() }; + winrt::Microsoft::Terminal::TerminalControl::TermControl term{}; + VERIFY_IS_NOT_NULL(term); + + auto newTab = std::make_shared(profileGuid, term); + + VERIFY_IS_NOT_NULL(newTab); + } + +} diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.AppxManifest.prototype.xml b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.AppxManifest.prototype.xml new file mode 100644 index 00000000000..c297907488f --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.AppxManifest.prototype.xml @@ -0,0 +1,52 @@ + + + + + + + + TerminalApp.LocalTests.Package Host Process + Microsoft Corp. + taef.png + TAEF Packaged Cwa FullTrust Application Host Process + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest new file mode 100644 index 00000000000..ef9516047af --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + PerMonitorV2 + true + + + diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj new file mode 100644 index 00000000000..554737e67e8 --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + Create + + + + NotUsing + + + + + + + + + + + + + + + + + + {CA5CAD1A-b11c-4ddb-a4fe-c3afae9b5506} + Win32Proj + TerminalAppLocalTests + LocalTests_TerminalApp + TerminalApp.LocalTests + 10.0.18362.0 + 10.0.18362.0 + + + + + + ..;$(OpenConsoleDir)\dep\jsoncpp\json;$(OpenConsoleDir)src\inc;$(OpenConsoleDir)src\inc\test;$(WinRT_IncludePath)\..\cppwinrt\winrt;"$(OpenConsoleDir)\src\cascadia\TerminalApp\lib\Generated Files";%(AdditionalIncludeDirectories) + precomp.h + + + 4702;%(DisableSpecificWarnings) + + + WindowsApp.lib;%(AdditionalDependencies) + + + + + true + true + + + + + + + + + + $(OpenConsoleDir)\bin\$(Platform)\$(Configuration)\$(ProjectName)\ + $(OpenConsoleDir)\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + + + + <_CppWinrtBinRoot>"$(OpenConsoleDir)$(Platform)\$(Configuration)\" + + x86 + $(Platform) + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.2.190611001-prerelease\runtimes\win10-$(Native-Platform)\native\" + + + + + + + + + + + + + $(BeforeLinkTargets); + _LocalTestsGenerateCombinedManifests; + _LocalTestsBuildAppxManifest; + _LocalTestsCopyDependencies; + + + + + + + + + + + + + + + + + + + + <_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true + <_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false + + + + + + + + + + + + + + + + + + + + diff --git a/src/cascadia/LocalTests_TerminalApp/precomp.cpp b/src/cascadia/LocalTests_TerminalApp/precomp.cpp new file mode 100644 index 00000000000..6a6fa8e5af1 --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/precomp.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" \ No newline at end of file diff --git a/src/cascadia/LocalTests_TerminalApp/precomp.h b/src/cascadia/LocalTests_TerminalApp/precomp.h new file mode 100644 index 00000000000..5c19aff512f --- /dev/null +++ b/src/cascadia/LocalTests_TerminalApp/precomp.h @@ -0,0 +1,53 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- precomp.h + +Abstract: +- Contains external headers to include in the precompile phase of console build process. +- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). + +Author(s): +- Carlos Zamora (cazamor) April 2019 +--*/ + +#pragma once + +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "LibraryIncludes.h" +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include +#include +#include "consoletaeftemplates.hpp" + +// Needed just for XamlIslands to work at all: +#include +#include +#include +#include + +// Common includes for most tests: +#include "../../inc/argb.h" +#include "../../inc/conattrs.hpp" +#include "../../types/inc/utils.hpp" +#include "../../inc/DefaultSettings.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include diff --git a/src/cascadia/ut_app/SettingsTests.cpp b/src/cascadia/ut_app/SettingsTests.cpp deleted file mode 100644 index 4f5189ef24e..00000000000 --- a/src/cascadia/ut_app/SettingsTests.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../TerminalApp/ColorScheme.h" - -using namespace Microsoft::Console; -using namespace TerminalApp; -using namespace WEX::Logging; -using namespace WEX::TestExecution; - -namespace TerminalAppUnitTests -{ - // Unfortunately, these tests _WILL NOT_ work in our CI, until we have a lab - // machine available that can run Windows version 18362. Until then, these - // tests will be commented out. GH#1012 should move our CI to that version. - // When that happens, these tests can be re-added. - - // class SettingsTests - // { - // // Use a custom manifest to ensure that we can activate winrt types from - // // our test. This property will tell taef to manually use this as the - // // sxs manifest during this test class. It includes all the cppwinrt - // // types we've defined, so if your test is crashing for an unknown - // // reason, make sure it's included in that file. - // // If you want to do anything XAML-y, you'll need to run yor test in a - // // packaged context. See TabTests.cpp for more details on that. - // BEGIN_TEST_CLASS(SettingsTests) - // TEST_CLASS_PROPERTY(L"ActivationContext", L"TerminalApp.Unit.Tests.manifest") - // END_TEST_CLASS() - - // TEST_METHOD(TryCreateWinRTType); - // }; - - // void SettingsTests::TryCreateWinRTType() - // { - // winrt::Microsoft::Terminal::Settings::TerminalSettings settings{}; - // VERIFY_IS_NOT_NULL(settings); - // auto oldFontSize = settings.FontSize(); - // settings.FontSize(oldFontSize + 5); - // auto newFontSize = settings.FontSize(); - // VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); - // } - -} diff --git a/src/cascadia/ut_app/TabTests.cpp b/src/cascadia/ut_app/TabTests.cpp deleted file mode 100644 index a36e445bb42..00000000000 --- a/src/cascadia/ut_app/TabTests.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../TerminalApp/ColorScheme.h" -#include "../TerminalApp/Tab.h" - -using namespace Microsoft::Console; -using namespace TerminalApp; -using namespace WEX::Logging; -using namespace WEX::TestExecution; - -namespace TerminalAppUnitTests -{ - // Unfortunately, these tests _WILL NOT_ work in our CI, until we have a lab - // machine available that can run Windows version 18362. Until then, these - // tests will be commented out. GH#1012 should move our CI to that version. - // When that happens, these tests can be re-added. - - // class TabTests - // { - // // For this set of tests, we need to activate some XAML content. To do - // // that, we need to be able to activate Xaml Islands(XI), using the Xaml - // // Hosting APIs. Because XI looks at the manifest of the exe running, we - // // can't just use the TerminalApp.Unit.Tests.manifest as our - // // ActivationContext. XI is going to inspect `te.exe`s manifest to try - // // and find the maxversiontested property, but te.exe hasn't set that. - // // Instead, this test will run as a UAP application, as a packaged - // // centenial (win32) app. We'll specify our own AppxManifest, so that - // // we'll be able to also load all the dll's for the types we've defined - // // (and want to use here). This does come with a minor caveat, as - // // deploying the appx takes a bit, so use sparingly (though it will - // // deploy once per class when used like this.) - // BEGIN_TEST_CLASS(TabTests) - // TEST_CLASS_PROPERTY(L"RunAs", L"UAP") - // TEST_CLASS_PROPERTY(L"UAP:AppXManifest", L"TerminalApp.Unit.Tests.AppxManifest.xml") - // END_TEST_CLASS() - - // // These four tests act as canary tests. If one of them fails, then they - // // can help you identify if something much lower in the stack has - // // failed. - // TEST_METHOD(TryInitXamlIslands); - // TEST_METHOD(TryCreateLocalWinRTType); - // TEST_METHOD(TryCreateXamlObjects); - // TEST_METHOD(TryCreateTab); - - // TEST_CLASS_SETUP(ClassSetup) - // { - // winrt::init_apartment(winrt::apartment_type::single_threaded); - // // Initialize the Xaml Hosting Manager - // _manager = winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread(); - // _source = winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource{}; - - // return true; - // } - - // private: - // winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _manager{ nullptr }; - // winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source{ nullptr }; - // }; - - // void TabTests::TryInitXamlIslands() - // { - // // Ensures that XAML Islands was initialized correctly - // VERIFY_IS_NOT_NULL(_manager); - // VERIFY_IS_NOT_NULL(_source); - // } - - // void TabTests::TryCreateLocalWinRTType() - // { - // // Verify we can create a WinRT type we authored - // // Just creating it is enough to know that everything is working. - // winrt::Microsoft::Terminal::Settings::TerminalSettings settings{}; - // VERIFY_IS_NOT_NULL(settings); - // auto oldFontSize = settings.FontSize(); - // settings.FontSize(oldFontSize + 5); - // auto newFontSize = settings.FontSize(); - // VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); - // } - - // void TabTests::TryCreateXamlObjects() - // { - // // Verify we can create a some XAML objects - // // Just creating all of them is enough to know that everything is working. - // winrt::Windows::UI::Xaml::Controls::UserControl controlRoot; - // VERIFY_IS_NOT_NULL(controlRoot); - // winrt::Windows::UI::Xaml::Controls::Grid root; - // VERIFY_IS_NOT_NULL(root); - // winrt::Windows::UI::Xaml::Controls::SwapChainPanel swapChainPanel; - // VERIFY_IS_NOT_NULL(swapChainPanel); - // winrt::Windows::UI::Xaml::Controls::Primitives::ScrollBar scrollBar; - // VERIFY_IS_NOT_NULL(scrollBar); - // } - - // void TabTests::TryCreateTab() - // { - // // Just try creating all of: - // // 1. one of our pure c++ types (Profile) - // // 2. one of our c++winrt types (TermControl) - // // 3. one of our types that uses MUX/Xaml (Tab). - // // Just creating all of them is enough to know that everything is working. - // const auto profileGuid{ Utils::CreateGuid() }; - // winrt::Microsoft::Terminal::TerminalControl::TermControl term{}; - // VERIFY_IS_NOT_NULL(term); - - // auto newTab = std::make_shared(profileGuid, term); - - // VERIFY_IS_NOT_NULL(newTab); - // } - -} diff --git a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj index 921bfd72167..94784d216ee 100644 --- a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj +++ b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj @@ -11,8 +11,6 @@ - - Create diff --git a/src/cascadia/ut_app/precomp.h b/src/cascadia/ut_app/precomp.h index 5c19aff512f..24638220f0f 100644 --- a/src/cascadia/ut_app/precomp.h +++ b/src/cascadia/ut_app/precomp.h @@ -29,25 +29,14 @@ Author(s): #include #include "consoletaeftemplates.hpp" -// Needed just for XamlIslands to work at all: -#include -#include -#include -#include - // Common includes for most tests: #include "../../inc/argb.h" #include "../../inc/conattrs.hpp" #include "../../types/inc/utils.hpp" #include "../../inc/DefaultSettings.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include +// Are you thinking about adding WinRT things here? If so, you probably want to +// add your test to TerminalApp.LocalTests, not TerminalApp.UnitTests. The +// UnitTests run in CI, while the LocalTests do not. However, since the CI can't +// run XAML islands or unpackaged WinRT, any tests using those features will +// need to be added to the LocalTests. diff --git a/tools/GenerateAppxFromManifest.ps1 b/tools/GenerateAppxFromManifest.ps1 new file mode 100644 index 00000000000..b23db70a7af --- /dev/null +++ b/tools/GenerateAppxFromManifest.ps1 @@ -0,0 +1,74 @@ +# This script is used for taking all the activatable classes from a SxS manifest +# and adding them as Extensions to an Appxmanifest.xml. +# Params: +# - SxSManifest: The path to the SxS manifest to get the types from +# - AppxManifestPrototype: The path to an AppxManifest.xml-style XML document to add the Extensions to +# - SxSManifest: The path to write the updated XML doc to. + +param ( + [parameter(Mandatory=$true, Position=0)] + [string]$SxSManifest, + + [parameter(Mandatory=$true, Position=1)] + [string]$AppxManifestPrototype, + + [parameter(Mandatory=$true, Position=2)] + [string]$OutPath +) + +# Load the xml files. +[xml]$manifestData = Get-Content $SxSManifest +[xml]$appxPrototypeData = Get-Content $AppxManifestPrototype + +# You need to make sure each element we add is part of the same namespace as the +# Package, otherwise powershell will append a bunch of `xmlns=""` properties +# that will make the appx deployment reject the manifest. +$rootNS = $appxPrototypeData.Package.NamespaceURI + +# Create an XML element for all the extensions we're adding. +$Extensions = $appxPrototypeData.CreateNode("element", "Extensions", $rootNS) + +$assembly = $manifestData.assembly +$files = $assembly.file +$files | ForEach-Object { + + $Extension = $appxPrototypeData.CreateNode("element", "Extension", $rootNS) + $Extension.SetAttribute("Category", "windows.activatableClass.inProcessServer") + + $InProcessServer = $appxPrototypeData.CreateNode("element", "InProcessServer", $rootNS) + $Path = $appxPrototypeData.CreateNode("element", "Path", $rootNS) + + # You need to stash the result here, otherwise a blank line will be echod to + # the console. + $placeholder = $Path.InnerText = $_.name + + $InProcessServer.AppendChild($Path) + $Extension.AppendChild($InProcessServer) | Out-Null + + foreach($class in $_.activatableClass) { + $ActivatableClass = $appxPrototypeData.CreateNode("element", "ActivatableClass", $rootNS) + $ActivatableClass.SetAttribute("ActivatableClassId", $class.name) + $ActivatableClass.SetAttribute("ThreadingModel", $class.threadingModel) + + $InProcessServer.AppendChild($ActivatableClass) | Out-Null + } + + $Extensions.AppendChild($Extension) | Out-Null + +} + +# Add our fully constructed list of extensions to the original Appxmanifest prototype +$appxPrototypeData.Package.AppendChild($Extensions) | Out-Null + +# Write the modified xml back out. +$appxPrototypeData.save($OutPath) + +# Left as a helper for debugging: +# $StringWriter = New-Object System.IO.StringWriter; +# $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter; +# $XmlWriter.Formatting = "indented"; +# $appxPrototypeData.WriteTo($XmlWriter); +# $XmlWriter.Flush(); +# $StringWriter.Flush(); +# Write-Output $StringWriter.ToString(); + diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index 2ba68c21da1..48d3f1c35dd 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests() [switch]$FTOnly, [parameter(Mandatory=$false)] - [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'types', 'terminalCore', 'terminalApp')] + [ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')] [string]$Test, [parameter(Mandatory=$false)] diff --git a/tools/runut.cmd b/tools/runut.cmd index 419f88b6d13..c4a3961aa95 100644 --- a/tools/runut.cmd +++ b/tools/runut.cmd @@ -12,4 +12,5 @@ call %TAEF% ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\ConAdapter.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^ %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^ + %OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_TerminalApp\TerminalApp.LocalTests.dll ^ %* diff --git a/tools/tests.xml b/tools/tests.xml index 2cf7c3b41f8..930cf5ec34d 100644 --- a/tools/tests.xml +++ b/tools/tests.xml @@ -4,6 +4,7 @@ + From 13d66c994896799da39bb58f7ab78c714312c89c Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 13 Aug 2019 08:28:04 -0500 Subject: [PATCH 034/154] Add info about adding copy/paste keybindings (#2290) * Add info about adding copy/paste keybindings * Update doc/user-docs/UsingJsonSettings.md * Apply suggestions from code review Co-Authored-By: Dustin L. Howett (MSFT) --- doc/user-docs/UsingJsonSettings.md | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/doc/user-docs/UsingJsonSettings.md b/doc/user-docs/UsingJsonSettings.md index 5ad69ca13bc..6d0f78f7fa7 100644 --- a/doc/user-docs/UsingJsonSettings.md +++ b/doc/user-docs/UsingJsonSettings.md @@ -126,3 +126,47 @@ More information about UWP URI schemes [here](https://docs.microsoft.com/en-us/w 1. URL such as `http://open.esa.int/files/2017/03/Mayer_and_Bond_craters_seen_by_SMART-1-350x346.jpg` 2. Local file location such as `C:\Users\Public\Pictures\openlogo.jpg` + +### Adding Copy and Paste Keybindings + +As of [#1093](https://github.com/microsoft/terminal/pull/1093) (first available in Windows Terminal v0.3), the Windows Terminal now +supports copy and paste keyboard shortcuts. However, if you installed and ran +the terminal before that, you won't automatically get the new keybindings added +to your settings. If you'd like to add shortcuts for copy and paste, you can do so by inserting the following objects into your `globals.keybindings` array: + +```json +{ "command": "copy", "keys": ["ctrl+shift+c"] }, +{ "command": "paste", "keys": ["ctrl+shift+v"] } +``` + +This will add copy and paste on ctrl+shift+c +and ctrl+shift+v respectively. + +You can set the keybindings to whatever you'd like. If you prefer +ctrl+c to copy, then set the `keys` to `"ctrl+c"`. + +You can even set multiple keybindings for a single action if you'd like. For example: + +```json + + { + "command" : "paste", + "keys" : + [ + "ctrl+shift+v" + ] + }, + { + "command" : "paste", + "keys" : + [ + "shift+insert" + ] + } +``` + +will bind both ctrl+shift+v and +shift+Insert to `paste`. + +Note: If you set your copy keybinding to `"ctrl+c"`, you won't be able to send an interrupt to the commandline application using Ctrl+C. This is a bug, and being tracked by [#2258](https://github.com/microsoft/terminal/issues/2285). +Additionally, if you set `paste` to `"ctrl+v"`, commandline applications won't be able to read a ctrl+v from the input. For these reasons, we suggest `"ctrl+shift+c"` and `"ctrl+shift+v"` From 8999c661b263a99af4b7987a8c00b77389023a83 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Aug 2019 18:12:14 -0500 Subject: [PATCH 035/154] Only update the icon of a tab it the icon actually _changed_ (#2376) Fixes #1333. Fixes #2329. --- src/cascadia/TerminalApp/App.cpp | 43 ++++++++-------------------- src/cascadia/TerminalApp/Profile.cpp | 15 ++++++---- src/cascadia/TerminalApp/Profile.h | 2 +- src/cascadia/TerminalApp/Tab.cpp | 16 +++++++++++ src/cascadia/TerminalApp/Tab.h | 3 ++ src/cascadia/TerminalApp/Utils.cpp | 30 +++++++++++++++++++ src/cascadia/TerminalApp/Utils.h | 2 ++ 7 files changed, 74 insertions(+), 37 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 6c0ab7b673b..f8e589ee2dc 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -7,6 +7,7 @@ #include "App.g.cpp" #include "TerminalPage.h" +#include "Utils.h" using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::UI::Xaml; @@ -686,16 +687,15 @@ namespace winrt::TerminalApp::implementation if (lastFocusedProfileOpt.has_value()) { const auto lastFocusedProfile = lastFocusedProfileOpt.value(); - - auto tabViewItem = tab->GetTabViewItem(); - tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this, lastFocusedProfile, tabViewItem]() { - // _GetIconFromProfile has to run on the main thread - const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile); - if (matchingProfile) - { - tabViewItem.Icon(App::_GetIconFromProfile(*matchingProfile)); - } - }); + const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile); + if (matchingProfile) + { + tab->UpdateIcon(matchingProfile->GetExpandedIconPath()); + } + else + { + tab->UpdateIcon({}); + } } } @@ -928,7 +928,7 @@ namespace winrt::TerminalApp::implementation // Set this profile's tab to the icon the user specified if (profile != nullptr && profile->HasIcon()) { - tabViewItem.Icon(_GetIconFromProfile(*profile)); + newTab->UpdateIcon(profile->GetExpandedIconPath()); } tabViewItem.PointerPressed({ this, &App::_OnTabClick }); @@ -1250,26 +1250,7 @@ namespace winrt::TerminalApp::implementation // - an IconElement for the profile's icon, if it has one. Controls::IconElement App::_GetIconFromProfile(const Profile& profile) { - if (profile.HasIcon()) - { - std::wstring path{ profile.GetIconPath() }; - const auto envExpandedPath{ wil::ExpandEnvironmentStringsW(path.data()) }; - winrt::hstring iconPath{ envExpandedPath }; - winrt::Windows::Foundation::Uri iconUri{ iconPath }; - Controls::BitmapIconSource iconSource; - // Make sure to set this to false, so we keep the RGB data of the - // image. Otherwise, the icon will be white for all the - // non-transparent pixels in the image. - iconSource.ShowAsMonochrome(false); - iconSource.UriSource(iconUri); - Controls::IconSourceElement elem; - elem.IconSource(iconSource); - return elem; - } - else - { - return { nullptr }; - } + return profile.HasIcon() ? GetColoredIcon(profile.GetExpandedIconPath()) : Controls::IconElement{ nullptr }; } winrt::Microsoft::Terminal::TerminalControl::TermControl App::_GetFocusedControl() diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 6bb744fd93b..21a97aa657c 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -568,14 +568,19 @@ void Profile::SetIconPath(std::wstring_view path) } // Method Description: -// - Returns this profile's icon path, if one is set. Otherwise returns the empty string. +// - Returns this profile's icon path, if one is set. Otherwise returns the +// empty string. This method will expand any environment variables in the +// path, if there are any. // Return Value: // - this profile's icon path, if one is set. Otherwise returns the empty string. -std::wstring_view Profile::GetIconPath() const noexcept +winrt::hstring Profile::GetExpandedIconPath() const { - return HasIcon() ? - std::wstring_view{ _icon.value().c_str(), _icon.value().size() } : - std::wstring_view{ L"", 0 }; + if (!HasIcon()) + { + return { L"" }; + } + winrt::hstring envExpandedPath{ wil::ExpandEnvironmentStringsW(_icon.value().data()) }; + return envExpandedPath; } // Method Description: diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 3f96c64b546..15bd8bfa3ca 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -55,7 +55,7 @@ class TerminalApp::Profile final void SetConnectionType(GUID connectionType) noexcept; bool HasIcon() const noexcept; - std::wstring_view GetIconPath() const noexcept; + winrt::hstring GetExpandedIconPath() const; void SetIconPath(std::wstring_view path); bool GetCloseOnExit() const noexcept; diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index ad8a938c3b1..f8ac1199c36 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "Tab.h" +#include "Utils.h" using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Core; @@ -142,6 +143,21 @@ void Tab::UpdateFocus() _rootPane->UpdateFocus(); } +void Tab::UpdateIcon(const winrt::hstring iconPath) +{ + // Don't reload our icon if it hasn't changed. + if (iconPath == _lastIconPath) + { + return; + } + + _lastIconPath = iconPath; + + _tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + _tabViewItem.Icon(GetColoredIcon(_lastIconPath)); + }); +} + // Method Description: // - Gets the title string of the last focused terminal control in our tree. // Returns the empty string if there is no such control. diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index 7e033ff323f..4de1ca87a26 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -23,6 +23,8 @@ class Tab void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void UpdateFocus(); + void UpdateIcon(const winrt::hstring iconPath); + void ResizeContent(const winrt::Windows::Foundation::Size& newSize); void ResizePane(const winrt::TerminalApp::Direction& direction); void NavigateFocus(const winrt::TerminalApp::Direction& direction); @@ -37,6 +39,7 @@ class Tab private: std::shared_ptr _rootPane{ nullptr }; + winrt::hstring _lastIconPath{}; bool _focused{ false }; winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr }; diff --git a/src/cascadia/TerminalApp/Utils.cpp b/src/cascadia/TerminalApp/Utils.cpp index 6bf6bdc87d4..90b2a587701 100644 --- a/src/cascadia/TerminalApp/Utils.cpp +++ b/src/cascadia/TerminalApp/Utils.cpp @@ -16,3 +16,33 @@ std::wstring GetWstringFromJson(const Json::Value& json) { return winrt::to_hstring(json.asString()).c_str(); } + +// Method Description: +// - Creates an IconElement for the given path. The icon returned is a colored +// icon. If we couldn't create the icon for any reason, we return an empty +// IconElement. +// Arguments: +// - path: the full, expanded path to the icon. +// Return Value: +// - An IconElement with its IconSource set, if possible. +winrt::Windows::UI::Xaml::Controls::IconElement GetColoredIcon(const winrt::hstring& path) +{ + winrt::Windows::UI::Xaml::Controls::IconSourceElement elem{}; + if (!path.empty()) + { + try + { + winrt::Windows::Foundation::Uri iconUri{ path }; + winrt::Windows::UI::Xaml::Controls::BitmapIconSource iconSource; + // Make sure to set this to false, so we keep the RGB data of the + // image. Otherwise, the icon will be white for all the + // non-transparent pixels in the image. + iconSource.ShowAsMonochrome(false); + iconSource.UriSource(iconUri); + elem.IconSource(iconSource); + } + CATCH_LOG(); + } + + return elem; +} diff --git a/src/cascadia/TerminalApp/Utils.h b/src/cascadia/TerminalApp/Utils.h index 4cdfc2f4b85..56e9cb87dd1 100644 --- a/src/cascadia/TerminalApp/Utils.h +++ b/src/cascadia/TerminalApp/Utils.h @@ -28,3 +28,5 @@ inline std::string JsonKey(const std::string_view key) { return static_cast(key); } + +winrt::Windows::UI::Xaml::Controls::IconElement GetColoredIcon(const winrt::hstring& path); From 82de43bce9c44d01380a18df13b86f91a7811dc0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 14 Aug 2019 18:16:38 -0500 Subject: [PATCH 036/154] A better fix for #tab-titles-are-too-long (#2373) ### User Stories: 1. A user wants to be able to use the executable path as their starting title - Does anyone want this? 2. A user wants to be able to set a custom starting title, but have that title be overridable 3. A user wants to be able to set an overridable starting title, different from the profile name - Presumably someone will want this 4. A user totally wants to ignore the VT title and use something else - This will make more sense in the post [#1320] "Support runtime variables in the custom user title" settings ### Solutions: 1. `name`, `startingTitle`, `tabTitle` * a. `name` is only ever used as the profile name. * b. If `startingTitle` isn't set, then the executable path is used * c. If `startingTitle` is set, it's used as the initial title * d. If `tabTitle` is set, it overrides the title from the terminal * e. Current users of `tabTitle` need to manually update to the new behavior. 2. `name` as starting title, `tabTitle` as a different starting title * a. `name` is used as the starting title and the profile name in the dropdown * b. If `tabTitle` is set, we'll use that as the overridable starting title instead. * c. In the future, `dynamicTabTitle` or `tabTitleOverride` could be added to support [#1320] * d. Current users of `tabTitle` automatically get the new (different!) behavior. * e. User Story 1 is impossible - Does anyone want the behavior _ever_? Perhaps making that scenario impossible is good? 3. `name` unchanged, `tabTitle` as the starting title * a. `name` is only ever used as the profile name. * b. If `tabTitle` is set, we'll use that as the overridable starting title. * c. In the future, `dynamicTabTitle` or `tabTitleOverride` could be added to support [#1320] * d. Current users of `tabTitle` automatically get the new (different!) behavior. 4. `name` as starting title, `tabTitle` as different starting title, `suppressApplicationTitle` Boolean to force it to override * a. `name`, `tabTitle` work as in Solution 2. * b. When someone wants to be able to statically totally override that title (story 4), they can use `suppressApplicationTitle` * c. `suppressApplicationTitle` name is WIP * d. We'll add `suppressApplicationTitle` when someone complains * e. If you really want story 1, use `tabTitle: c:\path\to\foo.exe` and `suppressApplicationTitle`. [#1320]: https://github.com/microsoft/terminal/issues/1320 We've decided to pursue path 4. --- doc/cascadia/SettingsSchema.md | 4 +-- src/cascadia/TerminalApp/App.cpp | 30 +++++++------------ src/cascadia/TerminalApp/Profile.cpp | 24 +++------------ src/cascadia/TerminalApp/Profile.h | 2 -- .../TerminalConnection/ConhostConnection.cpp | 17 +++++++++++ .../TerminalConnection/ConhostConnection.h | 3 +- .../TerminalConnection/ConhostConnection.idl | 5 ++-- .../TerminalSettings/IControlSettings.idl | 3 +- .../TerminalSettings/TerminalSettings.cpp | 10 +++++++ .../TerminalSettings/terminalsettings.h | 4 +++ src/inc/conpty-universal.h | 6 +++- src/server/Entrypoints.cpp | 8 +++-- 12 files changed, 65 insertions(+), 51 deletions(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index b1d17f658ae..d72255700bd 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -29,7 +29,7 @@ Properties listed below are specific to each unique profile. | `fontSize` | _Required_ | Integer | `10` | Sets the font size. | | `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. | | `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. | -| `name` | _Required_ | String | `PowerShell Core` | Name of the profile. Displays in the dropdown menu. | +| `name` | _Required_ | String | `PowerShell Core` | Name of the profile. Displays in the dropdown menu.
Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overriden by using `tabTitle`. | | `padding` | _Required_ | String | `0, 0, 0, 0` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. | | `snapOnInput` | _Required_ | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. | | `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. | @@ -44,7 +44,7 @@ Properties listed below are specific to each unique profile. | `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. | | `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. | | `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` | -| `tabTitle` | Optional | String | | Overrides default title of the tab. | +| `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. | ## Schemes Properties listed below are specific to each color scheme. [ColorTool](https://github.com/microsoft/terminal/tree/master/src/tools/ColorTool) is a great tool you can use to create and explore new color schemes. All colors use hex color format. diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index f8e589ee2dc..05120c06b2d 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -708,25 +708,12 @@ namespace winrt::TerminalApp::implementation void App::_UpdateTitle(std::shared_ptr tab) { auto newTabTitle = tab->GetFocusedTitle(); - const auto lastFocusedProfileOpt = tab->GetFocusedProfile(); - if (lastFocusedProfileOpt.has_value()) - { - const auto lastFocusedProfile = lastFocusedProfileOpt.value(); - const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile); - - const auto tabTitle = matchingProfile->GetTabTitle(); + tab->SetTabText(newTabTitle); - // Checks if tab title has been set in the profile settings and - // updates accordingly. - - const auto newActualTitle = tabTitle.empty() ? newTabTitle : tabTitle; - - tab->SetTabText(winrt::to_hstring(newActualTitle.data())); - if (_settings->GlobalSettings().GetShowTitleInTitlebar() && - tab->IsFocused()) - { - _titleChangeHandlers(newActualTitle); - } + if (_settings->GlobalSettings().GetShowTitleInTitlebar() && + tab->IsFocused()) + { + _titleChangeHandlers(newTabTitle); } } @@ -1456,7 +1443,12 @@ namespace winrt::TerminalApp::implementation } else { - connection = TerminalConnection::ConhostConnection(settings.Commandline(), settings.StartingDirectory(), settings.InitialRows(), settings.InitialCols(), winrt::guid()); + connection = TerminalConnection::ConhostConnection(settings.Commandline(), + settings.StartingDirectory(), + settings.StartingTitle(), + settings.InitialRows(), + settings.InitialCols(), + winrt::guid()); } TraceLoggingWrite( diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 21a97aa657c..5cd57f8a855 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -178,6 +178,10 @@ TerminalSettings Profile::CreateTerminalSettings(const std::vector& terminalSettings.StartingDirectory(winrt::to_hstring(evaluatedDirectory.c_str())); } + // GH#2373: Use the tabTitle as the starting title if it exists, otherwise + // use the profile name + terminalSettings.StartingTitle(_tabTitle ? _tabTitle.value() : _name); + if (_schemeName) { const ColorScheme* const matchingScheme = _FindScheme(schemes, _schemeName.value()); @@ -594,26 +598,6 @@ std::wstring_view Profile::GetName() const noexcept return _name; } -// Method Description: -// - Returns true if profile's custom tab title is set, if one is set. Otherwise returns false. -// Return Value: -// - true if this profile's custom tab title is set. Otherwise returns false. -bool Profile::HasTabTitle() const noexcept -{ - return _tabTitle.has_value(); -} - -// Method Description: -// - Returns the custom tab title, if one is set. Otherwise returns the empty string. -// Return Value: -// - this profile's custom tab title, if one is set. Otherwise returns the empty string. -std::wstring_view Profile::GetTabTitle() const noexcept -{ - return HasTabTitle() ? - std::wstring_view{ _tabTitle.value().c_str(), _tabTitle.value().size() } : - std::wstring_view{ L"", 0 }; -} - bool Profile::HasConnectionType() const noexcept { return _connectionType.has_value(); diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 15bd8bfa3ca..7b1627ab08a 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -36,8 +36,6 @@ class TerminalApp::Profile final GUID GetGuid() const noexcept; std::wstring_view GetName() const noexcept; - bool HasTabTitle() const noexcept; - std::wstring_view GetTabTitle() const noexcept; bool HasConnectionType() const noexcept; GUID GetConnectionType() const noexcept; diff --git a/src/cascadia/TerminalConnection/ConhostConnection.cpp b/src/cascadia/TerminalConnection/ConhostConnection.cpp index 90709bae40d..a5bea4f608f 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.cpp +++ b/src/cascadia/TerminalConnection/ConhostConnection.cpp @@ -18,6 +18,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { ConhostConnection::ConhostConnection(const hstring& commandline, const hstring& startingDirectory, + const hstring& startingTitle, const uint32_t initialRows, const uint32_t initialCols, const guid& initialGuid) : @@ -25,6 +26,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _initialCols{ initialCols }, _commandline{ commandline }, _startingDirectory{ startingDirectory }, + _startingTitle{ startingTitle }, _guid{ initialGuid } { if (_guid == guid{}) @@ -90,6 +92,20 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation THROW_LAST_ERROR_IF_NULL(_hOutputThread); + STARTUPINFO si = { 0 }; + si.cb = sizeof(STARTUPINFOW); + + // If we have a startingTitle, create a mutable character buffer to add + // it to the STARTUPINFO. + std::unique_ptr mutableTitle{ nullptr }; + if (!_startingTitle.empty()) + { + mutableTitle = std::make_unique(_startingTitle.size() + 1); + THROW_IF_NULL_ALLOC(mutableTitle); + THROW_IF_FAILED(StringCchCopy(mutableTitle.get(), _startingTitle.size() + 1, _startingTitle.c_str())); + si.lpTitle = mutableTitle.get(); + } + THROW_IF_FAILED( CreateConPty(cmdline, startingDirectory, @@ -100,6 +116,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation &_signalPipe, &_piConhost, 0, + si, extraEnvVars)); _connected = true; diff --git a/src/cascadia/TerminalConnection/ConhostConnection.h b/src/cascadia/TerminalConnection/ConhostConnection.h index e0f9bf82198..dc5893f0094 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.h +++ b/src/cascadia/TerminalConnection/ConhostConnection.h @@ -9,7 +9,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { struct ConhostConnection : ConhostConnectionT { - ConhostConnection(const hstring& cmdline, const hstring& startingDirectory, const uint32_t rows, const uint32_t cols, const guid& guid); + ConhostConnection(const hstring& cmdline, const hstring& startingDirectory, const hstring& startingTitle, const uint32_t rows, const uint32_t cols, const guid& guid); winrt::event_token TerminalOutput(TerminalConnection::TerminalOutputEventArgs const& handler); void TerminalOutput(winrt::event_token const& token) noexcept; @@ -30,6 +30,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation uint32_t _initialCols{}; hstring _commandline; hstring _startingDirectory; + hstring _startingTitle; guid _guid{}; // A unique session identifier for connected client bool _connected{}; diff --git a/src/cascadia/TerminalConnection/ConhostConnection.idl b/src/cascadia/TerminalConnection/ConhostConnection.idl index 00841cb94a5..50550c19030 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.idl +++ b/src/cascadia/TerminalConnection/ConhostConnection.idl @@ -5,10 +5,9 @@ import "ITerminalConnection.idl"; namespace Microsoft.Terminal.TerminalConnection { - [default_interface] - runtimeclass ConhostConnection : ITerminalConnection + [default_interface] runtimeclass ConhostConnection : ITerminalConnection { - ConhostConnection(String cmdline, String startingDirectory, UInt32 rows, UInt32 columns, Guid guid); + ConhostConnection(String cmdline, String startingDirectory, String startingTitle, UInt32 rows, UInt32 columns, Guid guid); Guid Guid { get; }; }; diff --git a/src/cascadia/TerminalSettings/IControlSettings.idl b/src/cascadia/TerminalSettings/IControlSettings.idl index e706f82b183..69c47072291 100644 --- a/src/cascadia/TerminalSettings/IControlSettings.idl +++ b/src/cascadia/TerminalSettings/IControlSettings.idl @@ -22,7 +22,7 @@ namespace Microsoft.Terminal.Settings Boolean UseAcrylic; Boolean CloseOnExit; Double TintOpacity; - ScrollbarState ScrollState; + ScrollbarState ScrollState; String FontFace; Int32 FontSize; @@ -32,6 +32,7 @@ namespace Microsoft.Terminal.Settings String Commandline; String StartingDirectory; + String StartingTitle; String EnvironmentVariables; String BackgroundImage; diff --git a/src/cascadia/TerminalSettings/TerminalSettings.cpp b/src/cascadia/TerminalSettings/TerminalSettings.cpp index 9e751e424d2..f36ff913b97 100644 --- a/src/cascadia/TerminalSettings/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettings/TerminalSettings.cpp @@ -289,6 +289,16 @@ namespace winrt::Microsoft::Terminal::Settings::implementation _startingDir = value; } + hstring TerminalSettings::StartingTitle() + { + return _startingTitle; + } + + void TerminalSettings::StartingTitle(hstring const& value) + { + _startingTitle = value; + } + hstring TerminalSettings::EnvironmentVariables() { return _envVars; diff --git a/src/cascadia/TerminalSettings/terminalsettings.h b/src/cascadia/TerminalSettings/terminalsettings.h index 39894c6674f..8979e546c0a 100644 --- a/src/cascadia/TerminalSettings/terminalsettings.h +++ b/src/cascadia/TerminalSettings/terminalsettings.h @@ -83,6 +83,9 @@ namespace winrt::Microsoft::Terminal::Settings::implementation hstring StartingDirectory(); void StartingDirectory(hstring const& value); + hstring StartingTitle(); + void StartingTitle(hstring const& value); + hstring EnvironmentVariables(); void EnvironmentVariables(hstring const& value); @@ -115,6 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation winrt::Windows::UI::Xaml::VerticalAlignment _backgroundImageVerticalAlignment; hstring _commandline; hstring _startingDir; + hstring _startingTitle; hstring _envVars; Settings::IKeyBindings _keyBindings; Settings::ScrollbarState _scrollbarState; diff --git a/src/inc/conpty-universal.h b/src/inc/conpty-universal.h index 5d6031fd79b..9fc8742d771 100644 --- a/src/inc/conpty-universal.h +++ b/src/inc/conpty-universal.h @@ -206,6 +206,9 @@ bool SignalResizeWindow(const HANDLE hSignal, // - hSignal: A handle to the pipe for writing signal messages to the pty. // - piPty: The PROCESS_INFORMATION of the pty process. NOTE: This is *not* the // PROCESS_INFORMATION of the process that's created as a result the cmdline. +// - startupInfo : A STARTUPINFO struct to use as additional information to pass +// into the created conhost process. Conhost may pass some of the properties +// in this struct to its child process, notably, the lpTitle. // - extraEnvVars : A map of pairs of (Name, Value) representing additional // environment variable strings and values to be set in the client process // environment. May override any already present in parent process. @@ -221,6 +224,7 @@ bool SignalResizeWindow(const HANDLE hSignal, HANDLE* const hSignal, PROCESS_INFORMATION* const piPty, DWORD dwCreationFlags = 0, + const STARTUPINFO startupInfo = { 0 }, const EnvironmentVariableMapW& extraEnvVars = {}) noexcept { // Create some anon pipes so we can pass handles down and into the console. @@ -268,7 +272,7 @@ bool SignalResizeWindow(const HANDLE hSignal, conhostCmdline += L" -- "; conhostCmdline += cmdline; - STARTUPINFO si = { 0 }; + STARTUPINFO si = startupInfo; si.cb = sizeof(STARTUPINFOW); si.hStdInput = inPipeConhostSide; si.hStdOutput = outPipeConhostSide; diff --git a/src/server/Entrypoints.cpp b/src/server/Entrypoints.cpp index cc77e2f3e78..7eb941238b1 100644 --- a/src/server/Entrypoints.cpp +++ b/src/server/Entrypoints.cpp @@ -86,10 +86,14 @@ HostStartupInfo.cb = sizeof(STARTUPINFO); GetStartupInfoW(&HostStartupInfo); - // If we were started with Title is Link Name, then pass the flag and the link name down to the child. + // Pass the title we were started with down to our child process. + // Conhost itself absolutely doesn't care about this value, but the + // child might. + StartupInformation.StartupInfo.lpTitle = HostStartupInfo.lpTitle; + // If we were started with Title is Link Name, then pass the flag + // down to the child. (the link name was already passed down above) if (WI_IsFlagSet(HostStartupInfo.dwFlags, STARTF_TITLEISLINKNAME)) { - StartupInformation.StartupInfo.lpTitle = HostStartupInfo.lpTitle; StartupInformation.StartupInfo.dwFlags |= STARTF_TITLEISLINKNAME; } } From 1f41fd35cf98281fa8c4476f70445fec3c9ccda4 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 14 Aug 2019 16:41:43 -0700 Subject: [PATCH 037/154] Chunk Selection Expansion for Double/Triple Click Selection (#2184) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Double/Triple click create a selection expanding beyond one cell. This PR makes it so that when you're dragging your mouse to expand the selection, you expand to the next delimiter defined by double/triple click. So, double click expands by doubleClickDelimiter ranges. Triple click expands by line. When you double/triple click, a word/line is selected. When you drag, that word/line will remain selected after the expansion occurs. Closes #1933 ## Details Rather than resizing the selection when the mouse event occurs, I figured I'd do what I did with wide glyph selection: expand at render time. We needed an enum `multiClickSelectionMode` to keep track of which expansion mode we're in. Minor modifications to `_ExpandDoubleClickSelection*(COORD)` had to be made so that we can re-use them. Actual expansion occurs in `_GetSelectionRects()` ## Validation Steps Performed - generic double click test - `dir` or `ls` - double click a word - drag up - Works! ✔ - double click on delimiter test - `dir` or `ls` - double click a word delimiter (i.e.: space between words) - drag up - Works! ✔ - generic triple click test - `dir` or `ls` - triple click a line - drag up - Works! ✔ - ALT + double click test - `dir` or `ls` - hold ALT - double click a word - drag up - Works! ✔ repeat above tests in following scenarios: - when at top of scrollback - drag down instead of up --- src/cascadia/TerminalCore/Terminal.hpp | 14 +- .../TerminalCore/TerminalSelection.cpp | 78 ++++--- .../UnitTests_TerminalCore/MockTermSettings.h | 59 +++++ .../ScreenSizeLimitsTest.cpp | 50 +---- .../UnitTests_TerminalCore/SelectionTest.cpp | 203 +++++++++++++++++- .../UnitTests_TerminalCore/UnitTests.vcxproj | 1 + 6 files changed, 320 insertions(+), 85 deletions(-) create mode 100644 src/cascadia/UnitTests_TerminalCore/MockTermSettings.h diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 157f0155ca9..81b397ebb35 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -164,7 +164,13 @@ class Microsoft::Terminal::Core::Terminal final : bool _snapOnInput; - // Text Selection +#pragma region Text Selection + enum SelectionExpansionMode + { + Cell, + Word, + Line + }; COORD _selectionAnchor; COORD _endSelectionPosition; bool _boxSelection; @@ -172,6 +178,8 @@ class Microsoft::Terminal::Core::Terminal final : SHORT _selectionAnchor_YOffset; SHORT _endSelectionPosition_YOffset; std::wstring _wordDelimiters; + SelectionExpansionMode _multiClickSelectionMode; +#pragma endregion std::shared_mutex _readWriteLock; @@ -214,8 +222,8 @@ class Microsoft::Terminal::Core::Terminal final : std::vector _GetSelectionRects() const; const SHORT _ExpandWideGlyphSelectionLeft(const SHORT xPos, const SHORT yPos) const; const SHORT _ExpandWideGlyphSelectionRight(const SHORT xPos, const SHORT yPos) const; - void _ExpandDoubleClickSelectionLeft(const COORD position); - void _ExpandDoubleClickSelectionRight(const COORD position); + COORD _ExpandDoubleClickSelectionLeft(const COORD position) const; + COORD _ExpandDoubleClickSelectionRight(const COORD position) const; const bool _isWordDelimiter(std::wstring_view cellChar) const; const COORD _ConvertToBufferCell(const COORD viewportPos) const; #pragma endregion diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 4d968f8f72f..33b0faf13b5 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -63,6 +63,27 @@ std::vector Terminal::_GetSelectionRects() const selectionRow.Right = (row == lowerCoord.Y) ? lowerCoord.X : bufferSize.RightInclusive(); } + // expand selection for Double/Triple Click + if (_multiClickSelectionMode == SelectionExpansionMode::Word) + { + const auto cellChar = _buffer->GetCellDataAt(selectionAnchorWithOffset)->Chars(); + if (_selectionAnchor == _endSelectionPosition && _isWordDelimiter(cellChar)) + { + // only highlight the cell if you double click a delimiter + } + else + { + selectionRow.Left = _ExpandDoubleClickSelectionLeft({ selectionRow.Left, row }).X; + selectionRow.Right = _ExpandDoubleClickSelectionRight({ selectionRow.Right, row }).X; + } + } + else if (_multiClickSelectionMode == SelectionExpansionMode::Line) + { + selectionRow.Left = 0; + selectionRow.Right = bufferSize.RightInclusive(); + } + + // expand selection for Wide Glyphs selectionRow.Left = _ExpandWideGlyphSelectionLeft(selectionRow.Left, row); selectionRow.Right = _ExpandWideGlyphSelectionRight(selectionRow.Right, row); @@ -142,16 +163,24 @@ void Terminal::DoubleClickSelection(const COORD position) if (_isWordDelimiter(cellChar)) { SetSelectionAnchor(position); + _multiClickSelectionMode = SelectionExpansionMode::Word; return; } // scan leftwards until delimiter is found and // set selection anchor to one right of that spot - _ExpandDoubleClickSelectionLeft(position); + _selectionAnchor = _ExpandDoubleClickSelectionLeft(positionWithOffsets); + THROW_IF_FAILED(ShortSub(_selectionAnchor.Y, gsl::narrow(_ViewStartIndex()), &_selectionAnchor.Y)); + _selectionAnchor_YOffset = gsl::narrow(_ViewStartIndex()); // scan rightwards until delimiter is found and // set endSelectionPosition to one left of that spot - _ExpandDoubleClickSelectionRight(position); + _endSelectionPosition = _ExpandDoubleClickSelectionRight(positionWithOffsets); + THROW_IF_FAILED(ShortSub(_endSelectionPosition.Y, gsl::narrow(_ViewStartIndex()), &_endSelectionPosition.Y)); + _endSelectionPosition_YOffset = gsl::narrow(_ViewStartIndex()); + + _selectionActive = true; + _multiClickSelectionMode = SelectionExpansionMode::Word; } // Method Description: @@ -162,6 +191,8 @@ void Terminal::TripleClickSelection(const COORD position) { SetSelectionAnchor({ 0, position.Y }); SetEndSelectionPosition({ _buffer->GetSize().RightInclusive(), position.Y }); + + _multiClickSelectionMode = SelectionExpansionMode::Line; } // Method Description: @@ -181,6 +212,8 @@ void Terminal::SetSelectionAnchor(const COORD position) _selectionActive = true; SetEndSelectionPosition(position); + + _multiClickSelectionMode = SelectionExpansionMode::Cell; } // Method Description: @@ -253,16 +286,10 @@ const std::wstring Terminal::RetrieveSelectedTextFromBuffer(bool trimTrailingWhi // Arguments: // - position: viewport coordinate for selection // Return Value: -// - update _selectionAnchor to new expanded location -void Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) +// - updated copy of "position" to new expanded location (with vertical offset) +COORD Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) const { - // don't change the value if at/outside the boundary - if (position.X <= 0 || position.X >= _buffer->GetSize().RightInclusive()) - { - return; - } - - COORD positionWithOffsets = _ConvertToBufferCell(position); + COORD positionWithOffsets = position; const auto bufferViewport = _buffer->GetSize(); auto cellChar = _buffer->GetCellDataAt(positionWithOffsets)->Chars(); while (positionWithOffsets.X != 0 && !_isWordDelimiter(cellChar)) @@ -271,16 +298,13 @@ void Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) cellChar = _buffer->GetCellDataAt(positionWithOffsets)->Chars(); } - if (positionWithOffsets.X != 0 || _isWordDelimiter(cellChar)) + if (positionWithOffsets.X != 0 && _isWordDelimiter(cellChar)) { // move off of delimiter to highlight properly bufferViewport.IncrementInBounds(positionWithOffsets); } - THROW_IF_FAILED(ShortSub(positionWithOffsets.Y, gsl::narrow(_ViewStartIndex()), &positionWithOffsets.Y)); - _selectionAnchor = positionWithOffsets; - _selectionAnchor_YOffset = gsl::narrow(_ViewStartIndex()); - _selectionActive = true; + return positionWithOffsets; } // Method Description: @@ -288,16 +312,10 @@ void Terminal::_ExpandDoubleClickSelectionLeft(const COORD position) // Arguments: // - position: viewport coordinate for selection // Return Value: -// - update _endSelectionPosition to new expanded location -void Terminal::_ExpandDoubleClickSelectionRight(const COORD position) +// - updated copy of "position" to new expanded location (with vertical offset) +COORD Terminal::_ExpandDoubleClickSelectionRight(const COORD position) const { - // don't change the value if at/outside the boundary - if (position.X <= 0 || position.X >= _buffer->GetSize().RightInclusive()) - { - return; - } - - COORD positionWithOffsets = _ConvertToBufferCell(position); + COORD positionWithOffsets = position; const auto bufferViewport = _buffer->GetSize(); auto cellChar = _buffer->GetCellDataAt(positionWithOffsets)->Chars(); while (positionWithOffsets.X != _buffer->GetSize().RightInclusive() && !_isWordDelimiter(cellChar)) @@ -306,15 +324,13 @@ void Terminal::_ExpandDoubleClickSelectionRight(const COORD position) cellChar = _buffer->GetCellDataAt(positionWithOffsets)->Chars(); } - if (positionWithOffsets.X != bufferViewport.RightInclusive() || _isWordDelimiter(cellChar)) + if (positionWithOffsets.X != bufferViewport.RightInclusive() && _isWordDelimiter(cellChar)) { // move off of delimiter to highlight properly bufferViewport.DecrementInBounds(positionWithOffsets); } - THROW_IF_FAILED(ShortSub(positionWithOffsets.Y, gsl::narrow(_ViewStartIndex()), &positionWithOffsets.Y)); - _endSelectionPosition = positionWithOffsets; - _endSelectionPosition_YOffset = gsl::narrow(_ViewStartIndex()); + return positionWithOffsets; } // Method Description: @@ -336,7 +352,11 @@ const bool Terminal::_isWordDelimiter(std::wstring_view cellChar) const // - the corresponding location on the buffer const COORD Terminal::_ConvertToBufferCell(const COORD viewportPos) const { + // Force position to be valid COORD positionWithOffsets = viewportPos; + positionWithOffsets.X = std::clamp(viewportPos.X, static_cast(0), _buffer->GetSize().RightInclusive()); + positionWithOffsets.Y = std::clamp(viewportPos.Y, static_cast(0), _buffer->GetSize().BottomInclusive()); + THROW_IF_FAILED(ShortSub(viewportPos.Y, gsl::narrow(_scrollOffset), &positionWithOffsets.Y)); THROW_IF_FAILED(ShortAdd(positionWithOffsets.Y, gsl::narrow(_ViewStartIndex()), &positionWithOffsets.Y)); return positionWithOffsets; diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h new file mode 100644 index 00000000000..3024f4a34d7 --- /dev/null +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -0,0 +1,59 @@ +#pragma once + +#include "precomp.h" +#include + +#include "DefaultSettings.h" + +#include "winrt/Microsoft.Terminal.Settings.h" + +using namespace winrt::Microsoft::Terminal::Settings; + +namespace TerminalCoreUnitTests +{ + class MockTermSettings : public winrt::implements + { + public: + MockTermSettings(int32_t historySize, int32_t initialRows, int32_t initialCols) : + _historySize(historySize), + _initialRows(initialRows), + _initialCols(initialCols) + { + } + + // property getters - all implemented + int32_t HistorySize() { return _historySize; } + int32_t InitialRows() { return _initialRows; } + int32_t InitialCols() { return _initialCols; } + uint32_t DefaultForeground() { return COLOR_WHITE; } + uint32_t DefaultBackground() { return COLOR_BLACK; } + bool SnapOnInput() { return false; } + uint32_t CursorColor() { return COLOR_WHITE; } + CursorStyle CursorShape() const noexcept { return CursorStyle::Vintage; } + uint32_t CursorHeight() { return 42UL; } + winrt::hstring WordDelimiters() { return winrt::to_hstring(DEFAULT_WORD_DELIMITERS.c_str()); } + + // other implemented methods + uint32_t GetColorTableEntry(int32_t) const { return 123; } + + // property setters - all unimplemented + void HistorySize(int32_t) {} + void InitialRows(int32_t) {} + void InitialCols(int32_t) {} + void DefaultForeground(uint32_t) {} + void DefaultBackground(uint32_t) {} + void SnapOnInput(bool) {} + void CursorColor(uint32_t) {} + void CursorShape(CursorStyle const&) noexcept {} + void CursorHeight(uint32_t) {} + void WordDelimiters(winrt::hstring) {} + + // other unimplemented methods + void SetColorTableEntry(int32_t /* index */, uint32_t /* value */) {} + + private: + int32_t _historySize; + int32_t _initialRows; + int32_t _initialCols; + }; +} diff --git a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp index bdaa57012b8..9c3e8e602fa 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp @@ -4,64 +4,16 @@ #include "precomp.h" #include -#include "DefaultSettings.h" #include "../cascadia/TerminalCore/Terminal.hpp" +#include "MockTermSettings.h" #include "../renderer/inc/DummyRenderTarget.hpp" #include "consoletaeftemplates.hpp" -#include "winrt/Microsoft.Terminal.Settings.h" - using namespace winrt::Microsoft::Terminal::Settings; using namespace Microsoft::Terminal::Core; namespace TerminalCoreUnitTests { - class MockTermSettings : public winrt::implements - { - public: - MockTermSettings(int32_t historySize, int32_t initialRows, int32_t initialCols) : - _historySize(historySize), - _initialRows(initialRows), - _initialCols(initialCols) - { - } - - // property getters - all implemented - int32_t HistorySize() { return _historySize; } - int32_t InitialRows() { return _initialRows; } - int32_t InitialCols() { return _initialCols; } - uint32_t DefaultForeground() { return COLOR_WHITE; } - uint32_t DefaultBackground() { return COLOR_BLACK; } - bool SnapOnInput() { return false; } - uint32_t CursorColor() { return COLOR_WHITE; } - CursorStyle CursorShape() const noexcept { return CursorStyle::Vintage; } - uint32_t CursorHeight() { return 42UL; } - winrt::hstring WordDelimiters() { return winrt::to_hstring(DEFAULT_WORD_DELIMITERS.c_str()); } - - // other implemented methods - uint32_t GetColorTableEntry(int32_t) const { return 123; } - - // property setters - all unimplemented - void HistorySize(int32_t) {} - void InitialRows(int32_t) {} - void InitialCols(int32_t) {} - void DefaultForeground(uint32_t) {} - void DefaultBackground(uint32_t) {} - void SnapOnInput(bool) {} - void CursorColor(uint32_t) {} - void CursorShape(CursorStyle const&) noexcept {} - void CursorHeight(uint32_t) {} - void WordDelimiters(winrt::hstring) {} - - // other unimplemented methods - void SetColorTableEntry(int32_t /* index */, uint32_t /* value */) {} - - private: - int32_t _historySize; - int32_t _initialRows; - int32_t _initialCols; - }; - #define WCS(x) WCSHELPER(x) #define WCSHELPER(x) L#x diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 0fd5a433da2..01465aa3d3a 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -9,6 +9,7 @@ #include #include "../cascadia/TerminalCore/Terminal.hpp" +#include "../cascadia/UnitTests_TerminalCore/MockTermSettings.h" #include "../renderer/inc/DummyRenderTarget.hpp" #include "consoletaeftemplates.hpp" @@ -16,7 +17,7 @@ using namespace WEX::Logging; using namespace WEX::TestExecution; using namespace Microsoft::Terminal::Core; -using namespace Microsoft::Console::Render; +using namespace winrt::Microsoft::Terminal::Settings; namespace TerminalCoreUnitTests { @@ -183,7 +184,7 @@ namespace TerminalCoreUnitTests { #ifdef _X86_ Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); - Log::Result(WEX::Logging::TestResults::Skipped); + Log::Result(TestResults::Skipped); return; #else Terminal term; @@ -218,7 +219,7 @@ namespace TerminalCoreUnitTests { #ifdef _X86_ Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); - Log::Result(WEX::Logging::TestResults::Skipped); + Log::Result(TestResults::Skipped); return; #else Terminal term; @@ -253,7 +254,7 @@ namespace TerminalCoreUnitTests { #ifdef _X86_ Log::Comment(L"This test is unreliable on x86 but is fine elsewhere. Disabled on x86."); - Log::Result(WEX::Logging::TestResults::Skipped); + Log::Result(TestResults::Skipped); return; #else Terminal term; @@ -309,5 +310,199 @@ namespace TerminalCoreUnitTests } #endif } + + TEST_METHOD(DoubleClick_GeneralCase) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // set word delimiters for terminal + auto settings = winrt::make(0, 100, 100); + term.UpdateSettings(settings); + + // Insert text at position (4,10) + const std::wstring_view text = L"doubleClickMe"; + term.SetCursorPosition(4, 10); + term.Write(text); + + // Simulate double click at (x,y) = (5,10) + auto clickPos = COORD{ 5, 10 }; + term.DoubleClickSelection(clickPos); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 4, 10, (4 + gsl::narrow(text.size()) - 1), 10 })); + } + + TEST_METHOD(DoubleClick_Delimiter) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // set word delimiters for terminal + auto settings = winrt::make(0, 100, 100); + term.UpdateSettings(settings); + + // Simulate click at (x,y) = (5,10) + auto clickPos = COORD{ 5, 10 }; + term.DoubleClickSelection(clickPos); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 5, 10, 5, 10 })); + } + + TEST_METHOD(DoubleClickDrag_Right) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // set word delimiters for terminal + auto settings = winrt::make(0, 100, 100); + term.UpdateSettings(settings); + + // Insert text at position (4,10) + const std::wstring_view text = L"doubleClickMe dragThroughHere"; + term.SetCursorPosition(4, 10); + term.Write(text); + + // Simulate double click at (x,y) = (5,10) + term.DoubleClickSelection({ 5, 10 }); + + // Simulate move to (x,y) = (21,10) + // + // buffer: doubleClickMe dragThroughHere + // ^ ^ + // start finish + term.SetEndSelectionPosition({ 21, 10 }); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 4, 10, 32, 10 })); + } + + TEST_METHOD(DoubleClickDrag_Left) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // set word delimiters for terminal + auto settings = winrt::make(0, 100, 100); + term.UpdateSettings(settings); + + // Insert text at position (21,10) + const std::wstring_view text = L"doubleClickMe dragThroughHere"; + term.SetCursorPosition(4, 10); + term.Write(text); + + // Simulate double click at (x,y) = (21,10) + term.DoubleClickSelection({ 21, 10 }); + + // Simulate move to (x,y) = (5,10) + // + // buffer: doubleClickMe dragThroughHere + // ^ ^ + // finish start + term.SetEndSelectionPosition({ 5, 10 }); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 4, 10, 32, 10 })); + } + + TEST_METHOD(TripleClick_GeneralCase) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // Simulate click at (x,y) = (5,10) + auto clickPos = COORD{ 5, 10 }; + term.TripleClickSelection(clickPos); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 10, 99, 10 })); + } + + TEST_METHOD(TripleClickDrag_Horizontal) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // Simulate click at (x,y) = (5,10) + auto clickPos = COORD{ 5, 10 }; + term.TripleClickSelection(clickPos); + + // Simulate move to (x,y) = (7,10) + term.SetEndSelectionPosition({ 7, 10 }); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 10, 99, 10 })); + } + + TEST_METHOD(TripleClickDrag_Vertical) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // Simulate click at (x,y) = (5,10) + auto clickPos = COORD{ 5, 10 }; + term.TripleClickSelection(clickPos); + + // Simulate move to (x,y) = (5,11) + term.SetEndSelectionPosition({ 5, 11 }); + + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(2)); + + // verify first selection rect + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 10, 99, 10 })); + + // verify second selection rect + selection = term.GetViewport().ConvertToOrigin(selectionRects.at(1)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 11, 99, 11 })); + } }; } diff --git a/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj b/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj index 1e71d072ccf..af44b123721 100644 --- a/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj +++ b/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj @@ -28,6 +28,7 @@
+ From becdd16008343a1f19741473abd53a32fd6a53b3 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 15 Aug 2019 16:01:46 -0500 Subject: [PATCH 038/154] Add Dustin's comment from #632 to Niksa's Doc (#2346) This seemed like it fit the style & depth of the other Niksa posts, so I'm proposing we add it here. We could always make a `Howett.md` if that seems more reasonable --- doc/Niksa.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/doc/Niksa.md b/doc/Niksa.md index ad26b6657f2..bd8fe0e2898 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -8,6 +8,7 @@ This document serves as a storage point for those posts. - [How are the Windows graphics/messaging stack assembled?](#gfxMsgStack) - [Output Processing between "Far East" and "Western"](#fesb) - [Why do we not backport things?](#backport) +- [Why can't we have mixed elevated and non-elevated tabs in the Terminal?](#elevation) ## Why do we avoid changing CMD.exe? `setlocal` doesn't behave the same way as an environment variable. It's a thing that would have to be put in at the top of the batch script that is `somefile.cmd` as one of its first commands to adjust the way that one specific batch file is processed by the `cmd.exe` engine. That's probably not suitable for your needs, but that's the way we have to go. @@ -145,3 +146,36 @@ It's also costly in terms of time, effort, and testing for us to validate a modi So from our little team working hard to make developers happy, we virtually never make the cut for servicing. We're sorry, but we hope you can understand. It's just the reality of the situation to say "nope" when people ask for a backport. In our team's ideal world, you would all be running the latest console bits everywhere everytime we make a change. But that's just not how it is today. Original Source: https://github.com/microsoft/terminal/issues/279#issuecomment-439179675 + +## Why can't we have mixed elevated and non-elevated tabs in the Terminal? + +_guest speaker @DHowett-MSFT_ + +[1] It is trivial when you are _hosting traditional windows_ with traditional window handles. That works very well in the conemu case, or in the tabbed shell case, where you can take over a window in an elevated session and re-parent it under a window in a non-elevated session. + +When you do that, there's a few security features that I'll touch on in [2]. Because of those, you can parent it but you can't really force it to do anything. + +There's a problem, though. The Terminal isn't architected as a collection of re-parentable windows. For example, it's not running a console host and moving its window into a tab. It was designed to support a "connection" -- something that can read and write text. It's a lower-level primitive than a window. We realized the error of our ways and decided that the UNIX model was right the entire time, and pipes and text and streams are _where it's at._ + +Given that we're using Xaml islands to host a modern UI and stitching a DirectX surface into it, we're far beyond the world of standard window handles anyway. Xaml islands are fully composed into a single HWND, much like Chrome and Firefox and the gamut of DirectX/OpenGL/SDL games. We don't **have** components that can be run in one process (elevated) and hosted in another (non-elevated) that aren't the aforementioned "connections". + +Now, the obvious followup question is _"why can't you have one elevated connection in a tab next to a non-elevated connection?"_ This is where @sba923 should pick up reading (:smile:). I'm probably going to cover some things that you (@robomac) know already. + +[2] When you have two windows on the same desktop in the same window station, they can communicate with eachother. I can use `SendKeys` easily through `WScript.Shell` to send keyboard input to any window that the shell can see. + +Running a process elevated _severs_ that connection. The shell can't see the elevated window. No other program at the same integrity level as the shell can see the elevated window. Even if it has its window handle, it can't really interact with it. This is also why you can't drag/drop from explorer into notepad if notepad is running elevated. Only another elevated process can interact with another elevated window. + +That "security" feature (call it what you like, it was probably intended to be a security feature at one point) only exists for a few session-global object types. Windows are one of them. Pipes aren't really one of them. + +Because of that, it's trivial to break that security. Take the terminal as an example of that. If we start an elevated connection and host it in a _non-elevated_ window, we've suddenly created a conduit through that security boundary. The elevated thing on the other end isn't a window, it's just a text-mode application. It immediately does the bidding of the non-elevated host. + +Anybody that can _control_ the non-elevated host (like `WScript.Shell::SendKeys`) _also_ gets an instant conduit through the elevation boundary. Suddenly, any medium integrity application on your system can control a high-integrity process. This could be your browser, or the bitcoin miner that got installed with the `left-pad` package from NPM, or really any number of things. + +It's a small risk, but it _is_ a risk. + +--- + +Other platforms have accepted that risk in preference for user convenience. They aren't wrong to do so, but I think Microsoft gets less of a "pass" on things like "accepting risk for user convenience". Windows 9x was an unmitigated security disaster, and limited user accounts and elevation prompts and kernel-level security for window management were the answer to those things. They're not locks to be loosened lightly. + +Original Source: https://github.com/microsoft/terminal/issues/632#issuecomment-519375707 + From 16e1e29a12ac3f3f2f41d0a24be41dedab1f5605 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Fri, 16 Aug 2019 10:54:17 -0700 Subject: [PATCH 039/154] Replace CodepointWidthDetector's runtime table with a static one (#2368) This commit replaces CodepointWidthDetector's dynamically-generated map with a static constexpr one that's compiled into the binary. It also almost totally removes the notion of an `Invalid` width. We definitely had gaps in our character coverage where we'd report a character as invalid, but we'd then flatten that down to `Narrow` when asked. By combining the not-present state and the narrow state, we get to save a significant chunk of data. I've tested this by feeding it all 0x10FFFF codepoints (and then some) and making sure they 100% match the old code's outputs. |------------------------------|---------------|----------------| | Metric | Then | Now | |------------------------------|---------------|----------------| | disk space | 56k (`.text`) | 3k (`.rdata`) | | runtime memory (allocations) | 1088 | 0 | | runtime memory (bytes) | 51k | ~0 | | memory behavior | not shared | fully shared | | lookup time | ~31ns | ~9ns | | first hit penalty | ~170000ns | 0ns | | lines of code | 1088 | 285 | | clarity | extreme | slightly worse | |------------------------------|---------------|----------------| I also took a moment and cleaned up a stray boolean that we didn't need. --- .../ut_host/CodepointWidthDetectorTests.cpp | 20 - src/types/CodepointWidthDetector.cpp | 1427 ++++------------- src/types/inc/CodepointWidthDetector.hpp | 78 +- 3 files changed, 318 insertions(+), 1207 deletions(-) diff --git a/src/host/ut_host/CodepointWidthDetectorTests.cpp b/src/host/ut_host/CodepointWidthDetectorTests.cpp index ca8dd337389..f7992dc9637 100644 --- a/src/host/ut_host/CodepointWidthDetectorTests.cpp +++ b/src/host/ut_host/CodepointWidthDetectorTests.cpp @@ -32,32 +32,12 @@ class CodepointWidthDetectorTests { TEST_CLASS(CodepointWidthDetectorTests); - TEST_METHOD(CodepointWidthDetectDefersMapPopulation) - { - CodepointWidthDetector widthDetector; - VERIFY_IS_TRUE(widthDetector._map.empty()); - widthDetector.IsWide(UNICODE_SPACE); - VERIFY_IS_TRUE(widthDetector._map.empty()); - // now force checking - widthDetector.GetWidth(emoji); - VERIFY_IS_FALSE(widthDetector._map.empty()); - } - TEST_METHOD(CanLookUpEmoji) { CodepointWidthDetector widthDetector; VERIFY_IS_TRUE(widthDetector.IsWide(emoji)); } - TEST_METHOD(TestUnicodeRangeCompare) - { - CodepointWidthDetector::UnicodeRangeCompare compare; - // test comparing 2 search terms - CodepointWidthDetector::UnicodeRange a{ 0x10 }; - CodepointWidthDetector::UnicodeRange b{ 0x15 }; - VERIFY_IS_TRUE(static_cast(compare(a, b))); - } - TEST_METHOD(CanExtractCodepoint) { CodepointWidthDetector widthDetector; diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index cf113134ced..c627a2b1fd8 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -4,6 +4,312 @@ #include "precomp.h" #include "inc/CodepointWidthDetector.hpp" +namespace +{ + // used to store range data in CodepointWidthDetector's internal map + struct UnicodeRange final + { + unsigned int lowerBound; + unsigned int upperBound; + CodepointWidth width; + }; + + static bool operator<(const UnicodeRange& range, const unsigned int searchTerm) + { + return range.upperBound < searchTerm; + } + + static constexpr std::array s_wideAndAmbiguousTable{ + // generated from http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt + // anything not present here is presumed to be Narrow. + UnicodeRange{ 0xa1, 0xa1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xa4, 0xa4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xa7, 0xa8, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xaa, 0xaa, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xad, 0xae, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xb0, 0xb4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xb6, 0xba, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xbc, 0xbf, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xc6, 0xc6, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xd0, 0xd0, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xd7, 0xd8, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xde, 0xe1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xe6, 0xe6, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xe8, 0xea, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xec, 0xed, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xf0, 0xf0, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xf2, 0xf3, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xf7, 0xfa, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xfc, 0xfc, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xfe, 0xfe, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x101, 0x101, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x111, 0x111, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x113, 0x113, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x11b, 0x11b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x126, 0x127, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x12b, 0x12b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x131, 0x133, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x138, 0x138, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x13f, 0x142, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x144, 0x144, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x148, 0x14b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x14d, 0x14d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x152, 0x153, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x166, 0x167, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x16b, 0x16b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1ce, 0x1ce, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1d0, 0x1d0, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1d2, 0x1d2, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1d4, 0x1d4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1d6, 0x1d6, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1d8, 0x1d8, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1da, 0x1da, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1dc, 0x1dc, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x251, 0x251, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x261, 0x261, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2c4, 0x2c4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2c7, 0x2c7, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2c9, 0x2cb, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2cd, 0x2cd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2d0, 0x2d0, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2d8, 0x2db, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2dd, 0x2dd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2df, 0x2df, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x300, 0x36f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x391, 0x3a1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x3a3, 0x3a9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x3b1, 0x3c1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x3c3, 0x3c9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x401, 0x401, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x410, 0x44f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x451, 0x451, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1100, 0x115f, CodepointWidth::Wide }, + UnicodeRange{ 0x2010, 0x2010, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2013, 0x2016, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2018, 0x2019, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x201c, 0x201d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2020, 0x2022, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2024, 0x2027, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2030, 0x2030, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2032, 0x2033, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2035, 0x2035, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x203b, 0x203b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x203e, 0x203e, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2074, 0x2074, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x207f, 0x207f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2081, 0x2084, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x20ac, 0x20ac, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2103, 0x2103, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2105, 0x2105, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2109, 0x2109, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2113, 0x2113, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2116, 0x2116, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2121, 0x2122, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2126, 0x2126, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x212b, 0x212b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2153, 0x2154, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x215b, 0x215e, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2160, 0x216b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2170, 0x2179, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2189, 0x2189, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2190, 0x2199, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x21b8, 0x21b9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x21d2, 0x21d2, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x21d4, 0x21d4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x21e7, 0x21e7, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2200, 0x2200, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2202, 0x2203, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2207, 0x2208, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x220b, 0x220b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x220f, 0x220f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2211, 0x2211, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2215, 0x2215, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x221a, 0x221a, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x221d, 0x2220, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2223, 0x2223, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2225, 0x2225, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2227, 0x222c, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x222e, 0x222e, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2234, 0x2237, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x223c, 0x223d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2248, 0x2248, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x224c, 0x224c, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2252, 0x2252, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2260, 0x2261, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2264, 0x2267, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x226a, 0x226b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x226e, 0x226f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2282, 0x2283, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2286, 0x2287, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2295, 0x2295, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2299, 0x2299, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x22a5, 0x22a5, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x22bf, 0x22bf, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2312, 0x2312, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x231a, 0x231b, CodepointWidth::Wide }, + UnicodeRange{ 0x2329, 0x232a, CodepointWidth::Wide }, + UnicodeRange{ 0x23e9, 0x23ec, CodepointWidth::Wide }, + UnicodeRange{ 0x23f0, 0x23f0, CodepointWidth::Wide }, + UnicodeRange{ 0x23f3, 0x23f3, CodepointWidth::Wide }, + UnicodeRange{ 0x2460, 0x24e9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x24eb, 0x254b, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2550, 0x2573, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2580, 0x258f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2592, 0x2595, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25a0, 0x25a1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25a3, 0x25a9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25b2, 0x25b3, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25b6, 0x25b7, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25bc, 0x25bd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25c0, 0x25c1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25c6, 0x25c8, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25cb, 0x25cb, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25ce, 0x25d1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25e2, 0x25e5, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25ef, 0x25ef, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x25fd, 0x25fe, CodepointWidth::Wide }, + UnicodeRange{ 0x2605, 0x2606, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2609, 0x2609, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x260e, 0x260f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2614, 0x2615, CodepointWidth::Wide }, + UnicodeRange{ 0x261c, 0x261c, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x261e, 0x261e, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2640, 0x2640, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2642, 0x2642, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2648, 0x2653, CodepointWidth::Wide }, + UnicodeRange{ 0x2660, 0x2661, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2663, 0x2665, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2667, 0x266a, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x266c, 0x266d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x266f, 0x266f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x267f, 0x267f, CodepointWidth::Wide }, + UnicodeRange{ 0x2693, 0x2693, CodepointWidth::Wide }, + UnicodeRange{ 0x269e, 0x269f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26a1, 0x26a1, CodepointWidth::Wide }, + UnicodeRange{ 0x26aa, 0x26ab, CodepointWidth::Wide }, + UnicodeRange{ 0x26bd, 0x26be, CodepointWidth::Wide }, + UnicodeRange{ 0x26bf, 0x26bf, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26c4, 0x26c5, CodepointWidth::Wide }, + UnicodeRange{ 0x26c6, 0x26cd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26ce, 0x26ce, CodepointWidth::Wide }, + UnicodeRange{ 0x26cf, 0x26d3, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26d4, 0x26d4, CodepointWidth::Wide }, + UnicodeRange{ 0x26d5, 0x26e1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26e3, 0x26e3, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26e8, 0x26e9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26ea, 0x26ea, CodepointWidth::Wide }, + UnicodeRange{ 0x26eb, 0x26f1, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26f2, 0x26f3, CodepointWidth::Wide }, + UnicodeRange{ 0x26f4, 0x26f4, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26f5, 0x26f5, CodepointWidth::Wide }, + UnicodeRange{ 0x26f6, 0x26f9, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26fa, 0x26fa, CodepointWidth::Wide }, + UnicodeRange{ 0x26fb, 0x26fc, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x26fd, 0x26fd, CodepointWidth::Wide }, + UnicodeRange{ 0x26fe, 0x26ff, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2705, 0x2705, CodepointWidth::Wide }, + UnicodeRange{ 0x270a, 0x270b, CodepointWidth::Wide }, + UnicodeRange{ 0x2728, 0x2728, CodepointWidth::Wide }, + UnicodeRange{ 0x273d, 0x273d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x274c, 0x274c, CodepointWidth::Wide }, + UnicodeRange{ 0x274e, 0x274e, CodepointWidth::Wide }, + UnicodeRange{ 0x2753, 0x2755, CodepointWidth::Wide }, + UnicodeRange{ 0x2757, 0x2757, CodepointWidth::Wide }, + UnicodeRange{ 0x2776, 0x277f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2795, 0x2797, CodepointWidth::Wide }, + UnicodeRange{ 0x27b0, 0x27b0, CodepointWidth::Wide }, + UnicodeRange{ 0x27bf, 0x27bf, CodepointWidth::Wide }, + UnicodeRange{ 0x2b1b, 0x2b1c, CodepointWidth::Wide }, + UnicodeRange{ 0x2b50, 0x2b50, CodepointWidth::Wide }, + UnicodeRange{ 0x2b55, 0x2b55, CodepointWidth::Wide }, + UnicodeRange{ 0x2b56, 0x2b59, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x2e80, 0x2e99, CodepointWidth::Wide }, + UnicodeRange{ 0x2e9b, 0x2ef3, CodepointWidth::Wide }, + UnicodeRange{ 0x2f00, 0x2fd5, CodepointWidth::Wide }, + UnicodeRange{ 0x2ff0, 0x2ffb, CodepointWidth::Wide }, + UnicodeRange{ 0x3000, 0x303e, CodepointWidth::Wide }, + UnicodeRange{ 0x3041, 0x3096, CodepointWidth::Wide }, + UnicodeRange{ 0x3099, 0x30ff, CodepointWidth::Wide }, + UnicodeRange{ 0x3105, 0x312e, CodepointWidth::Wide }, + UnicodeRange{ 0x3131, 0x318e, CodepointWidth::Wide }, + UnicodeRange{ 0x3190, 0x31ba, CodepointWidth::Wide }, + UnicodeRange{ 0x31c0, 0x31e3, CodepointWidth::Wide }, + UnicodeRange{ 0x31f0, 0x321e, CodepointWidth::Wide }, + UnicodeRange{ 0x3220, 0x3247, CodepointWidth::Wide }, + UnicodeRange{ 0x3248, 0x324f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x3250, 0x32fe, CodepointWidth::Wide }, + UnicodeRange{ 0x3300, 0x4dbf, CodepointWidth::Wide }, + UnicodeRange{ 0x4e00, 0xa48c, CodepointWidth::Wide }, + UnicodeRange{ 0xa490, 0xa4c6, CodepointWidth::Wide }, + UnicodeRange{ 0xa960, 0xa97c, CodepointWidth::Wide }, + UnicodeRange{ 0xac00, 0xd7a3, CodepointWidth::Wide }, + UnicodeRange{ 0xe000, 0xf8ff, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xf900, 0xfaff, CodepointWidth::Wide }, + UnicodeRange{ 0xfe00, 0xfe0f, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xfe10, 0xfe19, CodepointWidth::Wide }, + UnicodeRange{ 0xfe30, 0xfe52, CodepointWidth::Wide }, + UnicodeRange{ 0xfe54, 0xfe66, CodepointWidth::Wide }, + UnicodeRange{ 0xfe68, 0xfe6b, CodepointWidth::Wide }, + UnicodeRange{ 0xff01, 0xff60, CodepointWidth::Wide }, + UnicodeRange{ 0xffe0, 0xffe6, CodepointWidth::Wide }, + UnicodeRange{ 0xfffd, 0xfffd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x16fe0, 0x16fe1, CodepointWidth::Wide }, + UnicodeRange{ 0x17000, 0x187ec, CodepointWidth::Wide }, + UnicodeRange{ 0x18800, 0x18af2, CodepointWidth::Wide }, + UnicodeRange{ 0x1b000, 0x1b11e, CodepointWidth::Wide }, + UnicodeRange{ 0x1b170, 0x1b2fb, CodepointWidth::Wide }, + UnicodeRange{ 0x1f004, 0x1f004, CodepointWidth::Wide }, + UnicodeRange{ 0x1f0cf, 0x1f0cf, CodepointWidth::Wide }, + UnicodeRange{ 0x1f100, 0x1f10a, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f110, 0x1f12d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f130, 0x1f169, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f170, 0x1f18d, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f18e, 0x1f18e, CodepointWidth::Wide }, + UnicodeRange{ 0x1f18f, 0x1f190, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f191, 0x1f19a, CodepointWidth::Wide }, + UnicodeRange{ 0x1f19b, 0x1f1ac, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x1f200, 0x1f202, CodepointWidth::Wide }, + UnicodeRange{ 0x1f210, 0x1f23b, CodepointWidth::Wide }, + UnicodeRange{ 0x1f240, 0x1f248, CodepointWidth::Wide }, + UnicodeRange{ 0x1f250, 0x1f251, CodepointWidth::Wide }, + UnicodeRange{ 0x1f260, 0x1f265, CodepointWidth::Wide }, + UnicodeRange{ 0x1f300, 0x1f320, CodepointWidth::Wide }, + UnicodeRange{ 0x1f32d, 0x1f335, CodepointWidth::Wide }, + UnicodeRange{ 0x1f337, 0x1f37c, CodepointWidth::Wide }, + UnicodeRange{ 0x1f37e, 0x1f393, CodepointWidth::Wide }, + UnicodeRange{ 0x1f3a0, 0x1f3ca, CodepointWidth::Wide }, + UnicodeRange{ 0x1f3cf, 0x1f3d3, CodepointWidth::Wide }, + UnicodeRange{ 0x1f3e0, 0x1f3f0, CodepointWidth::Wide }, + UnicodeRange{ 0x1f3f4, 0x1f3f4, CodepointWidth::Wide }, + UnicodeRange{ 0x1f3f8, 0x1f43e, CodepointWidth::Wide }, + UnicodeRange{ 0x1f440, 0x1f440, CodepointWidth::Wide }, + UnicodeRange{ 0x1f442, 0x1f4fc, CodepointWidth::Wide }, + UnicodeRange{ 0x1f4ff, 0x1f53d, CodepointWidth::Wide }, + UnicodeRange{ 0x1f54b, 0x1f54e, CodepointWidth::Wide }, + UnicodeRange{ 0x1f550, 0x1f567, CodepointWidth::Wide }, + UnicodeRange{ 0x1f57a, 0x1f57a, CodepointWidth::Wide }, + UnicodeRange{ 0x1f595, 0x1f596, CodepointWidth::Wide }, + UnicodeRange{ 0x1f5a4, 0x1f5a4, CodepointWidth::Wide }, + UnicodeRange{ 0x1f5fb, 0x1f64f, CodepointWidth::Wide }, + UnicodeRange{ 0x1f680, 0x1f6c5, CodepointWidth::Wide }, + UnicodeRange{ 0x1f6cc, 0x1f6cc, CodepointWidth::Wide }, + UnicodeRange{ 0x1f6d0, 0x1f6d2, CodepointWidth::Wide }, + UnicodeRange{ 0x1f6eb, 0x1f6ec, CodepointWidth::Wide }, + UnicodeRange{ 0x1f6f4, 0x1f6f8, CodepointWidth::Wide }, + UnicodeRange{ 0x1f910, 0x1f93e, CodepointWidth::Wide }, + UnicodeRange{ 0x1f940, 0x1f94c, CodepointWidth::Wide }, + UnicodeRange{ 0x1f950, 0x1f96b, CodepointWidth::Wide }, + UnicodeRange{ 0x1f980, 0x1f997, CodepointWidth::Wide }, + UnicodeRange{ 0x1f9c0, 0x1f9c0, CodepointWidth::Wide }, + UnicodeRange{ 0x1f9d0, 0x1f9e6, CodepointWidth::Wide }, + UnicodeRange{ 0x20000, 0x2fffd, CodepointWidth::Wide }, + UnicodeRange{ 0x30000, 0x3fffd, CodepointWidth::Wide }, + UnicodeRange{ 0xe0100, 0xe01ef, CodepointWidth::Ambiguous }, + UnicodeRange{ 0xf0000, 0xffffd, CodepointWidth::Ambiguous }, + UnicodeRange{ 0x100000, 0x10fffd, CodepointWidth::Ambiguous } + }; +} + // Routine Description: // - returns the width type of codepoint by searching the map generated from the unicode spec // Arguments: @@ -17,22 +323,17 @@ CodepointWidth CodepointWidthDetector::GetWidth(const std::wstring_view glyph) c return CodepointWidth::Invalid; } - if (_map.empty()) - { - const_cast(this)->_populateUnicodeSearchMap(); - } - const auto codepoint = _extractCodepoint(glyph); - UnicodeRange search{ codepoint }; - auto it = _map.find(search); - if (it == _map.end()) - { - return CodepointWidth::Invalid; - } - else + const auto it = std::lower_bound(s_wideAndAmbiguousTable.begin(), s_wideAndAmbiguousTable.end(), codepoint); + + // For characters that are not _in_ the table, lower_bound will return the nearest item that is. + // We must check its bounds to make sure that our hit was a true hit. + if (it != s_wideAndAmbiguousTable.end() && codepoint >= it->lowerBound && codepoint <= it->upperBound) { - return it->second; + return it->width; } + + return CodepointWidth::Narrow; } // Routine Description: @@ -75,7 +376,7 @@ bool CodepointWidthDetector::IsWide(const std::wstring_view glyph) const // If not, go to the lookup table. else if (width == CodepointWidth::Ambiguous) { - if (_hasFallback) + if (_pfnFallbackMethod) { return _checkFallbackViaCache(glyph); } @@ -112,7 +413,7 @@ bool CodepointWidthDetector::_lookupIsWide(const std::wstring_view glyph) const // If it's ambiguous, then ask the font if we can. if (width == CodepointWidth::Ambiguous) { - if (_hasFallback) + if (_pfnFallbackMethod) { return _checkFallbackViaCache(glyph); } @@ -161,7 +462,7 @@ bool CodepointWidthDetector::_checkFallbackViaCache(const std::wstring_view glyp // - glyph - the utf16 encoded codepoint convert // Return Value: // - the codepoint being stored -unsigned int CodepointWidthDetector::_extractCodepoint(const std::wstring_view glyph) const noexcept +unsigned int CodepointWidthDetector::_extractCodepoint(const std::wstring_view glyph) noexcept { if (glyph.size() == 1) { @@ -193,7 +494,6 @@ unsigned int CodepointWidthDetector::_extractCodepoint(const std::wstring_view g void CodepointWidthDetector::SetFallbackMethod(std::function pfnFallback) { _pfnFallbackMethod = pfnFallback; - _hasFallback = true; } // Method Description: @@ -208,1096 +508,3 @@ void CodepointWidthDetector::NotifyFontChanged() const noexcept { _fallbackCache.clear(); } - -void CodepointWidthDetector::_populateUnicodeSearchMap() -{ - // generated from http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt - _map[UnicodeRange(0, 160)] = CodepointWidth::Narrow; - _map[UnicodeRange(161, 161)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(162, 163)] = CodepointWidth::Narrow; - _map[UnicodeRange(164, 164)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(165, 166)] = CodepointWidth::Narrow; - _map[UnicodeRange(167, 168)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(169, 169)] = CodepointWidth::Narrow; - _map[UnicodeRange(170, 170)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(171, 172)] = CodepointWidth::Narrow; - _map[UnicodeRange(173, 174)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(175, 175)] = CodepointWidth::Narrow; - _map[UnicodeRange(176, 180)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(181, 181)] = CodepointWidth::Narrow; - _map[UnicodeRange(182, 186)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(187, 187)] = CodepointWidth::Narrow; - _map[UnicodeRange(188, 191)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(192, 197)] = CodepointWidth::Narrow; - _map[UnicodeRange(198, 198)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(199, 207)] = CodepointWidth::Narrow; - _map[UnicodeRange(208, 208)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(209, 214)] = CodepointWidth::Narrow; - _map[UnicodeRange(215, 216)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(217, 221)] = CodepointWidth::Narrow; - _map[UnicodeRange(222, 225)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(226, 229)] = CodepointWidth::Narrow; - _map[UnicodeRange(230, 230)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(231, 231)] = CodepointWidth::Narrow; - _map[UnicodeRange(232, 234)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(235, 235)] = CodepointWidth::Narrow; - _map[UnicodeRange(236, 237)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(238, 239)] = CodepointWidth::Narrow; - _map[UnicodeRange(240, 240)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(241, 241)] = CodepointWidth::Narrow; - _map[UnicodeRange(242, 243)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(244, 246)] = CodepointWidth::Narrow; - _map[UnicodeRange(247, 250)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(251, 251)] = CodepointWidth::Narrow; - _map[UnicodeRange(252, 252)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(253, 253)] = CodepointWidth::Narrow; - _map[UnicodeRange(254, 254)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(255, 256)] = CodepointWidth::Narrow; - _map[UnicodeRange(257, 257)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(258, 272)] = CodepointWidth::Narrow; - _map[UnicodeRange(273, 273)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(274, 274)] = CodepointWidth::Narrow; - _map[UnicodeRange(275, 275)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(276, 282)] = CodepointWidth::Narrow; - _map[UnicodeRange(283, 283)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(284, 293)] = CodepointWidth::Narrow; - _map[UnicodeRange(294, 295)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(296, 298)] = CodepointWidth::Narrow; - _map[UnicodeRange(299, 299)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(300, 304)] = CodepointWidth::Narrow; - _map[UnicodeRange(305, 307)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(308, 311)] = CodepointWidth::Narrow; - _map[UnicodeRange(312, 312)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(313, 318)] = CodepointWidth::Narrow; - _map[UnicodeRange(319, 322)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(323, 323)] = CodepointWidth::Narrow; - _map[UnicodeRange(324, 324)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(325, 327)] = CodepointWidth::Narrow; - _map[UnicodeRange(328, 331)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(332, 332)] = CodepointWidth::Narrow; - _map[UnicodeRange(333, 333)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(334, 337)] = CodepointWidth::Narrow; - _map[UnicodeRange(338, 339)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(340, 357)] = CodepointWidth::Narrow; - _map[UnicodeRange(358, 359)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(360, 362)] = CodepointWidth::Narrow; - _map[UnicodeRange(363, 363)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(364, 461)] = CodepointWidth::Narrow; - _map[UnicodeRange(462, 462)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(463, 463)] = CodepointWidth::Narrow; - _map[UnicodeRange(464, 464)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(465, 465)] = CodepointWidth::Narrow; - _map[UnicodeRange(466, 466)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(467, 467)] = CodepointWidth::Narrow; - _map[UnicodeRange(468, 468)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(469, 469)] = CodepointWidth::Narrow; - _map[UnicodeRange(470, 470)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(471, 471)] = CodepointWidth::Narrow; - _map[UnicodeRange(472, 472)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(473, 473)] = CodepointWidth::Narrow; - _map[UnicodeRange(474, 474)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(475, 475)] = CodepointWidth::Narrow; - _map[UnicodeRange(476, 476)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(477, 592)] = CodepointWidth::Narrow; - _map[UnicodeRange(593, 593)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(594, 608)] = CodepointWidth::Narrow; - _map[UnicodeRange(609, 609)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(610, 707)] = CodepointWidth::Narrow; - _map[UnicodeRange(708, 708)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(709, 710)] = CodepointWidth::Narrow; - _map[UnicodeRange(711, 711)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(712, 712)] = CodepointWidth::Narrow; - _map[UnicodeRange(713, 715)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(716, 716)] = CodepointWidth::Narrow; - _map[UnicodeRange(717, 717)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(718, 719)] = CodepointWidth::Narrow; - _map[UnicodeRange(720, 720)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(721, 727)] = CodepointWidth::Narrow; - _map[UnicodeRange(728, 731)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(732, 732)] = CodepointWidth::Narrow; - _map[UnicodeRange(733, 733)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(734, 734)] = CodepointWidth::Narrow; - _map[UnicodeRange(735, 735)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(736, 767)] = CodepointWidth::Narrow; - _map[UnicodeRange(768, 879)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(880, 887)] = CodepointWidth::Narrow; - _map[UnicodeRange(890, 895)] = CodepointWidth::Narrow; - _map[UnicodeRange(900, 906)] = CodepointWidth::Narrow; - _map[UnicodeRange(908, 908)] = CodepointWidth::Narrow; - _map[UnicodeRange(910, 912)] = CodepointWidth::Narrow; - _map[UnicodeRange(913, 929)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(931, 937)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(938, 944)] = CodepointWidth::Narrow; - _map[UnicodeRange(945, 961)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(962, 962)] = CodepointWidth::Narrow; - _map[UnicodeRange(963, 969)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(970, 1024)] = CodepointWidth::Narrow; - _map[UnicodeRange(1025, 1025)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(1026, 1039)] = CodepointWidth::Narrow; - _map[UnicodeRange(1040, 1103)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(1104, 1104)] = CodepointWidth::Narrow; - _map[UnicodeRange(1105, 1105)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(1106, 1327)] = CodepointWidth::Narrow; - _map[UnicodeRange(1329, 1366)] = CodepointWidth::Narrow; - _map[UnicodeRange(1369, 1375)] = CodepointWidth::Narrow; - _map[UnicodeRange(1377, 1415)] = CodepointWidth::Narrow; - _map[UnicodeRange(1417, 1418)] = CodepointWidth::Narrow; - _map[UnicodeRange(1421, 1423)] = CodepointWidth::Narrow; - _map[UnicodeRange(1425, 1479)] = CodepointWidth::Narrow; - _map[UnicodeRange(1488, 1514)] = CodepointWidth::Narrow; - _map[UnicodeRange(1520, 1524)] = CodepointWidth::Narrow; - _map[UnicodeRange(1536, 1564)] = CodepointWidth::Narrow; - _map[UnicodeRange(1566, 1805)] = CodepointWidth::Narrow; - _map[UnicodeRange(1807, 1866)] = CodepointWidth::Narrow; - _map[UnicodeRange(1869, 1969)] = CodepointWidth::Narrow; - _map[UnicodeRange(1984, 2042)] = CodepointWidth::Narrow; - _map[UnicodeRange(2048, 2093)] = CodepointWidth::Narrow; - _map[UnicodeRange(2096, 2110)] = CodepointWidth::Narrow; - _map[UnicodeRange(2112, 2139)] = CodepointWidth::Narrow; - _map[UnicodeRange(2142, 2142)] = CodepointWidth::Narrow; - _map[UnicodeRange(2144, 2154)] = CodepointWidth::Narrow; - _map[UnicodeRange(2208, 2228)] = CodepointWidth::Narrow; - _map[UnicodeRange(2230, 2237)] = CodepointWidth::Narrow; - _map[UnicodeRange(2260, 2435)] = CodepointWidth::Narrow; - _map[UnicodeRange(2437, 2444)] = CodepointWidth::Narrow; - _map[UnicodeRange(2447, 2448)] = CodepointWidth::Narrow; - _map[UnicodeRange(2451, 2472)] = CodepointWidth::Narrow; - _map[UnicodeRange(2474, 2480)] = CodepointWidth::Narrow; - _map[UnicodeRange(2482, 2482)] = CodepointWidth::Narrow; - _map[UnicodeRange(2486, 2489)] = CodepointWidth::Narrow; - _map[UnicodeRange(2492, 2500)] = CodepointWidth::Narrow; - _map[UnicodeRange(2503, 2504)] = CodepointWidth::Narrow; - _map[UnicodeRange(2507, 2510)] = CodepointWidth::Narrow; - _map[UnicodeRange(2519, 2519)] = CodepointWidth::Narrow; - _map[UnicodeRange(2524, 2525)] = CodepointWidth::Narrow; - _map[UnicodeRange(2527, 2531)] = CodepointWidth::Narrow; - _map[UnicodeRange(2534, 2557)] = CodepointWidth::Narrow; - _map[UnicodeRange(2561, 2563)] = CodepointWidth::Narrow; - _map[UnicodeRange(2565, 2570)] = CodepointWidth::Narrow; - _map[UnicodeRange(2575, 2576)] = CodepointWidth::Narrow; - _map[UnicodeRange(2579, 2600)] = CodepointWidth::Narrow; - _map[UnicodeRange(2602, 2608)] = CodepointWidth::Narrow; - _map[UnicodeRange(2610, 2611)] = CodepointWidth::Narrow; - _map[UnicodeRange(2613, 2614)] = CodepointWidth::Narrow; - _map[UnicodeRange(2616, 2617)] = CodepointWidth::Narrow; - _map[UnicodeRange(2620, 2620)] = CodepointWidth::Narrow; - _map[UnicodeRange(2622, 2626)] = CodepointWidth::Narrow; - _map[UnicodeRange(2631, 2632)] = CodepointWidth::Narrow; - _map[UnicodeRange(2635, 2637)] = CodepointWidth::Narrow; - _map[UnicodeRange(2641, 2641)] = CodepointWidth::Narrow; - _map[UnicodeRange(2649, 2652)] = CodepointWidth::Narrow; - _map[UnicodeRange(2654, 2654)] = CodepointWidth::Narrow; - _map[UnicodeRange(2662, 2677)] = CodepointWidth::Narrow; - _map[UnicodeRange(2689, 2691)] = CodepointWidth::Narrow; - _map[UnicodeRange(2693, 2701)] = CodepointWidth::Narrow; - _map[UnicodeRange(2703, 2705)] = CodepointWidth::Narrow; - _map[UnicodeRange(2707, 2728)] = CodepointWidth::Narrow; - _map[UnicodeRange(2730, 2736)] = CodepointWidth::Narrow; - _map[UnicodeRange(2738, 2739)] = CodepointWidth::Narrow; - _map[UnicodeRange(2741, 2745)] = CodepointWidth::Narrow; - _map[UnicodeRange(2748, 2757)] = CodepointWidth::Narrow; - _map[UnicodeRange(2759, 2761)] = CodepointWidth::Narrow; - _map[UnicodeRange(2763, 2765)] = CodepointWidth::Narrow; - _map[UnicodeRange(2768, 2768)] = CodepointWidth::Narrow; - _map[UnicodeRange(2784, 2787)] = CodepointWidth::Narrow; - _map[UnicodeRange(2790, 2801)] = CodepointWidth::Narrow; - _map[UnicodeRange(2809, 2815)] = CodepointWidth::Narrow; - _map[UnicodeRange(2817, 2819)] = CodepointWidth::Narrow; - _map[UnicodeRange(2821, 2828)] = CodepointWidth::Narrow; - _map[UnicodeRange(2831, 2832)] = CodepointWidth::Narrow; - _map[UnicodeRange(2835, 2856)] = CodepointWidth::Narrow; - _map[UnicodeRange(2858, 2864)] = CodepointWidth::Narrow; - _map[UnicodeRange(2866, 2867)] = CodepointWidth::Narrow; - _map[UnicodeRange(2869, 2873)] = CodepointWidth::Narrow; - _map[UnicodeRange(2876, 2884)] = CodepointWidth::Narrow; - _map[UnicodeRange(2887, 2888)] = CodepointWidth::Narrow; - _map[UnicodeRange(2891, 2893)] = CodepointWidth::Narrow; - _map[UnicodeRange(2902, 2903)] = CodepointWidth::Narrow; - _map[UnicodeRange(2908, 2909)] = CodepointWidth::Narrow; - _map[UnicodeRange(2911, 2915)] = CodepointWidth::Narrow; - _map[UnicodeRange(2918, 2935)] = CodepointWidth::Narrow; - _map[UnicodeRange(2946, 2947)] = CodepointWidth::Narrow; - _map[UnicodeRange(2949, 2954)] = CodepointWidth::Narrow; - _map[UnicodeRange(2958, 2960)] = CodepointWidth::Narrow; - _map[UnicodeRange(2962, 2965)] = CodepointWidth::Narrow; - _map[UnicodeRange(2969, 2970)] = CodepointWidth::Narrow; - _map[UnicodeRange(2972, 2972)] = CodepointWidth::Narrow; - _map[UnicodeRange(2974, 2975)] = CodepointWidth::Narrow; - _map[UnicodeRange(2979, 2980)] = CodepointWidth::Narrow; - _map[UnicodeRange(2984, 2986)] = CodepointWidth::Narrow; - _map[UnicodeRange(2990, 3001)] = CodepointWidth::Narrow; - _map[UnicodeRange(3006, 3010)] = CodepointWidth::Narrow; - _map[UnicodeRange(3014, 3016)] = CodepointWidth::Narrow; - _map[UnicodeRange(3018, 3021)] = CodepointWidth::Narrow; - _map[UnicodeRange(3024, 3024)] = CodepointWidth::Narrow; - _map[UnicodeRange(3031, 3031)] = CodepointWidth::Narrow; - _map[UnicodeRange(3046, 3066)] = CodepointWidth::Narrow; - _map[UnicodeRange(3072, 3075)] = CodepointWidth::Narrow; - _map[UnicodeRange(3077, 3084)] = CodepointWidth::Narrow; - _map[UnicodeRange(3086, 3088)] = CodepointWidth::Narrow; - _map[UnicodeRange(3090, 3112)] = CodepointWidth::Narrow; - _map[UnicodeRange(3114, 3129)] = CodepointWidth::Narrow; - _map[UnicodeRange(3133, 3140)] = CodepointWidth::Narrow; - _map[UnicodeRange(3142, 3144)] = CodepointWidth::Narrow; - _map[UnicodeRange(3146, 3149)] = CodepointWidth::Narrow; - _map[UnicodeRange(3157, 3158)] = CodepointWidth::Narrow; - _map[UnicodeRange(3160, 3162)] = CodepointWidth::Narrow; - _map[UnicodeRange(3168, 3171)] = CodepointWidth::Narrow; - _map[UnicodeRange(3174, 3183)] = CodepointWidth::Narrow; - _map[UnicodeRange(3192, 3203)] = CodepointWidth::Narrow; - _map[UnicodeRange(3205, 3212)] = CodepointWidth::Narrow; - _map[UnicodeRange(3214, 3216)] = CodepointWidth::Narrow; - _map[UnicodeRange(3218, 3240)] = CodepointWidth::Narrow; - _map[UnicodeRange(3242, 3251)] = CodepointWidth::Narrow; - _map[UnicodeRange(3253, 3257)] = CodepointWidth::Narrow; - _map[UnicodeRange(3260, 3268)] = CodepointWidth::Narrow; - _map[UnicodeRange(3270, 3272)] = CodepointWidth::Narrow; - _map[UnicodeRange(3274, 3277)] = CodepointWidth::Narrow; - _map[UnicodeRange(3285, 3286)] = CodepointWidth::Narrow; - _map[UnicodeRange(3294, 3294)] = CodepointWidth::Narrow; - _map[UnicodeRange(3296, 3299)] = CodepointWidth::Narrow; - _map[UnicodeRange(3302, 3311)] = CodepointWidth::Narrow; - _map[UnicodeRange(3313, 3314)] = CodepointWidth::Narrow; - _map[UnicodeRange(3328, 3331)] = CodepointWidth::Narrow; - _map[UnicodeRange(3333, 3340)] = CodepointWidth::Narrow; - _map[UnicodeRange(3342, 3344)] = CodepointWidth::Narrow; - _map[UnicodeRange(3346, 3396)] = CodepointWidth::Narrow; - _map[UnicodeRange(3398, 3400)] = CodepointWidth::Narrow; - _map[UnicodeRange(3402, 3407)] = CodepointWidth::Narrow; - _map[UnicodeRange(3412, 3427)] = CodepointWidth::Narrow; - _map[UnicodeRange(3430, 3455)] = CodepointWidth::Narrow; - _map[UnicodeRange(3458, 3459)] = CodepointWidth::Narrow; - _map[UnicodeRange(3461, 3478)] = CodepointWidth::Narrow; - _map[UnicodeRange(3482, 3505)] = CodepointWidth::Narrow; - _map[UnicodeRange(3507, 3515)] = CodepointWidth::Narrow; - _map[UnicodeRange(3517, 3517)] = CodepointWidth::Narrow; - _map[UnicodeRange(3520, 3526)] = CodepointWidth::Narrow; - _map[UnicodeRange(3530, 3530)] = CodepointWidth::Narrow; - _map[UnicodeRange(3535, 3540)] = CodepointWidth::Narrow; - _map[UnicodeRange(3542, 3542)] = CodepointWidth::Narrow; - _map[UnicodeRange(3544, 3551)] = CodepointWidth::Narrow; - _map[UnicodeRange(3558, 3567)] = CodepointWidth::Narrow; - _map[UnicodeRange(3570, 3572)] = CodepointWidth::Narrow; - _map[UnicodeRange(3585, 3642)] = CodepointWidth::Narrow; - _map[UnicodeRange(3647, 3675)] = CodepointWidth::Narrow; - _map[UnicodeRange(3713, 3714)] = CodepointWidth::Narrow; - _map[UnicodeRange(3716, 3716)] = CodepointWidth::Narrow; - _map[UnicodeRange(3719, 3720)] = CodepointWidth::Narrow; - _map[UnicodeRange(3722, 3722)] = CodepointWidth::Narrow; - _map[UnicodeRange(3725, 3725)] = CodepointWidth::Narrow; - _map[UnicodeRange(3732, 3735)] = CodepointWidth::Narrow; - _map[UnicodeRange(3737, 3743)] = CodepointWidth::Narrow; - _map[UnicodeRange(3745, 3747)] = CodepointWidth::Narrow; - _map[UnicodeRange(3749, 3749)] = CodepointWidth::Narrow; - _map[UnicodeRange(3751, 3751)] = CodepointWidth::Narrow; - _map[UnicodeRange(3754, 3755)] = CodepointWidth::Narrow; - _map[UnicodeRange(3757, 3769)] = CodepointWidth::Narrow; - _map[UnicodeRange(3771, 3773)] = CodepointWidth::Narrow; - _map[UnicodeRange(3776, 3780)] = CodepointWidth::Narrow; - _map[UnicodeRange(3782, 3782)] = CodepointWidth::Narrow; - _map[UnicodeRange(3784, 3789)] = CodepointWidth::Narrow; - _map[UnicodeRange(3792, 3801)] = CodepointWidth::Narrow; - _map[UnicodeRange(3804, 3807)] = CodepointWidth::Narrow; - _map[UnicodeRange(3840, 3911)] = CodepointWidth::Narrow; - _map[UnicodeRange(3913, 3948)] = CodepointWidth::Narrow; - _map[UnicodeRange(3953, 3991)] = CodepointWidth::Narrow; - _map[UnicodeRange(3993, 4028)] = CodepointWidth::Narrow; - _map[UnicodeRange(4030, 4044)] = CodepointWidth::Narrow; - _map[UnicodeRange(4046, 4058)] = CodepointWidth::Narrow; - _map[UnicodeRange(4096, 4293)] = CodepointWidth::Narrow; - _map[UnicodeRange(4295, 4295)] = CodepointWidth::Narrow; - _map[UnicodeRange(4301, 4301)] = CodepointWidth::Narrow; - _map[UnicodeRange(4304, 4351)] = CodepointWidth::Narrow; - _map[UnicodeRange(4352, 4447)] = CodepointWidth::Wide; - _map[UnicodeRange(4448, 4680)] = CodepointWidth::Narrow; - _map[UnicodeRange(4682, 4685)] = CodepointWidth::Narrow; - _map[UnicodeRange(4688, 4694)] = CodepointWidth::Narrow; - _map[UnicodeRange(4696, 4696)] = CodepointWidth::Narrow; - _map[UnicodeRange(4698, 4701)] = CodepointWidth::Narrow; - _map[UnicodeRange(4704, 4744)] = CodepointWidth::Narrow; - _map[UnicodeRange(4746, 4749)] = CodepointWidth::Narrow; - _map[UnicodeRange(4752, 4784)] = CodepointWidth::Narrow; - _map[UnicodeRange(4786, 4789)] = CodepointWidth::Narrow; - _map[UnicodeRange(4792, 4798)] = CodepointWidth::Narrow; - _map[UnicodeRange(4800, 4800)] = CodepointWidth::Narrow; - _map[UnicodeRange(4802, 4805)] = CodepointWidth::Narrow; - _map[UnicodeRange(4808, 4822)] = CodepointWidth::Narrow; - _map[UnicodeRange(4824, 4880)] = CodepointWidth::Narrow; - _map[UnicodeRange(4882, 4885)] = CodepointWidth::Narrow; - _map[UnicodeRange(4888, 4954)] = CodepointWidth::Narrow; - _map[UnicodeRange(4957, 4988)] = CodepointWidth::Narrow; - _map[UnicodeRange(4992, 5017)] = CodepointWidth::Narrow; - _map[UnicodeRange(5024, 5109)] = CodepointWidth::Narrow; - _map[UnicodeRange(5112, 5117)] = CodepointWidth::Narrow; - _map[UnicodeRange(5120, 5788)] = CodepointWidth::Narrow; - _map[UnicodeRange(5792, 5880)] = CodepointWidth::Narrow; - _map[UnicodeRange(5888, 5900)] = CodepointWidth::Narrow; - _map[UnicodeRange(5902, 5908)] = CodepointWidth::Narrow; - _map[UnicodeRange(5920, 5942)] = CodepointWidth::Narrow; - _map[UnicodeRange(5952, 5971)] = CodepointWidth::Narrow; - _map[UnicodeRange(5984, 5996)] = CodepointWidth::Narrow; - _map[UnicodeRange(5998, 6000)] = CodepointWidth::Narrow; - _map[UnicodeRange(6002, 6003)] = CodepointWidth::Narrow; - _map[UnicodeRange(6016, 6109)] = CodepointWidth::Narrow; - _map[UnicodeRange(6112, 6121)] = CodepointWidth::Narrow; - _map[UnicodeRange(6128, 6137)] = CodepointWidth::Narrow; - _map[UnicodeRange(6144, 6158)] = CodepointWidth::Narrow; - _map[UnicodeRange(6160, 6169)] = CodepointWidth::Narrow; - _map[UnicodeRange(6176, 6263)] = CodepointWidth::Narrow; - _map[UnicodeRange(6272, 6314)] = CodepointWidth::Narrow; - _map[UnicodeRange(6320, 6389)] = CodepointWidth::Narrow; - _map[UnicodeRange(6400, 6430)] = CodepointWidth::Narrow; - _map[UnicodeRange(6432, 6443)] = CodepointWidth::Narrow; - _map[UnicodeRange(6448, 6459)] = CodepointWidth::Narrow; - _map[UnicodeRange(6464, 6464)] = CodepointWidth::Narrow; - _map[UnicodeRange(6468, 6509)] = CodepointWidth::Narrow; - _map[UnicodeRange(6512, 6516)] = CodepointWidth::Narrow; - _map[UnicodeRange(6528, 6571)] = CodepointWidth::Narrow; - _map[UnicodeRange(6576, 6601)] = CodepointWidth::Narrow; - _map[UnicodeRange(6608, 6618)] = CodepointWidth::Narrow; - _map[UnicodeRange(6622, 6683)] = CodepointWidth::Narrow; - _map[UnicodeRange(6686, 6750)] = CodepointWidth::Narrow; - _map[UnicodeRange(6752, 6780)] = CodepointWidth::Narrow; - _map[UnicodeRange(6783, 6793)] = CodepointWidth::Narrow; - _map[UnicodeRange(6800, 6809)] = CodepointWidth::Narrow; - _map[UnicodeRange(6816, 6829)] = CodepointWidth::Narrow; - _map[UnicodeRange(6832, 6846)] = CodepointWidth::Narrow; - _map[UnicodeRange(6912, 6987)] = CodepointWidth::Narrow; - _map[UnicodeRange(6992, 7036)] = CodepointWidth::Narrow; - _map[UnicodeRange(7040, 7155)] = CodepointWidth::Narrow; - _map[UnicodeRange(7164, 7223)] = CodepointWidth::Narrow; - _map[UnicodeRange(7227, 7241)] = CodepointWidth::Narrow; - _map[UnicodeRange(7245, 7304)] = CodepointWidth::Narrow; - _map[UnicodeRange(7360, 7367)] = CodepointWidth::Narrow; - _map[UnicodeRange(7376, 7417)] = CodepointWidth::Narrow; - _map[UnicodeRange(7424, 7673)] = CodepointWidth::Narrow; - _map[UnicodeRange(7675, 7957)] = CodepointWidth::Narrow; - _map[UnicodeRange(7960, 7965)] = CodepointWidth::Narrow; - _map[UnicodeRange(7968, 8005)] = CodepointWidth::Narrow; - _map[UnicodeRange(8008, 8013)] = CodepointWidth::Narrow; - _map[UnicodeRange(8016, 8023)] = CodepointWidth::Narrow; - _map[UnicodeRange(8025, 8025)] = CodepointWidth::Narrow; - _map[UnicodeRange(8027, 8027)] = CodepointWidth::Narrow; - _map[UnicodeRange(8029, 8029)] = CodepointWidth::Narrow; - _map[UnicodeRange(8031, 8061)] = CodepointWidth::Narrow; - _map[UnicodeRange(8064, 8116)] = CodepointWidth::Narrow; - _map[UnicodeRange(8118, 8132)] = CodepointWidth::Narrow; - _map[UnicodeRange(8134, 8147)] = CodepointWidth::Narrow; - _map[UnicodeRange(8150, 8155)] = CodepointWidth::Narrow; - _map[UnicodeRange(8157, 8175)] = CodepointWidth::Narrow; - _map[UnicodeRange(8178, 8180)] = CodepointWidth::Narrow; - _map[UnicodeRange(8182, 8190)] = CodepointWidth::Narrow; - _map[UnicodeRange(8192, 8207)] = CodepointWidth::Narrow; - _map[UnicodeRange(8208, 8208)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8209, 8210)] = CodepointWidth::Narrow; - _map[UnicodeRange(8211, 8214)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8215, 8215)] = CodepointWidth::Narrow; - _map[UnicodeRange(8216, 8217)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8218, 8219)] = CodepointWidth::Narrow; - _map[UnicodeRange(8220, 8221)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8222, 8223)] = CodepointWidth::Narrow; - _map[UnicodeRange(8224, 8226)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8227, 8227)] = CodepointWidth::Narrow; - _map[UnicodeRange(8228, 8231)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8232, 8239)] = CodepointWidth::Narrow; - _map[UnicodeRange(8240, 8240)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8241, 8241)] = CodepointWidth::Narrow; - _map[UnicodeRange(8242, 8243)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8244, 8244)] = CodepointWidth::Narrow; - _map[UnicodeRange(8245, 8245)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8246, 8250)] = CodepointWidth::Narrow; - _map[UnicodeRange(8251, 8251)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8252, 8253)] = CodepointWidth::Narrow; - _map[UnicodeRange(8254, 8254)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8255, 8292)] = CodepointWidth::Narrow; - _map[UnicodeRange(8294, 8305)] = CodepointWidth::Narrow; - _map[UnicodeRange(8308, 8308)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8309, 8318)] = CodepointWidth::Narrow; - _map[UnicodeRange(8319, 8319)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8320, 8320)] = CodepointWidth::Narrow; - _map[UnicodeRange(8321, 8324)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8325, 8334)] = CodepointWidth::Narrow; - _map[UnicodeRange(8336, 8348)] = CodepointWidth::Narrow; - _map[UnicodeRange(8352, 8363)] = CodepointWidth::Narrow; - _map[UnicodeRange(8364, 8364)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8365, 8383)] = CodepointWidth::Narrow; - _map[UnicodeRange(8400, 8432)] = CodepointWidth::Narrow; - _map[UnicodeRange(8448, 8450)] = CodepointWidth::Narrow; - _map[UnicodeRange(8451, 8451)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8452, 8452)] = CodepointWidth::Narrow; - _map[UnicodeRange(8453, 8453)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8454, 8456)] = CodepointWidth::Narrow; - _map[UnicodeRange(8457, 8457)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8458, 8466)] = CodepointWidth::Narrow; - _map[UnicodeRange(8467, 8467)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8468, 8469)] = CodepointWidth::Narrow; - _map[UnicodeRange(8470, 8470)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8471, 8480)] = CodepointWidth::Narrow; - _map[UnicodeRange(8481, 8482)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8483, 8485)] = CodepointWidth::Narrow; - _map[UnicodeRange(8486, 8486)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8487, 8490)] = CodepointWidth::Narrow; - _map[UnicodeRange(8491, 8491)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8492, 8530)] = CodepointWidth::Narrow; - _map[UnicodeRange(8531, 8532)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8533, 8538)] = CodepointWidth::Narrow; - _map[UnicodeRange(8539, 8542)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8543, 8543)] = CodepointWidth::Narrow; - _map[UnicodeRange(8544, 8555)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8556, 8559)] = CodepointWidth::Narrow; - _map[UnicodeRange(8560, 8569)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8570, 8584)] = CodepointWidth::Narrow; - _map[UnicodeRange(8585, 8585)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8586, 8587)] = CodepointWidth::Narrow; - _map[UnicodeRange(8592, 8601)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8602, 8631)] = CodepointWidth::Narrow; - _map[UnicodeRange(8632, 8633)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8634, 8657)] = CodepointWidth::Narrow; - _map[UnicodeRange(8658, 8658)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8659, 8659)] = CodepointWidth::Narrow; - _map[UnicodeRange(8660, 8660)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8661, 8678)] = CodepointWidth::Narrow; - _map[UnicodeRange(8679, 8679)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8680, 8703)] = CodepointWidth::Narrow; - _map[UnicodeRange(8704, 8704)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8705, 8705)] = CodepointWidth::Narrow; - _map[UnicodeRange(8706, 8707)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8708, 8710)] = CodepointWidth::Narrow; - _map[UnicodeRange(8711, 8712)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8713, 8714)] = CodepointWidth::Narrow; - _map[UnicodeRange(8715, 8715)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8716, 8718)] = CodepointWidth::Narrow; - _map[UnicodeRange(8719, 8719)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8720, 8720)] = CodepointWidth::Narrow; - _map[UnicodeRange(8721, 8721)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8722, 8724)] = CodepointWidth::Narrow; - _map[UnicodeRange(8725, 8725)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8726, 8729)] = CodepointWidth::Narrow; - _map[UnicodeRange(8730, 8730)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8731, 8732)] = CodepointWidth::Narrow; - _map[UnicodeRange(8733, 8736)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8737, 8738)] = CodepointWidth::Narrow; - _map[UnicodeRange(8739, 8739)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8740, 8740)] = CodepointWidth::Narrow; - _map[UnicodeRange(8741, 8741)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8742, 8742)] = CodepointWidth::Narrow; - _map[UnicodeRange(8743, 8748)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8749, 8749)] = CodepointWidth::Narrow; - _map[UnicodeRange(8750, 8750)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8751, 8755)] = CodepointWidth::Narrow; - _map[UnicodeRange(8756, 8759)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8760, 8763)] = CodepointWidth::Narrow; - _map[UnicodeRange(8764, 8765)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8766, 8775)] = CodepointWidth::Narrow; - _map[UnicodeRange(8776, 8776)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8777, 8779)] = CodepointWidth::Narrow; - _map[UnicodeRange(8780, 8780)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8781, 8785)] = CodepointWidth::Narrow; - _map[UnicodeRange(8786, 8786)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8787, 8799)] = CodepointWidth::Narrow; - _map[UnicodeRange(8800, 8801)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8802, 8803)] = CodepointWidth::Narrow; - _map[UnicodeRange(8804, 8807)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8808, 8809)] = CodepointWidth::Narrow; - _map[UnicodeRange(8810, 8811)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8812, 8813)] = CodepointWidth::Narrow; - _map[UnicodeRange(8814, 8815)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8816, 8833)] = CodepointWidth::Narrow; - _map[UnicodeRange(8834, 8835)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8836, 8837)] = CodepointWidth::Narrow; - _map[UnicodeRange(8838, 8839)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8840, 8852)] = CodepointWidth::Narrow; - _map[UnicodeRange(8853, 8853)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8854, 8856)] = CodepointWidth::Narrow; - _map[UnicodeRange(8857, 8857)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8858, 8868)] = CodepointWidth::Narrow; - _map[UnicodeRange(8869, 8869)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8870, 8894)] = CodepointWidth::Narrow; - _map[UnicodeRange(8895, 8895)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8896, 8977)] = CodepointWidth::Narrow; - _map[UnicodeRange(8978, 8978)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(8979, 8985)] = CodepointWidth::Narrow; - _map[UnicodeRange(8986, 8987)] = CodepointWidth::Wide; - _map[UnicodeRange(8988, 9000)] = CodepointWidth::Narrow; - _map[UnicodeRange(9001, 9002)] = CodepointWidth::Wide; - _map[UnicodeRange(9003, 9192)] = CodepointWidth::Narrow; - _map[UnicodeRange(9193, 9196)] = CodepointWidth::Wide; - _map[UnicodeRange(9197, 9199)] = CodepointWidth::Narrow; - _map[UnicodeRange(9200, 9200)] = CodepointWidth::Wide; - _map[UnicodeRange(9201, 9202)] = CodepointWidth::Narrow; - _map[UnicodeRange(9203, 9203)] = CodepointWidth::Wide; - _map[UnicodeRange(9204, 9254)] = CodepointWidth::Narrow; - _map[UnicodeRange(9280, 9290)] = CodepointWidth::Narrow; - _map[UnicodeRange(9312, 9449)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9450, 9450)] = CodepointWidth::Narrow; - _map[UnicodeRange(9451, 9547)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9548, 9551)] = CodepointWidth::Narrow; - _map[UnicodeRange(9552, 9587)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9588, 9599)] = CodepointWidth::Narrow; - _map[UnicodeRange(9600, 9615)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9616, 9617)] = CodepointWidth::Narrow; - _map[UnicodeRange(9618, 9621)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9622, 9631)] = CodepointWidth::Narrow; - _map[UnicodeRange(9632, 9633)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9634, 9634)] = CodepointWidth::Narrow; - _map[UnicodeRange(9635, 9641)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9642, 9649)] = CodepointWidth::Narrow; - _map[UnicodeRange(9650, 9651)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9652, 9653)] = CodepointWidth::Narrow; - _map[UnicodeRange(9654, 9655)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9656, 9659)] = CodepointWidth::Narrow; - _map[UnicodeRange(9660, 9661)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9662, 9663)] = CodepointWidth::Narrow; - _map[UnicodeRange(9664, 9665)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9666, 9669)] = CodepointWidth::Narrow; - _map[UnicodeRange(9670, 9672)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9673, 9674)] = CodepointWidth::Narrow; - _map[UnicodeRange(9675, 9675)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9676, 9677)] = CodepointWidth::Narrow; - _map[UnicodeRange(9678, 9681)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9682, 9697)] = CodepointWidth::Narrow; - _map[UnicodeRange(9698, 9701)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9702, 9710)] = CodepointWidth::Narrow; - _map[UnicodeRange(9711, 9711)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9712, 9724)] = CodepointWidth::Narrow; - _map[UnicodeRange(9725, 9726)] = CodepointWidth::Wide; - _map[UnicodeRange(9727, 9732)] = CodepointWidth::Narrow; - _map[UnicodeRange(9733, 9734)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9735, 9736)] = CodepointWidth::Narrow; - _map[UnicodeRange(9737, 9737)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9738, 9741)] = CodepointWidth::Narrow; - _map[UnicodeRange(9742, 9743)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9744, 9747)] = CodepointWidth::Narrow; - _map[UnicodeRange(9748, 9749)] = CodepointWidth::Wide; - _map[UnicodeRange(9750, 9755)] = CodepointWidth::Narrow; - _map[UnicodeRange(9756, 9756)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9757, 9757)] = CodepointWidth::Narrow; - _map[UnicodeRange(9758, 9758)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9759, 9791)] = CodepointWidth::Narrow; - _map[UnicodeRange(9792, 9792)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9793, 9793)] = CodepointWidth::Narrow; - _map[UnicodeRange(9794, 9794)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9795, 9799)] = CodepointWidth::Narrow; - _map[UnicodeRange(9800, 9811)] = CodepointWidth::Wide; - _map[UnicodeRange(9812, 9823)] = CodepointWidth::Narrow; - _map[UnicodeRange(9824, 9825)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9826, 9826)] = CodepointWidth::Narrow; - _map[UnicodeRange(9827, 9829)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9830, 9830)] = CodepointWidth::Narrow; - _map[UnicodeRange(9831, 9834)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9835, 9835)] = CodepointWidth::Narrow; - _map[UnicodeRange(9836, 9837)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9838, 9838)] = CodepointWidth::Narrow; - _map[UnicodeRange(9839, 9839)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9840, 9854)] = CodepointWidth::Narrow; - _map[UnicodeRange(9855, 9855)] = CodepointWidth::Wide; - _map[UnicodeRange(9856, 9874)] = CodepointWidth::Narrow; - _map[UnicodeRange(9875, 9875)] = CodepointWidth::Wide; - _map[UnicodeRange(9876, 9885)] = CodepointWidth::Narrow; - _map[UnicodeRange(9886, 9887)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9888, 9888)] = CodepointWidth::Narrow; - _map[UnicodeRange(9889, 9889)] = CodepointWidth::Wide; - _map[UnicodeRange(9890, 9897)] = CodepointWidth::Narrow; - _map[UnicodeRange(9898, 9899)] = CodepointWidth::Wide; - _map[UnicodeRange(9900, 9916)] = CodepointWidth::Narrow; - _map[UnicodeRange(9917, 9918)] = CodepointWidth::Wide; - _map[UnicodeRange(9919, 9919)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9920, 9923)] = CodepointWidth::Narrow; - _map[UnicodeRange(9924, 9925)] = CodepointWidth::Wide; - _map[UnicodeRange(9926, 9933)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9934, 9934)] = CodepointWidth::Wide; - _map[UnicodeRange(9935, 9939)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9940, 9940)] = CodepointWidth::Wide; - _map[UnicodeRange(9941, 9953)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9954, 9954)] = CodepointWidth::Narrow; - _map[UnicodeRange(9955, 9955)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9956, 9959)] = CodepointWidth::Narrow; - _map[UnicodeRange(9960, 9961)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9962, 9962)] = CodepointWidth::Wide; - _map[UnicodeRange(9963, 9969)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9970, 9971)] = CodepointWidth::Wide; - _map[UnicodeRange(9972, 9972)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9973, 9973)] = CodepointWidth::Wide; - _map[UnicodeRange(9974, 9977)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9978, 9978)] = CodepointWidth::Wide; - _map[UnicodeRange(9979, 9980)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9981, 9981)] = CodepointWidth::Wide; - _map[UnicodeRange(9982, 9983)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(9984, 9988)] = CodepointWidth::Narrow; - _map[UnicodeRange(9989, 9989)] = CodepointWidth::Wide; - _map[UnicodeRange(9990, 9993)] = CodepointWidth::Narrow; - _map[UnicodeRange(9994, 9995)] = CodepointWidth::Wide; - _map[UnicodeRange(9996, 10023)] = CodepointWidth::Narrow; - _map[UnicodeRange(10024, 10024)] = CodepointWidth::Wide; - _map[UnicodeRange(10025, 10044)] = CodepointWidth::Narrow; - _map[UnicodeRange(10045, 10045)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(10046, 10059)] = CodepointWidth::Narrow; - _map[UnicodeRange(10060, 10060)] = CodepointWidth::Wide; - _map[UnicodeRange(10061, 10061)] = CodepointWidth::Narrow; - _map[UnicodeRange(10062, 10062)] = CodepointWidth::Wide; - _map[UnicodeRange(10063, 10066)] = CodepointWidth::Narrow; - _map[UnicodeRange(10067, 10069)] = CodepointWidth::Wide; - _map[UnicodeRange(10070, 10070)] = CodepointWidth::Narrow; - _map[UnicodeRange(10071, 10071)] = CodepointWidth::Wide; - _map[UnicodeRange(10072, 10101)] = CodepointWidth::Narrow; - _map[UnicodeRange(10102, 10111)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(10112, 10132)] = CodepointWidth::Narrow; - _map[UnicodeRange(10133, 10135)] = CodepointWidth::Wide; - _map[UnicodeRange(10136, 10159)] = CodepointWidth::Narrow; - _map[UnicodeRange(10160, 10160)] = CodepointWidth::Wide; - _map[UnicodeRange(10161, 10174)] = CodepointWidth::Narrow; - _map[UnicodeRange(10175, 10175)] = CodepointWidth::Wide; - _map[UnicodeRange(10176, 11034)] = CodepointWidth::Narrow; - _map[UnicodeRange(11035, 11036)] = CodepointWidth::Wide; - _map[UnicodeRange(11037, 11087)] = CodepointWidth::Narrow; - _map[UnicodeRange(11088, 11088)] = CodepointWidth::Wide; - _map[UnicodeRange(11089, 11092)] = CodepointWidth::Narrow; - _map[UnicodeRange(11093, 11093)] = CodepointWidth::Wide; - _map[UnicodeRange(11094, 11097)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(11098, 11123)] = CodepointWidth::Narrow; - _map[UnicodeRange(11126, 11157)] = CodepointWidth::Narrow; - _map[UnicodeRange(11160, 11193)] = CodepointWidth::Narrow; - _map[UnicodeRange(11197, 11208)] = CodepointWidth::Narrow; - _map[UnicodeRange(11210, 11218)] = CodepointWidth::Narrow; - _map[UnicodeRange(11244, 11247)] = CodepointWidth::Narrow; - _map[UnicodeRange(11264, 11310)] = CodepointWidth::Narrow; - _map[UnicodeRange(11312, 11358)] = CodepointWidth::Narrow; - _map[UnicodeRange(11360, 11507)] = CodepointWidth::Narrow; - _map[UnicodeRange(11513, 11557)] = CodepointWidth::Narrow; - _map[UnicodeRange(11559, 11559)] = CodepointWidth::Narrow; - _map[UnicodeRange(11565, 11565)] = CodepointWidth::Narrow; - _map[UnicodeRange(11568, 11623)] = CodepointWidth::Narrow; - _map[UnicodeRange(11631, 11632)] = CodepointWidth::Narrow; - _map[UnicodeRange(11647, 11670)] = CodepointWidth::Narrow; - _map[UnicodeRange(11680, 11686)] = CodepointWidth::Narrow; - _map[UnicodeRange(11688, 11694)] = CodepointWidth::Narrow; - _map[UnicodeRange(11696, 11702)] = CodepointWidth::Narrow; - _map[UnicodeRange(11704, 11710)] = CodepointWidth::Narrow; - _map[UnicodeRange(11712, 11718)] = CodepointWidth::Narrow; - _map[UnicodeRange(11720, 11726)] = CodepointWidth::Narrow; - _map[UnicodeRange(11728, 11734)] = CodepointWidth::Narrow; - _map[UnicodeRange(11736, 11742)] = CodepointWidth::Narrow; - _map[UnicodeRange(11744, 11849)] = CodepointWidth::Narrow; - _map[UnicodeRange(11904, 11929)] = CodepointWidth::Wide; - _map[UnicodeRange(11931, 12019)] = CodepointWidth::Wide; - _map[UnicodeRange(12032, 12245)] = CodepointWidth::Wide; - _map[UnicodeRange(12272, 12283)] = CodepointWidth::Wide; - _map[UnicodeRange(12288, 12350)] = CodepointWidth::Wide; - _map[UnicodeRange(12351, 12351)] = CodepointWidth::Narrow; - _map[UnicodeRange(12353, 12438)] = CodepointWidth::Wide; - _map[UnicodeRange(12441, 12543)] = CodepointWidth::Wide; - _map[UnicodeRange(12549, 12590)] = CodepointWidth::Wide; - _map[UnicodeRange(12593, 12686)] = CodepointWidth::Wide; - _map[UnicodeRange(12688, 12730)] = CodepointWidth::Wide; - _map[UnicodeRange(12736, 12771)] = CodepointWidth::Wide; - _map[UnicodeRange(12784, 12830)] = CodepointWidth::Wide; - _map[UnicodeRange(12832, 12871)] = CodepointWidth::Wide; - _map[UnicodeRange(12872, 12879)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(12880, 13054)] = CodepointWidth::Wide; - _map[UnicodeRange(13056, 19903)] = CodepointWidth::Wide; - _map[UnicodeRange(19904, 19967)] = CodepointWidth::Narrow; - _map[UnicodeRange(19968, 42124)] = CodepointWidth::Wide; - _map[UnicodeRange(42128, 42182)] = CodepointWidth::Wide; - _map[UnicodeRange(42192, 42539)] = CodepointWidth::Narrow; - _map[UnicodeRange(42560, 42743)] = CodepointWidth::Narrow; - _map[UnicodeRange(42752, 42926)] = CodepointWidth::Narrow; - _map[UnicodeRange(42928, 42935)] = CodepointWidth::Narrow; - _map[UnicodeRange(42999, 43051)] = CodepointWidth::Narrow; - _map[UnicodeRange(43056, 43065)] = CodepointWidth::Narrow; - _map[UnicodeRange(43072, 43127)] = CodepointWidth::Narrow; - _map[UnicodeRange(43136, 43205)] = CodepointWidth::Narrow; - _map[UnicodeRange(43214, 43225)] = CodepointWidth::Narrow; - _map[UnicodeRange(43232, 43261)] = CodepointWidth::Narrow; - _map[UnicodeRange(43264, 43347)] = CodepointWidth::Narrow; - _map[UnicodeRange(43359, 43359)] = CodepointWidth::Narrow; - _map[UnicodeRange(43360, 43388)] = CodepointWidth::Wide; - _map[UnicodeRange(43392, 43469)] = CodepointWidth::Narrow; - _map[UnicodeRange(43471, 43481)] = CodepointWidth::Narrow; - _map[UnicodeRange(43486, 43518)] = CodepointWidth::Narrow; - _map[UnicodeRange(43520, 43574)] = CodepointWidth::Narrow; - _map[UnicodeRange(43584, 43597)] = CodepointWidth::Narrow; - _map[UnicodeRange(43600, 43609)] = CodepointWidth::Narrow; - _map[UnicodeRange(43612, 43714)] = CodepointWidth::Narrow; - _map[UnicodeRange(43739, 43766)] = CodepointWidth::Narrow; - _map[UnicodeRange(43777, 43782)] = CodepointWidth::Narrow; - _map[UnicodeRange(43785, 43790)] = CodepointWidth::Narrow; - _map[UnicodeRange(43793, 43798)] = CodepointWidth::Narrow; - _map[UnicodeRange(43808, 43814)] = CodepointWidth::Narrow; - _map[UnicodeRange(43816, 43822)] = CodepointWidth::Narrow; - _map[UnicodeRange(43824, 43877)] = CodepointWidth::Narrow; - _map[UnicodeRange(43888, 44013)] = CodepointWidth::Narrow; - _map[UnicodeRange(44016, 44025)] = CodepointWidth::Narrow; - _map[UnicodeRange(44032, 55203)] = CodepointWidth::Wide; - _map[UnicodeRange(55216, 55238)] = CodepointWidth::Narrow; - _map[UnicodeRange(55243, 55291)] = CodepointWidth::Narrow; - _map[UnicodeRange(55296, 57343)] = CodepointWidth::Narrow; - _map[UnicodeRange(57344, 63743)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(63744, 64255)] = CodepointWidth::Wide; - _map[UnicodeRange(64256, 64262)] = CodepointWidth::Narrow; - _map[UnicodeRange(64275, 64279)] = CodepointWidth::Narrow; - _map[UnicodeRange(64285, 64310)] = CodepointWidth::Narrow; - _map[UnicodeRange(64312, 64316)] = CodepointWidth::Narrow; - _map[UnicodeRange(64318, 64318)] = CodepointWidth::Narrow; - _map[UnicodeRange(64320, 64321)] = CodepointWidth::Narrow; - _map[UnicodeRange(64323, 64324)] = CodepointWidth::Narrow; - _map[UnicodeRange(64326, 64449)] = CodepointWidth::Narrow; - _map[UnicodeRange(64467, 64831)] = CodepointWidth::Narrow; - _map[UnicodeRange(64848, 64911)] = CodepointWidth::Narrow; - _map[UnicodeRange(64914, 64967)] = CodepointWidth::Narrow; - _map[UnicodeRange(65008, 65021)] = CodepointWidth::Narrow; - _map[UnicodeRange(65024, 65039)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(65040, 65049)] = CodepointWidth::Wide; - _map[UnicodeRange(65056, 65071)] = CodepointWidth::Narrow; - _map[UnicodeRange(65072, 65106)] = CodepointWidth::Wide; - _map[UnicodeRange(65108, 65126)] = CodepointWidth::Wide; - _map[UnicodeRange(65128, 65131)] = CodepointWidth::Wide; - _map[UnicodeRange(65136, 65140)] = CodepointWidth::Narrow; - _map[UnicodeRange(65142, 65276)] = CodepointWidth::Narrow; - _map[UnicodeRange(65279, 65279)] = CodepointWidth::Narrow; - _map[UnicodeRange(65281, 65376)] = CodepointWidth::Wide; - _map[UnicodeRange(65377, 65470)] = CodepointWidth::Narrow; - _map[UnicodeRange(65474, 65479)] = CodepointWidth::Narrow; - _map[UnicodeRange(65482, 65487)] = CodepointWidth::Narrow; - _map[UnicodeRange(65490, 65495)] = CodepointWidth::Narrow; - _map[UnicodeRange(65498, 65500)] = CodepointWidth::Narrow; - _map[UnicodeRange(65504, 65510)] = CodepointWidth::Wide; - _map[UnicodeRange(65512, 65518)] = CodepointWidth::Narrow; - _map[UnicodeRange(65529, 65532)] = CodepointWidth::Narrow; - _map[UnicodeRange(65533, 65533)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(65536, 65547)] = CodepointWidth::Narrow; - _map[UnicodeRange(65549, 65574)] = CodepointWidth::Narrow; - _map[UnicodeRange(65576, 65594)] = CodepointWidth::Narrow; - _map[UnicodeRange(65596, 65597)] = CodepointWidth::Narrow; - _map[UnicodeRange(65599, 65613)] = CodepointWidth::Narrow; - _map[UnicodeRange(65616, 65629)] = CodepointWidth::Narrow; - _map[UnicodeRange(65664, 65786)] = CodepointWidth::Narrow; - _map[UnicodeRange(65792, 65794)] = CodepointWidth::Narrow; - _map[UnicodeRange(65799, 65843)] = CodepointWidth::Narrow; - _map[UnicodeRange(65847, 65934)] = CodepointWidth::Narrow; - _map[UnicodeRange(65936, 65947)] = CodepointWidth::Narrow; - _map[UnicodeRange(65952, 65952)] = CodepointWidth::Narrow; - _map[UnicodeRange(66000, 66045)] = CodepointWidth::Narrow; - _map[UnicodeRange(66176, 66204)] = CodepointWidth::Narrow; - _map[UnicodeRange(66208, 66256)] = CodepointWidth::Narrow; - _map[UnicodeRange(66272, 66299)] = CodepointWidth::Narrow; - _map[UnicodeRange(66304, 66339)] = CodepointWidth::Narrow; - _map[UnicodeRange(66349, 66378)] = CodepointWidth::Narrow; - _map[UnicodeRange(66384, 66426)] = CodepointWidth::Narrow; - _map[UnicodeRange(66432, 66461)] = CodepointWidth::Narrow; - _map[UnicodeRange(66463, 66499)] = CodepointWidth::Narrow; - _map[UnicodeRange(66504, 66517)] = CodepointWidth::Narrow; - _map[UnicodeRange(66560, 66717)] = CodepointWidth::Narrow; - _map[UnicodeRange(66720, 66729)] = CodepointWidth::Narrow; - _map[UnicodeRange(66736, 66771)] = CodepointWidth::Narrow; - _map[UnicodeRange(66776, 66811)] = CodepointWidth::Narrow; - _map[UnicodeRange(66816, 66855)] = CodepointWidth::Narrow; - _map[UnicodeRange(66864, 66915)] = CodepointWidth::Narrow; - _map[UnicodeRange(66927, 66927)] = CodepointWidth::Narrow; - _map[UnicodeRange(67072, 67382)] = CodepointWidth::Narrow; - _map[UnicodeRange(67392, 67413)] = CodepointWidth::Narrow; - _map[UnicodeRange(67424, 67431)] = CodepointWidth::Narrow; - _map[UnicodeRange(67584, 67589)] = CodepointWidth::Narrow; - _map[UnicodeRange(67592, 67592)] = CodepointWidth::Narrow; - _map[UnicodeRange(67594, 67637)] = CodepointWidth::Narrow; - _map[UnicodeRange(67639, 67640)] = CodepointWidth::Narrow; - _map[UnicodeRange(67644, 67644)] = CodepointWidth::Narrow; - _map[UnicodeRange(67647, 67669)] = CodepointWidth::Narrow; - _map[UnicodeRange(67671, 67742)] = CodepointWidth::Narrow; - _map[UnicodeRange(67751, 67759)] = CodepointWidth::Narrow; - _map[UnicodeRange(67808, 67826)] = CodepointWidth::Narrow; - _map[UnicodeRange(67828, 67829)] = CodepointWidth::Narrow; - _map[UnicodeRange(67835, 67867)] = CodepointWidth::Narrow; - _map[UnicodeRange(67871, 67897)] = CodepointWidth::Narrow; - _map[UnicodeRange(67903, 67903)] = CodepointWidth::Narrow; - _map[UnicodeRange(67968, 68023)] = CodepointWidth::Narrow; - _map[UnicodeRange(68028, 68047)] = CodepointWidth::Narrow; - _map[UnicodeRange(68050, 68099)] = CodepointWidth::Narrow; - _map[UnicodeRange(68101, 68102)] = CodepointWidth::Narrow; - _map[UnicodeRange(68108, 68115)] = CodepointWidth::Narrow; - _map[UnicodeRange(68117, 68119)] = CodepointWidth::Narrow; - _map[UnicodeRange(68121, 68147)] = CodepointWidth::Narrow; - _map[UnicodeRange(68152, 68154)] = CodepointWidth::Narrow; - _map[UnicodeRange(68159, 68167)] = CodepointWidth::Narrow; - _map[UnicodeRange(68176, 68184)] = CodepointWidth::Narrow; - _map[UnicodeRange(68192, 68255)] = CodepointWidth::Narrow; - _map[UnicodeRange(68288, 68326)] = CodepointWidth::Narrow; - _map[UnicodeRange(68331, 68342)] = CodepointWidth::Narrow; - _map[UnicodeRange(68352, 68405)] = CodepointWidth::Narrow; - _map[UnicodeRange(68409, 68437)] = CodepointWidth::Narrow; - _map[UnicodeRange(68440, 68466)] = CodepointWidth::Narrow; - _map[UnicodeRange(68472, 68497)] = CodepointWidth::Narrow; - _map[UnicodeRange(68505, 68508)] = CodepointWidth::Narrow; - _map[UnicodeRange(68521, 68527)] = CodepointWidth::Narrow; - _map[UnicodeRange(68608, 68680)] = CodepointWidth::Narrow; - _map[UnicodeRange(68736, 68786)] = CodepointWidth::Narrow; - _map[UnicodeRange(68800, 68850)] = CodepointWidth::Narrow; - _map[UnicodeRange(68858, 68863)] = CodepointWidth::Narrow; - _map[UnicodeRange(69216, 69246)] = CodepointWidth::Narrow; - _map[UnicodeRange(69632, 69709)] = CodepointWidth::Narrow; - _map[UnicodeRange(69714, 69743)] = CodepointWidth::Narrow; - _map[UnicodeRange(69759, 69825)] = CodepointWidth::Narrow; - _map[UnicodeRange(69840, 69864)] = CodepointWidth::Narrow; - _map[UnicodeRange(69872, 69881)] = CodepointWidth::Narrow; - _map[UnicodeRange(69888, 69940)] = CodepointWidth::Narrow; - _map[UnicodeRange(69942, 69955)] = CodepointWidth::Narrow; - _map[UnicodeRange(69968, 70006)] = CodepointWidth::Narrow; - _map[UnicodeRange(70016, 70093)] = CodepointWidth::Narrow; - _map[UnicodeRange(70096, 70111)] = CodepointWidth::Narrow; - _map[UnicodeRange(70113, 70132)] = CodepointWidth::Narrow; - _map[UnicodeRange(70144, 70161)] = CodepointWidth::Narrow; - _map[UnicodeRange(70163, 70206)] = CodepointWidth::Narrow; - _map[UnicodeRange(70272, 70278)] = CodepointWidth::Narrow; - _map[UnicodeRange(70280, 70280)] = CodepointWidth::Narrow; - _map[UnicodeRange(70282, 70285)] = CodepointWidth::Narrow; - _map[UnicodeRange(70287, 70301)] = CodepointWidth::Narrow; - _map[UnicodeRange(70303, 70313)] = CodepointWidth::Narrow; - _map[UnicodeRange(70320, 70378)] = CodepointWidth::Narrow; - _map[UnicodeRange(70384, 70393)] = CodepointWidth::Narrow; - _map[UnicodeRange(70400, 70403)] = CodepointWidth::Narrow; - _map[UnicodeRange(70405, 70412)] = CodepointWidth::Narrow; - _map[UnicodeRange(70415, 70416)] = CodepointWidth::Narrow; - _map[UnicodeRange(70419, 70440)] = CodepointWidth::Narrow; - _map[UnicodeRange(70442, 70448)] = CodepointWidth::Narrow; - _map[UnicodeRange(70450, 70451)] = CodepointWidth::Narrow; - _map[UnicodeRange(70453, 70457)] = CodepointWidth::Narrow; - _map[UnicodeRange(70460, 70468)] = CodepointWidth::Narrow; - _map[UnicodeRange(70471, 70472)] = CodepointWidth::Narrow; - _map[UnicodeRange(70475, 70477)] = CodepointWidth::Narrow; - _map[UnicodeRange(70480, 70480)] = CodepointWidth::Narrow; - _map[UnicodeRange(70487, 70487)] = CodepointWidth::Narrow; - _map[UnicodeRange(70493, 70499)] = CodepointWidth::Narrow; - _map[UnicodeRange(70502, 70508)] = CodepointWidth::Narrow; - _map[UnicodeRange(70512, 70516)] = CodepointWidth::Narrow; - _map[UnicodeRange(70656, 70745)] = CodepointWidth::Narrow; - _map[UnicodeRange(70747, 70747)] = CodepointWidth::Narrow; - _map[UnicodeRange(70749, 70749)] = CodepointWidth::Narrow; - _map[UnicodeRange(70784, 70855)] = CodepointWidth::Narrow; - _map[UnicodeRange(70864, 70873)] = CodepointWidth::Narrow; - _map[UnicodeRange(71040, 71093)] = CodepointWidth::Narrow; - _map[UnicodeRange(71096, 71133)] = CodepointWidth::Narrow; - _map[UnicodeRange(71168, 71236)] = CodepointWidth::Narrow; - _map[UnicodeRange(71248, 71257)] = CodepointWidth::Narrow; - _map[UnicodeRange(71264, 71276)] = CodepointWidth::Narrow; - _map[UnicodeRange(71296, 71351)] = CodepointWidth::Narrow; - _map[UnicodeRange(71360, 71369)] = CodepointWidth::Narrow; - _map[UnicodeRange(71424, 71449)] = CodepointWidth::Narrow; - _map[UnicodeRange(71453, 71467)] = CodepointWidth::Narrow; - _map[UnicodeRange(71472, 71487)] = CodepointWidth::Narrow; - _map[UnicodeRange(71840, 71922)] = CodepointWidth::Narrow; - _map[UnicodeRange(71935, 71935)] = CodepointWidth::Narrow; - _map[UnicodeRange(72192, 72263)] = CodepointWidth::Narrow; - _map[UnicodeRange(72272, 72323)] = CodepointWidth::Narrow; - _map[UnicodeRange(72326, 72348)] = CodepointWidth::Narrow; - _map[UnicodeRange(72350, 72354)] = CodepointWidth::Narrow; - _map[UnicodeRange(72384, 72440)] = CodepointWidth::Narrow; - _map[UnicodeRange(72704, 72712)] = CodepointWidth::Narrow; - _map[UnicodeRange(72714, 72758)] = CodepointWidth::Narrow; - _map[UnicodeRange(72760, 72773)] = CodepointWidth::Narrow; - _map[UnicodeRange(72784, 72812)] = CodepointWidth::Narrow; - _map[UnicodeRange(72816, 72847)] = CodepointWidth::Narrow; - _map[UnicodeRange(72850, 72871)] = CodepointWidth::Narrow; - _map[UnicodeRange(72873, 72886)] = CodepointWidth::Narrow; - _map[UnicodeRange(72960, 72966)] = CodepointWidth::Narrow; - _map[UnicodeRange(72968, 72969)] = CodepointWidth::Narrow; - _map[UnicodeRange(72971, 73014)] = CodepointWidth::Narrow; - _map[UnicodeRange(73018, 73018)] = CodepointWidth::Narrow; - _map[UnicodeRange(73020, 73021)] = CodepointWidth::Narrow; - _map[UnicodeRange(73023, 73031)] = CodepointWidth::Narrow; - _map[UnicodeRange(73040, 73049)] = CodepointWidth::Narrow; - _map[UnicodeRange(73728, 74649)] = CodepointWidth::Narrow; - _map[UnicodeRange(74752, 74862)] = CodepointWidth::Narrow; - _map[UnicodeRange(74864, 74868)] = CodepointWidth::Narrow; - _map[UnicodeRange(74880, 75075)] = CodepointWidth::Narrow; - _map[UnicodeRange(77824, 78894)] = CodepointWidth::Narrow; - _map[UnicodeRange(82944, 83526)] = CodepointWidth::Narrow; - _map[UnicodeRange(92160, 92728)] = CodepointWidth::Narrow; - _map[UnicodeRange(92736, 92766)] = CodepointWidth::Narrow; - _map[UnicodeRange(92768, 92777)] = CodepointWidth::Narrow; - _map[UnicodeRange(92782, 92783)] = CodepointWidth::Narrow; - _map[UnicodeRange(92880, 92909)] = CodepointWidth::Narrow; - _map[UnicodeRange(92912, 92917)] = CodepointWidth::Narrow; - _map[UnicodeRange(92928, 92997)] = CodepointWidth::Narrow; - _map[UnicodeRange(93008, 93017)] = CodepointWidth::Narrow; - _map[UnicodeRange(93019, 93025)] = CodepointWidth::Narrow; - _map[UnicodeRange(93027, 93047)] = CodepointWidth::Narrow; - _map[UnicodeRange(93053, 93071)] = CodepointWidth::Narrow; - _map[UnicodeRange(93952, 94020)] = CodepointWidth::Narrow; - _map[UnicodeRange(94032, 94078)] = CodepointWidth::Narrow; - _map[UnicodeRange(94095, 94111)] = CodepointWidth::Narrow; - _map[UnicodeRange(94176, 94177)] = CodepointWidth::Wide; - _map[UnicodeRange(94208, 100332)] = CodepointWidth::Wide; - _map[UnicodeRange(100352, 101106)] = CodepointWidth::Wide; - _map[UnicodeRange(110592, 110878)] = CodepointWidth::Wide; - _map[UnicodeRange(110960, 111355)] = CodepointWidth::Wide; - _map[UnicodeRange(113664, 113770)] = CodepointWidth::Narrow; - _map[UnicodeRange(113776, 113788)] = CodepointWidth::Narrow; - _map[UnicodeRange(113792, 113800)] = CodepointWidth::Narrow; - _map[UnicodeRange(113808, 113817)] = CodepointWidth::Narrow; - _map[UnicodeRange(113820, 113827)] = CodepointWidth::Narrow; - _map[UnicodeRange(118784, 119029)] = CodepointWidth::Narrow; - _map[UnicodeRange(119040, 119078)] = CodepointWidth::Narrow; - _map[UnicodeRange(119081, 119272)] = CodepointWidth::Narrow; - _map[UnicodeRange(119296, 119365)] = CodepointWidth::Narrow; - _map[UnicodeRange(119552, 119638)] = CodepointWidth::Narrow; - _map[UnicodeRange(119648, 119665)] = CodepointWidth::Narrow; - _map[UnicodeRange(119808, 119892)] = CodepointWidth::Narrow; - _map[UnicodeRange(119894, 119964)] = CodepointWidth::Narrow; - _map[UnicodeRange(119966, 119967)] = CodepointWidth::Narrow; - _map[UnicodeRange(119970, 119970)] = CodepointWidth::Narrow; - _map[UnicodeRange(119973, 119974)] = CodepointWidth::Narrow; - _map[UnicodeRange(119977, 119980)] = CodepointWidth::Narrow; - _map[UnicodeRange(119982, 119993)] = CodepointWidth::Narrow; - _map[UnicodeRange(119995, 119995)] = CodepointWidth::Narrow; - _map[UnicodeRange(119997, 120003)] = CodepointWidth::Narrow; - _map[UnicodeRange(120005, 120069)] = CodepointWidth::Narrow; - _map[UnicodeRange(120071, 120074)] = CodepointWidth::Narrow; - _map[UnicodeRange(120077, 120084)] = CodepointWidth::Narrow; - _map[UnicodeRange(120086, 120092)] = CodepointWidth::Narrow; - _map[UnicodeRange(120094, 120121)] = CodepointWidth::Narrow; - _map[UnicodeRange(120123, 120126)] = CodepointWidth::Narrow; - _map[UnicodeRange(120128, 120132)] = CodepointWidth::Narrow; - _map[UnicodeRange(120134, 120134)] = CodepointWidth::Narrow; - _map[UnicodeRange(120138, 120144)] = CodepointWidth::Narrow; - _map[UnicodeRange(120146, 120485)] = CodepointWidth::Narrow; - _map[UnicodeRange(120488, 120779)] = CodepointWidth::Narrow; - _map[UnicodeRange(120782, 121483)] = CodepointWidth::Narrow; - _map[UnicodeRange(121499, 121503)] = CodepointWidth::Narrow; - _map[UnicodeRange(121505, 121519)] = CodepointWidth::Narrow; - _map[UnicodeRange(122880, 122886)] = CodepointWidth::Narrow; - _map[UnicodeRange(122888, 122904)] = CodepointWidth::Narrow; - _map[UnicodeRange(122907, 122913)] = CodepointWidth::Narrow; - _map[UnicodeRange(122915, 122916)] = CodepointWidth::Narrow; - _map[UnicodeRange(122918, 122922)] = CodepointWidth::Narrow; - _map[UnicodeRange(124928, 125124)] = CodepointWidth::Narrow; - _map[UnicodeRange(125127, 125142)] = CodepointWidth::Narrow; - _map[UnicodeRange(125184, 125258)] = CodepointWidth::Narrow; - _map[UnicodeRange(125264, 125273)] = CodepointWidth::Narrow; - _map[UnicodeRange(125278, 125279)] = CodepointWidth::Narrow; - _map[UnicodeRange(126464, 126467)] = CodepointWidth::Narrow; - _map[UnicodeRange(126469, 126495)] = CodepointWidth::Narrow; - _map[UnicodeRange(126497, 126498)] = CodepointWidth::Narrow; - _map[UnicodeRange(126500, 126500)] = CodepointWidth::Narrow; - _map[UnicodeRange(126503, 126503)] = CodepointWidth::Narrow; - _map[UnicodeRange(126505, 126514)] = CodepointWidth::Narrow; - _map[UnicodeRange(126516, 126519)] = CodepointWidth::Narrow; - _map[UnicodeRange(126521, 126521)] = CodepointWidth::Narrow; - _map[UnicodeRange(126523, 126523)] = CodepointWidth::Narrow; - _map[UnicodeRange(126530, 126530)] = CodepointWidth::Narrow; - _map[UnicodeRange(126535, 126535)] = CodepointWidth::Narrow; - _map[UnicodeRange(126537, 126537)] = CodepointWidth::Narrow; - _map[UnicodeRange(126539, 126539)] = CodepointWidth::Narrow; - _map[UnicodeRange(126541, 126543)] = CodepointWidth::Narrow; - _map[UnicodeRange(126545, 126546)] = CodepointWidth::Narrow; - _map[UnicodeRange(126548, 126548)] = CodepointWidth::Narrow; - _map[UnicodeRange(126551, 126551)] = CodepointWidth::Narrow; - _map[UnicodeRange(126553, 126553)] = CodepointWidth::Narrow; - _map[UnicodeRange(126555, 126555)] = CodepointWidth::Narrow; - _map[UnicodeRange(126557, 126557)] = CodepointWidth::Narrow; - _map[UnicodeRange(126559, 126559)] = CodepointWidth::Narrow; - _map[UnicodeRange(126561, 126562)] = CodepointWidth::Narrow; - _map[UnicodeRange(126564, 126564)] = CodepointWidth::Narrow; - _map[UnicodeRange(126567, 126570)] = CodepointWidth::Narrow; - _map[UnicodeRange(126572, 126578)] = CodepointWidth::Narrow; - _map[UnicodeRange(126580, 126583)] = CodepointWidth::Narrow; - _map[UnicodeRange(126585, 126588)] = CodepointWidth::Narrow; - _map[UnicodeRange(126590, 126590)] = CodepointWidth::Narrow; - _map[UnicodeRange(126592, 126601)] = CodepointWidth::Narrow; - _map[UnicodeRange(126603, 126619)] = CodepointWidth::Narrow; - _map[UnicodeRange(126625, 126627)] = CodepointWidth::Narrow; - _map[UnicodeRange(126629, 126633)] = CodepointWidth::Narrow; - _map[UnicodeRange(126635, 126651)] = CodepointWidth::Narrow; - _map[UnicodeRange(126704, 126705)] = CodepointWidth::Narrow; - _map[UnicodeRange(126976, 126979)] = CodepointWidth::Narrow; - _map[UnicodeRange(126980, 126980)] = CodepointWidth::Wide; - _map[UnicodeRange(126981, 127019)] = CodepointWidth::Narrow; - _map[UnicodeRange(127024, 127123)] = CodepointWidth::Narrow; - _map[UnicodeRange(127136, 127150)] = CodepointWidth::Narrow; - _map[UnicodeRange(127153, 127167)] = CodepointWidth::Narrow; - _map[UnicodeRange(127169, 127182)] = CodepointWidth::Narrow; - _map[UnicodeRange(127183, 127183)] = CodepointWidth::Wide; - _map[UnicodeRange(127185, 127221)] = CodepointWidth::Narrow; - _map[UnicodeRange(127232, 127242)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127243, 127244)] = CodepointWidth::Narrow; - _map[UnicodeRange(127248, 127277)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127278, 127278)] = CodepointWidth::Narrow; - _map[UnicodeRange(127280, 127337)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127338, 127339)] = CodepointWidth::Narrow; - _map[UnicodeRange(127344, 127373)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127374, 127374)] = CodepointWidth::Wide; - _map[UnicodeRange(127375, 127376)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127377, 127386)] = CodepointWidth::Wide; - _map[UnicodeRange(127387, 127404)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(127462, 127487)] = CodepointWidth::Narrow; - _map[UnicodeRange(127488, 127490)] = CodepointWidth::Wide; - _map[UnicodeRange(127504, 127547)] = CodepointWidth::Wide; - _map[UnicodeRange(127552, 127560)] = CodepointWidth::Wide; - _map[UnicodeRange(127568, 127569)] = CodepointWidth::Wide; - _map[UnicodeRange(127584, 127589)] = CodepointWidth::Wide; - _map[UnicodeRange(127744, 127776)] = CodepointWidth::Wide; - _map[UnicodeRange(127777, 127788)] = CodepointWidth::Narrow; - _map[UnicodeRange(127789, 127797)] = CodepointWidth::Wide; - _map[UnicodeRange(127798, 127798)] = CodepointWidth::Narrow; - _map[UnicodeRange(127799, 127868)] = CodepointWidth::Wide; - _map[UnicodeRange(127869, 127869)] = CodepointWidth::Narrow; - _map[UnicodeRange(127870, 127891)] = CodepointWidth::Wide; - _map[UnicodeRange(127892, 127903)] = CodepointWidth::Narrow; - _map[UnicodeRange(127904, 127946)] = CodepointWidth::Wide; - _map[UnicodeRange(127947, 127950)] = CodepointWidth::Narrow; - _map[UnicodeRange(127951, 127955)] = CodepointWidth::Wide; - _map[UnicodeRange(127956, 127967)] = CodepointWidth::Narrow; - _map[UnicodeRange(127968, 127984)] = CodepointWidth::Wide; - _map[UnicodeRange(127985, 127987)] = CodepointWidth::Narrow; - _map[UnicodeRange(127988, 127988)] = CodepointWidth::Wide; - _map[UnicodeRange(127989, 127991)] = CodepointWidth::Narrow; - _map[UnicodeRange(127992, 128062)] = CodepointWidth::Wide; - _map[UnicodeRange(128063, 128063)] = CodepointWidth::Narrow; - _map[UnicodeRange(128064, 128064)] = CodepointWidth::Wide; - _map[UnicodeRange(128065, 128065)] = CodepointWidth::Narrow; - _map[UnicodeRange(128066, 128252)] = CodepointWidth::Wide; - _map[UnicodeRange(128253, 128254)] = CodepointWidth::Narrow; - _map[UnicodeRange(128255, 128317)] = CodepointWidth::Wide; - _map[UnicodeRange(128318, 128330)] = CodepointWidth::Narrow; - _map[UnicodeRange(128331, 128334)] = CodepointWidth::Wide; - _map[UnicodeRange(128335, 128335)] = CodepointWidth::Narrow; - _map[UnicodeRange(128336, 128359)] = CodepointWidth::Wide; - _map[UnicodeRange(128360, 128377)] = CodepointWidth::Narrow; - _map[UnicodeRange(128378, 128378)] = CodepointWidth::Wide; - _map[UnicodeRange(128379, 128404)] = CodepointWidth::Narrow; - _map[UnicodeRange(128405, 128406)] = CodepointWidth::Wide; - _map[UnicodeRange(128407, 128419)] = CodepointWidth::Narrow; - _map[UnicodeRange(128420, 128420)] = CodepointWidth::Wide; - _map[UnicodeRange(128421, 128506)] = CodepointWidth::Narrow; - _map[UnicodeRange(128507, 128591)] = CodepointWidth::Wide; - _map[UnicodeRange(128592, 128639)] = CodepointWidth::Narrow; - _map[UnicodeRange(128640, 128709)] = CodepointWidth::Wide; - _map[UnicodeRange(128710, 128715)] = CodepointWidth::Narrow; - _map[UnicodeRange(128716, 128716)] = CodepointWidth::Wide; - _map[UnicodeRange(128717, 128719)] = CodepointWidth::Narrow; - _map[UnicodeRange(128720, 128722)] = CodepointWidth::Wide; - _map[UnicodeRange(128723, 128724)] = CodepointWidth::Narrow; - _map[UnicodeRange(128736, 128746)] = CodepointWidth::Narrow; - _map[UnicodeRange(128747, 128748)] = CodepointWidth::Wide; - _map[UnicodeRange(128752, 128755)] = CodepointWidth::Narrow; - _map[UnicodeRange(128756, 128760)] = CodepointWidth::Wide; - _map[UnicodeRange(128768, 128883)] = CodepointWidth::Narrow; - _map[UnicodeRange(128896, 128980)] = CodepointWidth::Narrow; - _map[UnicodeRange(129024, 129035)] = CodepointWidth::Narrow; - _map[UnicodeRange(129040, 129095)] = CodepointWidth::Narrow; - _map[UnicodeRange(129104, 129113)] = CodepointWidth::Narrow; - _map[UnicodeRange(129120, 129159)] = CodepointWidth::Narrow; - _map[UnicodeRange(129168, 129197)] = CodepointWidth::Narrow; - _map[UnicodeRange(129280, 129291)] = CodepointWidth::Narrow; - _map[UnicodeRange(129296, 129342)] = CodepointWidth::Wide; - _map[UnicodeRange(129344, 129356)] = CodepointWidth::Wide; - _map[UnicodeRange(129360, 129387)] = CodepointWidth::Wide; - _map[UnicodeRange(129408, 129431)] = CodepointWidth::Wide; - _map[UnicodeRange(129472, 129472)] = CodepointWidth::Wide; - _map[UnicodeRange(129488, 129510)] = CodepointWidth::Wide; - _map[UnicodeRange(131072, 196605)] = CodepointWidth::Wide; - _map[UnicodeRange(196608, 262141)] = CodepointWidth::Wide; - _map[UnicodeRange(917505, 917505)] = CodepointWidth::Narrow; - _map[UnicodeRange(917536, 917631)] = CodepointWidth::Narrow; - _map[UnicodeRange(917760, 917999)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(983040, 1048573)] = CodepointWidth::Ambiguous; - _map[UnicodeRange(1048576, 1114109)] = CodepointWidth::Ambiguous; -} diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 9055063f310..6967ac942bc 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -22,79 +22,6 @@ static_assert(sizeof(unsigned int) == sizeof(wchar_t) * 2, // use to measure the width of a codepoint class CodepointWidthDetector final { -protected: - // used to store range data in CodepointWidthDetector's internal map - class UnicodeRange final - { - public: - UnicodeRange(const unsigned int lowerBound, - const unsigned int upperBound) : - _lowerBound{ lowerBound }, - _upperBound{ upperBound }, - _isBounds{ true } - { - } - - UnicodeRange(const unsigned int searchTerm) : - _lowerBound{ searchTerm }, - _upperBound{ searchTerm }, - _isBounds{ false } - { - } - - bool IsBounds() const noexcept - { - return _isBounds; - } - - unsigned int LowerBound() const - { - FAIL_FAST_IF(!_isBounds); - return _lowerBound; - } - - unsigned int UpperBound() const - { - FAIL_FAST_IF(!_isBounds); - return _upperBound; - } - - unsigned int SearchTerm() const - { - FAIL_FAST_IF(_isBounds); - return _lowerBound; - } - - private: - unsigned int _lowerBound; - unsigned int _upperBound; - bool _isBounds; - }; - - // used for comparing if we've found the range that a searching UnicodeRange falls into - struct UnicodeRangeCompare final - { - bool operator()(const UnicodeRange& a, const UnicodeRange& b) const - { - if (!a.IsBounds() && b.IsBounds()) - { - return a.SearchTerm() < b.LowerBound(); - } - else if (a.IsBounds() && !b.IsBounds()) - { - return a.UpperBound() < b.SearchTerm(); - } - else if (a.IsBounds() && b.IsBounds()) - { - return a.LowerBound() < b.LowerBound(); - } - else - { - return a.SearchTerm() < b.SearchTerm(); - } - } - }; - public: CodepointWidthDetector() = default; CodepointWidthDetector(const CodepointWidthDetector&) = delete; @@ -115,11 +42,8 @@ class CodepointWidthDetector final private: bool _lookupIsWide(const std::wstring_view glyph) const noexcept; bool _checkFallbackViaCache(const std::wstring_view glyph) const; - unsigned int _extractCodepoint(const std::wstring_view glyph) const noexcept; - void _populateUnicodeSearchMap(); + static unsigned int _extractCodepoint(const std::wstring_view glyph) noexcept; mutable std::map _fallbackCache; - std::map _map; std::function _pfnFallbackMethod; - bool _hasFallback = false; }; From fca0cd98791b4ac7eaaeb333b31ac114d39d7f05 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Fri, 16 Aug 2019 13:31:21 -0700 Subject: [PATCH 040/154] Reduce scope of audit mode build to just the projects that are currently ready to be audited to alleviate disk space problem. (#2457) --- OpenConsole.sln | 81 ------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/OpenConsole.sln b/OpenConsole.sln index 65a12e008e7..35fa999fd19 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -262,14 +262,8 @@ Global EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|ARM64.Build.0 = Release|ARM64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|ARM64.Deploy.0 = Release|ARM64 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x64.Build.0 = Release|x64 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x64.Deploy.0 = Release|x64 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x86.ActiveCfg = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x86.Build.0 = Release|x86 - {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.AuditMode|x86.Deploy.0 = Release|x86 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Debug|ARM64.Deploy.0 = Debug|ARM64 @@ -289,11 +283,8 @@ Global {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x86.Build.0 = Release|x86 {CA5CAD1A-224A-4171-B13A-F16E576FDD12}.Release|x86.Deploy.0 = Release|x86 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.Build.0 = Release|ARM64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.ActiveCfg = Release|x64 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.Build.0 = Release|x64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.ActiveCfg = Release|Win32 - {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.Build.0 = Release|Win32 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.ActiveCfg = Debug|ARM64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.Build.0 = Debug|ARM64 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.ActiveCfg = Debug|x64 @@ -307,11 +298,8 @@ Global {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.ActiveCfg = Release|Win32 {9CBD7DFA-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.Build.0 = Release|Win32 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|ARM64.Build.0 = Release|ARM64 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x64.ActiveCfg = Release|x64 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x64.Build.0 = Release|x64 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x86.ActiveCfg = Release|Win32 - {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.AuditMode|x86.Build.0 = Release|Win32 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|ARM64.ActiveCfg = Debug|ARM64 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|ARM64.Build.0 = Debug|ARM64 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Debug|x64.ActiveCfg = Debug|x64 @@ -325,11 +313,8 @@ Global {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x86.ActiveCfg = Release|Win32 {345FD5A4-B32B-4F29-BD1C-B033BD2C35CC}.Release|x86.Build.0 = Release|Win32 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|ARM64.Build.0 = Release|ARM64 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x64.ActiveCfg = Release|x64 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x64.Build.0 = Release|x64 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x86.ActiveCfg = Release|Win32 - {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.AuditMode|x86.Build.0 = Release|Win32 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|ARM64.Build.0 = Debug|ARM64 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Debug|x64.ActiveCfg = Debug|x64 @@ -343,11 +328,8 @@ Global {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x86.ActiveCfg = Release|Win32 {2FD12FBB-1DDB-46D8-B818-1023C624CACA}.Release|x86.Build.0 = Release|Win32 {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|ARM64.Build.0 = Release|ARM64 {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x64.ActiveCfg = Release|x64 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x64.Build.0 = Release|x64 {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x86.ActiveCfg = Release|Win32 - {3AE13314-1939-4DFA-9C14-38CA0834050C}.AuditMode|x86.Build.0 = Release|Win32 {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|ARM64.ActiveCfg = Debug|ARM64 {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|ARM64.Build.0 = Debug|ARM64 {3AE13314-1939-4DFA-9C14-38CA0834050C}.Debug|x64.ActiveCfg = Debug|x64 @@ -361,11 +343,8 @@ Global {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x86.ActiveCfg = Release|Win32 {3AE13314-1939-4DFA-9C14-38CA0834050C}.Release|x86.Build.0 = Release|Win32 {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.Build.0 = Release|ARM64 {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.ActiveCfg = Release|x64 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.Build.0 = Release|x64 {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.ActiveCfg = Release|Win32 - {DCF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.Build.0 = Release|Win32 {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64 {DCF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64 @@ -379,11 +358,8 @@ Global {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.ActiveCfg = Release|Win32 {DCF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.Build.0 = Release|Win32 {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|ARM64.Build.0 = Release|ARM64 {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.ActiveCfg = Release|x64 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x64.Build.0 = Release|x64 {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.ActiveCfg = Release|Win32 - {1CF55140-EF6A-4736-A403-957E4F7430BB}.AuditMode|x86.Build.0 = Release|Win32 {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|ARM64.Build.0 = Debug|ARM64 {1CF55140-EF6A-4736-A403-957E4F7430BB}.Debug|x64.ActiveCfg = Debug|x64 @@ -397,11 +373,8 @@ Global {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.ActiveCfg = Release|Win32 {1CF55140-EF6A-4736-A403-957E4F7430BB}.Release|x86.Build.0 = Release|Win32 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|ARM64.Build.0 = Release|ARM64 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x64.ActiveCfg = Release|x64 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x64.Build.0 = Release|x64 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x86.ActiveCfg = Release|Win32 - {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.AuditMode|x86.Build.0 = Release|Win32 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|ARM64.ActiveCfg = Debug|ARM64 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|ARM64.Build.0 = Debug|ARM64 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Debug|x64.ActiveCfg = Debug|x64 @@ -415,11 +388,8 @@ Global {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x86.ActiveCfg = Release|Win32 {AF0A096A-8B3A-4949-81EF-7DF8F0FEE91F}.Release|x86.Build.0 = Release|Win32 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|ARM64.Build.0 = Release|ARM64 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x64.ActiveCfg = Release|x64 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x64.Build.0 = Release|x64 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x86.ActiveCfg = Release|Win32 - {1C959542-BAC2-4E55-9A6D-13251914CBB9}.AuditMode|x86.Build.0 = Release|Win32 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|ARM64.Build.0 = Debug|ARM64 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Debug|x64.ActiveCfg = Debug|x64 @@ -433,11 +403,8 @@ Global {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x86.ActiveCfg = Release|Win32 {1C959542-BAC2-4E55-9A6D-13251914CBB9}.Release|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.Build.0 = Release|ARM64 {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x64.Build.0 = Release|x64 {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954746}.AuditMode|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 @@ -451,11 +418,8 @@ Global {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|ARM64.Build.0 = Release|ARM64 {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x64.Build.0 = Release|x64 {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC954747}.AuditMode|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|ARM64.ActiveCfg = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|ARM64.Build.0 = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC954747}.Debug|x64.ActiveCfg = Debug|x64 @@ -572,11 +536,8 @@ Global {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x86.ActiveCfg = Release|Win32 {F210A4AE-E02A-4BFC-80BB-F50A672FE763}.Release|x86.Build.0 = Release|Win32 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|ARM64.Build.0 = Release|ARM64 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x64.ActiveCfg = Release|x64 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x64.Build.0 = Release|x64 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x86.ActiveCfg = Release|Win32 - {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.AuditMode|x86.Build.0 = Release|Win32 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|ARM64.ActiveCfg = Debug|ARM64 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|ARM64.Build.0 = Debug|ARM64 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Debug|x64.ActiveCfg = Debug|x64 @@ -590,11 +551,8 @@ Global {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x86.ActiveCfg = Release|Win32 {5D23E8E1-3C64-4CC1-A8F7-6861677F7239}.Release|x86.Build.0 = Release|Win32 {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|ARM64.Build.0 = Release|ARM64 {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x64.ActiveCfg = Release|x64 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x64.Build.0 = Release|x64 {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x86.ActiveCfg = Release|Win32 - {18D09A24-8240-42D6-8CB6-236EEE820262}.AuditMode|x86.Build.0 = Release|Win32 {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|ARM64.ActiveCfg = Debug|ARM64 {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|ARM64.Build.0 = Debug|ARM64 {18D09A24-8240-42D6-8CB6-236EEE820262}.Debug|x64.ActiveCfg = Debug|x64 @@ -679,11 +637,8 @@ Global {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x86.ActiveCfg = Release|Win32 {ED82003F-FC5D-4E94-8B36-F480018ED064}.Release|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|ARM64.Build.0 = Release|ARM64 {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x64.Build.0 = Release|x64 {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8532EC964726}.AuditMode|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|ARM64.ActiveCfg = Debug|ARM64 {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|ARM64.Build.0 = Debug|ARM64 {06EC74CB-9A12-429C-B551-8532EC964726}.Debug|x64.ActiveCfg = Debug|x64 @@ -712,11 +667,8 @@ Global {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x86.ActiveCfg = Release|Win32 {ED82003F-FC5D-4E94-8B47-F480018ED064}.Release|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|ARM64.Build.0 = Release|ARM64 {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x64.ActiveCfg = Release|x64 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x64.Build.0 = Release|x64 {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x86.ActiveCfg = Release|Win32 - {06EC74CB-9A12-429C-B551-8562EC964846}.AuditMode|x86.Build.0 = Release|Win32 {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|ARM64.ActiveCfg = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|ARM64.Build.0 = Debug|ARM64 {06EC74CB-9A12-429C-B551-8562EC964846}.Debug|x64.ActiveCfg = Debug|x64 @@ -760,11 +712,8 @@ Global {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x86.ActiveCfg = Release|Win32 {C7A6A5D9-60BE-4AEB-A5F6-AFE352F86CBB}.Release|x86.Build.0 = Release|Win32 {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|ARM64.Build.0 = Release|ARM64 {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|x64.ActiveCfg = Release|x64 - {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|x64.Build.0 = Release|x64 {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|x86.ActiveCfg = Release|Win32 - {990F2657-8580-4828-943F-5DD657D11842}.AuditMode|x86.Build.0 = Release|Win32 {990F2657-8580-4828-943F-5DD657D11842}.Debug|ARM64.ActiveCfg = Debug|ARM64 {990F2657-8580-4828-943F-5DD657D11842}.Debug|ARM64.Build.0 = Debug|ARM64 {990F2657-8580-4828-943F-5DD657D11842}.Debug|x64.ActiveCfg = Debug|x64 @@ -877,11 +826,8 @@ Global {48D21369-3D7B-4431-9967-24E81292CF62}.Release|x86.ActiveCfg = Release|Win32 {48D21369-3D7B-4431-9967-24E81292CF62}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Debug|x64.ActiveCfg = Debug|x64 @@ -895,11 +841,8 @@ Global {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-ABCD-429C-B551-8562EC954746}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 @@ -913,11 +856,8 @@ Global {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-ABCD-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Debug|x64.ActiveCfg = Debug|x64 @@ -931,11 +871,8 @@ Global {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-6CA5B3AB89ED}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Debug|x64.ActiveCfg = Debug|x64 @@ -949,11 +886,8 @@ Global {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Debug|x64.ActiveCfg = Debug|x64 @@ -967,11 +901,8 @@ Global {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-44BD-4AC7-AC72-F16E576FDD12}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Debug|x64.ActiveCfg = Debug|x64 @@ -985,14 +916,8 @@ Global {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-D7EC-4107-B7C6-79CB77AE2907}.Release|x86.Build.0 = Release|Win32 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|ARM64.Build.0 = Release|ARM64 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|ARM64.Deploy.0 = Release|ARM64 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x64.ActiveCfg = Release|x64 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x64.Build.0 = Release|x64 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x64.Deploy.0 = Release|x64 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x86.ActiveCfg = Release|x86 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x86.Build.0 = Release|x86 - {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.AuditMode|x86.Deploy.0 = Release|x86 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.Debug|ARM64.Build.0 = Debug|ARM64 {2D310963-F3E0-4EE5-8AC6-FBC94DCC3310}.Debug|ARM64.Deploy.0 = Debug|ARM64 @@ -1075,11 +1000,8 @@ Global {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9333-4D05-B12A-1905CBF112F9}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.ActiveCfg = Release|ARM64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|ARM64.Build.0 = Release|ARM64 {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x64.ActiveCfg = Release|x64 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x64.Build.0 = Release|x64 {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x86.ActiveCfg = Release|Win32 - {CA5CAD1A-9A12-429C-B551-8562EC954746}.AuditMode|x86.Build.0 = Release|Win32 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Debug|x64.ActiveCfg = Debug|x64 @@ -1093,11 +1015,8 @@ Global {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-9A12-429C-B551-8562EC954746}.Release|x86.Build.0 = Release|Win32 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x64.ActiveCfg = AuditMode|x64 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x64.Build.0 = AuditMode|x64 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x86.ActiveCfg = AuditMode|Win32 - {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.AuditMode|x86.Build.0 = AuditMode|Win32 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.ActiveCfg = Debug|ARM64 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|ARM64.Build.0 = Debug|ARM64 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Debug|x64.ActiveCfg = Debug|x64 From 24ea0866d30226afdd3df71b3d0c7096b7b841fc Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Aug 2019 16:18:29 -0500 Subject: [PATCH 041/154] When the titlebar is clicked, dismiss the new tab flyout (#2438) * When the titlebar is clicked, dismiss the new tab flyout Fixes #2028. * Fix this for the base IslandWindow as well --- src/cascadia/TerminalApp/App.cpp | 17 +++++++++++ src/cascadia/TerminalApp/App.h | 1 + src/cascadia/TerminalApp/App.idl | 2 ++ src/cascadia/WindowsTerminal/AppHost.cpp | 5 ++++ src/cascadia/WindowsTerminal/IslandWindow.cpp | 17 +++++++++++ src/cascadia/WindowsTerminal/IslandWindow.h | 3 ++ .../WindowsTerminal/NonClientIslandWindow.cpp | 29 ++++++++++++++----- .../WindowsTerminal/NonClientIslandWindow.h | 2 +- src/cascadia/inc/cppwinrt_utils.h | 2 +- 9 files changed, 68 insertions(+), 10 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 05120c06b2d..f56421e205a 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1462,6 +1462,23 @@ namespace winrt::TerminalApp::implementation return connection; } + // Method Description: + // - Used to tell the app that the titlebar has been clicked. The App won't + // actually recieve any clicks in the titlebar area, so this is a helper + // to clue the app in that a click has happened. The App will use this as + // a indicator that it needs to dismiss any open flyouts. + // Arguments: + // - + // Return Value: + // - + void App::TitlebarClicked() + { + if (_newTabButton && _newTabButton.Flyout()) + { + _newTabButton.Flyout().Hide(); + } + } + // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 473f220d12c..0d52350357d 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -37,6 +37,7 @@ namespace winrt::TerminalApp::implementation ~App() = default; hstring GetTitle(); + void TitlebarClicked(); // -------------------------------- WinRT Events --------------------------------- DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs); diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 3fe919a697d..3d954850ef7 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -30,5 +30,7 @@ namespace TerminalApp event Windows.Foundation.TypedEventHandler RequestedThemeChanged; String GetTitle(); + + void TitlebarClicked(); } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 5893c1ad9ea..e2a72ce5594 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -69,6 +69,11 @@ void AppHost::Initialize() // content in Create. _app.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent }); } + + // Add an event handler to plumb clicks in the titlebar area down to the + // application layer. + _window->DragRegionClicked([this]() { _app.TitlebarClicked(); }); + _app.RequestedThemeChanged({ this, &AppHost::_UpdateTheme }); _app.Create(); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 142c9118509..881054bb645 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -166,6 +166,21 @@ void IslandWindow::OnSize(const UINT width, const UINT height) return 0; // eat the message } } + + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCXBUTTONDOWN: + case WM_NCXBUTTONUP: + { + // If we clicked in the titlebar, raise an event so the app host can + // dispatch an appropriate event. + _DragRegionClickedHandlers(); + break; + } case WM_MENUCHAR: { // GH#891: return this LRESULT here to prevent the app from making a @@ -258,3 +273,5 @@ void IslandWindow::UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& req // drawing ourselves to match the new theme ::InvalidateRect(_window.get(), nullptr, false); } + +DEFINE_EVENT(IslandWindow, DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index b7d853ee70c..1b95b3308f4 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -7,6 +7,7 @@ #include "WindowUiaProvider.hpp" #include #include +#include "../../cascadia/inc/cppwinrt_utils.h" class IslandWindow : public BaseWindow, @@ -66,6 +67,8 @@ class IslandWindow : #pragma endregion + DECLARE_EVENT(DragRegionClicked, _DragRegionClickedHandlers, winrt::delegate<>); + protected: void ForceResize() { diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 9d1cfa7d2d8..c486e773532 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -263,14 +263,22 @@ void NonClientIslandWindow::_UpdateDragRegion() // - Hit test the frame for resizing and moving. // Arguments: // - ptMouse: the mouse point being tested, in absolute (NOT WINDOW) coordinates. +// - titlebarIsCaption: If true, we want to treat the titlebar area as +// HTCAPTION, otherwise we'll return HTNOWHERE for the titlebar. // Return Value: // - one of the values from // https://docs.microsoft.com/en-us/windows/desktop/inputdev/wm-nchittest#return-value // corresponding to the area of the window that was hit // NOTE: -// Largely taken from code on: +// - Largely taken from code on: // https://docs.microsoft.com/en-us/windows/desktop/dwm/customframe -[[nodiscard]] LRESULT NonClientIslandWindow::HitTestNCA(POINT ptMouse) const noexcept +// NOTE[2]: Concerning `titlebarIsCaption` +// - We want HTNOWHERE as the return value for WM_NCHITTEST, so that we can get +// mouse presses in the titlebar area. If we return HTCAPTION there, we won't +// get any mouse WMs. However, when we're handling the mouse events, we need +// to know if the mouse was in that are or not, so we'll return HTCAPTION in +// that handler, to differentiate from the rest of the window. +[[nodiscard]] LRESULT NonClientIslandWindow::HitTestNCA(POINT ptMouse, const bool titlebarIsCaption) const noexcept { // Get the window rectangle. RECT rcWindow = BaseWindow::GetWindowRect(); @@ -311,10 +319,11 @@ void NonClientIslandWindow::_UpdateDragRegion() // clang-format off // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT) + const auto topHt = fOnResizeBorder ? HTTOP : (titlebarIsCaption ? HTCAPTION : HTNOWHERE); LRESULT hitTests[3][3] = { - { HTTOPLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, HTTOPRIGHT }, - { HTLEFT, HTNOWHERE, HTRIGHT }, - { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT }, + { HTTOPLEFT, topHt, HTTOPRIGHT }, + { HTLEFT, HTNOWHERE, HTRIGHT }, + { HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT }, }; // clang-format on @@ -511,8 +520,7 @@ RECT NonClientIslandWindow::GetMaxWindowRectInPixels(const RECT* const prcSugges // Handle hit testing in the NCA if not handled by DwmDefWindowProc. if (lRet == 0) { - lRet = HitTestNCA({ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }); - + lRet = HitTestNCA({ GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }, false); if (lRet != HTNOWHERE) { return lRet; @@ -594,9 +602,14 @@ RECT NonClientIslandWindow::GetMaxWindowRectInPixels(const RECT* const prcSugges { POINT point1 = {}; ::GetCursorPos(&point1); - const auto region = HitTestNCA(point1); + + const auto region = HitTestNCA(point1, true); if (region == HTCAPTION) { + // If we clicked in the titlebar, raise an event so the app host can + // dispatch an appropriate event. + _DragRegionClickedHandlers(); + const auto longParam = MAKELPARAM(point1.x, point1.y); ::SetActiveWindow(_window.get()); ::PostMessage(_window.get(), WM_SYSCOMMAND, SC_MOVE | HTCAPTION, longParam); diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 6949b5c64a8..161a182576b 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -55,7 +55,7 @@ class NonClientIslandWindow : public IslandWindow RECT GetDragAreaRect() const noexcept; - [[nodiscard]] LRESULT HitTestNCA(POINT ptMouse) const noexcept; + [[nodiscard]] LRESULT HitTestNCA(POINT ptMouse, const bool titlebarIsCaption) const noexcept; [[nodiscard]] HRESULT _UpdateFrameMargins() const noexcept; diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index f088734b2d5..8609698dfc5 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -25,7 +25,7 @@ public: \ winrt::event_token name(args const& handler); \ void name(winrt::event_token const& token) noexcept; \ \ -private: \ +protected: \ winrt::event eventHandler; // This is a helper macro for defining the body of events. From d7d96f723a2b8ce392251c7bb2b103580fc56dc2 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Aug 2019 16:21:43 -0500 Subject: [PATCH 042/154] Add Warnings during settings load (#2422) * Warn the user when their settings are bad The start of work on #1348 * Display an error dialog for errors during validation * Polish for PR * Add a ton of tests * Polish the _GetMessageText bits * Add code to check for duplicate profiles * Verify that many warnings work at the same time * comments y'all * Apply fixes for dustin's thoughts from PR * Add a proper exception type, use an array instead of a map * PR Fixes * Fix x86 build break * Add a bit on "using the defaults" when we encountering an exception * remove a redundant variable * guid->GUID * Address Michael's PR comments * Clean up this error text, and catch exceptions better * Update src/cascadia/TerminalApp/Resources/en-US/Resources.resw --- .../LocalTests_TerminalApp/SettingsTests.cpp | 324 ++++++++++++++++++ src/cascadia/TerminalApp/App.cpp | 209 ++++++++++- src/cascadia/TerminalApp/App.h | 3 + src/cascadia/TerminalApp/App.xaml | 5 + src/cascadia/TerminalApp/CascadiaSettings.cpp | 129 +++++++ src/cascadia/TerminalApp/CascadiaSettings.h | 19 +- .../CascadiaSettingsSerialization.cpp | 21 +- .../Resources/en-US/Resources.resw | 86 +++-- src/cascadia/TerminalApp/TerminalWarnings.h | 57 +++ .../TerminalApp/lib/TerminalAppLib.vcxproj | 1 + src/inc/LibraryIncludes.h | 1 + 11 files changed, 803 insertions(+), 52 deletions(-) create mode 100644 src/cascadia/TerminalApp/TerminalWarnings.h diff --git a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp index 82bcfbe611f..4a70386e33d 100644 --- a/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/SettingsTests.cpp @@ -4,11 +4,13 @@ #include "precomp.h" #include "../TerminalApp/ColorScheme.h" +#include "../TerminalApp/CascadiaSettings.h" using namespace Microsoft::Console; using namespace TerminalApp; using namespace WEX::Logging; using namespace WEX::TestExecution; +using namespace WEX::Common; namespace TerminalAppLocalTests { @@ -29,8 +31,31 @@ namespace TerminalAppLocalTests END_TEST_CLASS() TEST_METHOD(TryCreateWinRTType); + TEST_METHOD(ValidateProfilesExist); + TEST_METHOD(ValidateDefaultProfileExists); + TEST_METHOD(ValidateDuplicateProfiles); + TEST_METHOD(ValidateManyWarnings); + + TEST_CLASS_SETUP(ClassSetup) + { + reader = std::unique_ptr(Json::CharReaderBuilder::CharReaderBuilder().newCharReader()); + return true; + } + Json::Value VerifyParseSucceeded(std::string content); + + private: + std::unique_ptr reader; }; + Json::Value SettingsTests::VerifyParseSucceeded(std::string content) + { + Json::Value root; + std::string errs; + const bool parseResult = reader->parse(content.c_str(), content.c_str() + content.size(), &root, &errs); + VERIFY_IS_TRUE(parseResult, winrt::to_hstring(errs).c_str()); + return root; + } + void SettingsTests::TryCreateWinRTType() { winrt::Microsoft::Terminal::Settings::TerminalSettings settings{}; @@ -41,4 +66,303 @@ namespace TerminalAppLocalTests VERIFY_ARE_NOT_EQUAL(oldFontSize, newFontSize); } + void SettingsTests::ValidateProfilesExist() + { + const std::string settingsWithProfiles{ R"( + { + "profiles": [ + { + "name" : "profile0" + } + ] + })" }; + + const std::string settingsWithoutProfiles{ R"( + { + "defaultProfile": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}" + })" }; + + const std::string settingsWithEmptyProfiles{ R"( + { + "profiles": [] + })" }; + + { + // Case 1: Good settings + const auto settingsObject = VerifyParseSucceeded(settingsWithProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateProfilesExist(); + } + { + // Case 2: Bad settings + const auto settingsObject = VerifyParseSucceeded(settingsWithoutProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + bool caughtExpectedException = false; + try + { + settings->_ValidateProfilesExist(); + } + catch (const ::TerminalApp::SettingsException& ex) + { + VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::NoProfiles); + caughtExpectedException = true; + } + VERIFY_IS_TRUE(caughtExpectedException); + } + { + // Case 3: Bad settings + const auto settingsObject = VerifyParseSucceeded(settingsWithEmptyProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + bool caughtExpectedException = false; + try + { + settings->_ValidateProfilesExist(); + } + catch (const ::TerminalApp::SettingsException& ex) + { + VERIFY_IS_TRUE(ex.Error() == ::TerminalApp::SettingsLoadErrors::NoProfiles); + caughtExpectedException = true; + } + VERIFY_IS_TRUE(caughtExpectedException); + } + } + + void SettingsTests::ValidateDefaultProfileExists() + { + const std::string goodProfiles{ R"( + { + "globals": { + "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" + }, + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile0", + "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + const std::string badProfiles{ R"( + { + "globals": { + "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" + }, + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile1", + "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + const std::string noDefaultAtAll{ R"( + { + "globals": { + "alwaysShowTabs": true + }, + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-5555-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile1", + "guid": "{6239a42c-6666-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + { + // Case 1: Good settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with unique guids, and the defaultProfile is one of those guids")); + const auto settingsObject = VerifyParseSucceeded(goodProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateDefaultProfileExists(); + VERIFY_ARE_EQUAL(static_cast(0), settings->_warnings.size()); + VERIFY_ARE_EQUAL(static_cast(2), settings->_profiles.size()); + VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid()); + } + { + // Case 2: Bad settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with unique guids, but the defaultProfile is NOT one of those guids")); + const auto settingsObject = VerifyParseSucceeded(badProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateDefaultProfileExists(); + VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.size()); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(0)); + + VERIFY_ARE_EQUAL(static_cast(2), settings->_profiles.size()); + VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid()); + } + { + // Case 2: Bad settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with unique guids, and no defaultProfile at all")); + const auto settingsObject = VerifyParseSucceeded(badProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateDefaultProfileExists(); + VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.size()); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(0)); + + VERIFY_ARE_EQUAL(static_cast(2), settings->_profiles.size()); + VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid()); + } + } + + void SettingsTests::ValidateDuplicateProfiles() + { + const std::string goodProfiles{ R"( + { + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile0", + "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + const std::string badProfiles{ R"( + { + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile1", + "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + const std::string veryBadProfiles{ R"( + { + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile1", + "guid": "{6239a42c-5555-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile2", + "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile3", + "guid": "{6239a42c-4444-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile4", + "guid": "{6239a42c-6666-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile5", + "guid": "{6239a42c-5555-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile6", + "guid": "{6239a42c-7777-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + { + // Case 1: Good settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with unique guids")); + const auto settingsObject = VerifyParseSucceeded(goodProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateNoDuplicateProfiles(); + VERIFY_ARE_EQUAL(static_cast(0), settings->_warnings.size()); + VERIFY_ARE_EQUAL(static_cast(2), settings->_profiles.size()); + } + { + // Case 2: Bad settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with the same guid")); + const auto settingsObject = VerifyParseSucceeded(badProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + + settings->_ValidateNoDuplicateProfiles(); + + VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.size()); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0)); + + VERIFY_ARE_EQUAL(static_cast(1), settings->_profiles.size()); + VERIFY_ARE_EQUAL(L"profile0", settings->_profiles.at(0).GetName()); + } + { + // Case 3: Very bad settings + Log::Comment(NoThrowString().Format( + L"Testing a set of profiles, many of which with duplicated guids")); + const auto settingsObject = VerifyParseSucceeded(veryBadProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + settings->_ValidateNoDuplicateProfiles(); + VERIFY_ARE_EQUAL(static_cast(1), settings->_warnings.size()); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0)); + + VERIFY_ARE_EQUAL(static_cast(4), settings->_profiles.size()); + VERIFY_ARE_EQUAL(L"profile0", settings->_profiles.at(0).GetName()); + VERIFY_ARE_EQUAL(L"profile1", settings->_profiles.at(1).GetName()); + VERIFY_ARE_EQUAL(L"profile4", settings->_profiles.at(2).GetName()); + VERIFY_ARE_EQUAL(L"profile6", settings->_profiles.at(3).GetName()); + } + } + + void SettingsTests::ValidateManyWarnings() + { + const std::string badProfiles{ R"( + { + "globals": { + "defaultProfile": "{6239a42c-1111-49a3-80bd-e8fdd045185c}" + }, + "profiles": [ + { + "name" : "profile0", + "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile1", + "guid": "{6239a42c-3333-49a3-80bd-e8fdd045185c}" + }, + { + "name" : "profile2", + "guid": "{6239a42c-2222-49a3-80bd-e8fdd045185c}" + } + ] + })" }; + + // Case 2: Bad settings + Log::Comment(NoThrowString().Format( + L"Testing a pair of profiles with the same guid")); + const auto settingsObject = VerifyParseSucceeded(badProfiles); + auto settings = CascadiaSettings::FromJson(settingsObject); + + settings->_ValidateSettings(); + + VERIFY_ARE_EQUAL(static_cast(2), settings->_warnings.size()); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::DuplicateProfile, settings->_warnings.at(0)); + VERIFY_ARE_EQUAL(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile, settings->_warnings.at(1)); + + VERIFY_ARE_EQUAL(static_cast(2), settings->_profiles.size()); + VERIFY_ARE_EQUAL(settings->_globals.GetDefaultProfile(), settings->_profiles.at(0).GetGuid()); + } + } diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index f56421e205a..fdc8f795da7 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -26,6 +26,94 @@ namespace winrt using IInspectable = Windows::Foundation::IInspectable; } +// clang-format off +// !!! IMPORTANT !!! +// Make sure that these keys are in the same order as the +// SettingsLoadWarnings/Errors enum is! +static const std::array settingsLoadWarningsLabels { + L"MissingDefaultProfileText", + L"DuplicateProfileText" +}; +static const std::array settingsLoadErrorsLabels { + L"NoProfilesText" +}; +// clang-format on + +// Function Description: +// - General-purpose helper for looking up a localized string for a +// warning/error. First will look for the given key in the provided map of +// keys->strings, where the values in the map are ResourceKeys. If it finds +// one, it will lookup the localized string from that ResourceKey. +// - If it does not find a key, it'll return an empty string +// Arguments: +// - key: the value to use to look for a resource key in the given map +// - map: A map of keys->Resource keys. +// - loader: the ScopedResourceLoader to use to look up the localized string. +// Return Value: +// - the localized string for the given type, if it exists. +template +static winrt::hstring _GetMessageText(uint32_t index, std::array keys, ScopedResourceLoader loader) +{ + if (index < keys.size()) + { + return loader.GetLocalizedString(keys.at(index)); + } + return {}; +} + +// Function Description: +// - Gets the text from our ResourceDictionary for the given +// SettingsLoadWarning. If there is no such text, we'll return nullptr. +// - The warning should have an entry in settingsLoadWarningsLabels. +// Arguments: +// - warning: the SettingsLoadWarnings value to get the localized text for. +// - loader: the ScopedResourceLoader to use to look up the localized string. +// Return Value: +// - localized text for the given warning +static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warning, ScopedResourceLoader loader) +{ + return _GetMessageText(static_cast(warning), settingsLoadWarningsLabels, loader); +} + +// Function Description: +// - Gets the text from our ResourceDictionary for the given +// SettingsLoadError. If there is no such text, we'll return nullptr. +// - The warning should have an entry in settingsLoadErrorsLabels. +// Arguments: +// - error: the SettingsLoadErrors value to get the localized text for. +// - loader: the ScopedResourceLoader to use to look up the localized string. +// Return Value: +// - localized text for the given error +static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error, ScopedResourceLoader loader) +{ + return _GetMessageText(static_cast(error), settingsLoadErrorsLabels, loader); +} + +// Function Description: +// - Creates a Run of text to display an error message. The text is yellow or +// red for dark/light theme, respectively. +// Arguments: +// - text: The text of the error message. +// - resources: The application's resource loader. +// Return Value: +// - The fully styled text run. +static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceDictionary& resources) +{ + Documents::Run textRun; + textRun.Text(text); + + // Color the text red (light theme) or yellow (dark theme) based on the system theme + winrt::IInspectable key = winrt::box_value(L"ErrorTextBrush"); + if (resources.HasKey(key)) + { + winrt::IInspectable g = resources.Lookup(key); + auto brush = g.try_as(); + textRun.Foreground(brush); + } + + return textRun; +} + namespace winrt::TerminalApp::implementation { App::App() : @@ -177,6 +265,81 @@ namespace winrt::TerminalApp::implementation _ShowDialog(winrt::box_value(title), winrt::box_value(message), buttonText); } + // Method Description: + // - Displays a dialog for errors found while loading or validating the + // settings. Uses the resources under the provided title and content keys + // as the title and first content of the dialog, then also displays a + // message for whatever exception was found while validating the settings. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. See _ShowDialog for details + // Arguments: + // - titleKey: The key to use to lookup the title text from our resources. + // - contentKey: The key to use to lookup the content text from our resources. + void App::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, + const winrt::hstring& contentKey) + { + auto title = _resourceLoader.GetLocalizedString(titleKey); + auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); + + Controls::TextBlock warningsTextBlock; + // Make sure you can copy-paste + warningsTextBlock.IsTextSelectionEnabled(true); + // Make sure the lines of text wrap + warningsTextBlock.TextWrapping(TextWrapping::Wrap); + + winrt::Windows::UI::Xaml::Documents::Run errorRun; + const auto errorLabel = _resourceLoader.GetLocalizedString(contentKey); + errorRun.Text(errorLabel); + warningsTextBlock.Inlines().Append(errorRun); + + if (FAILED(_settingsLoadedResult)) + { + if (!_settingsLoadExceptionText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(_settingsLoadExceptionText, Resources())); + } + } + + // Add a note that we're using the default settings in this case. + winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun; + const auto usingDefaultsText = _resourceLoader.GetLocalizedString(L"UsingDefaultSettingsText"); + usingDefaultsRun.Text(usingDefaultsText); + warningsTextBlock.Inlines().Append(usingDefaultsRun); + + _ShowDialog(winrt::box_value(title), warningsTextBlock, buttonText); + } + + // Method Description: + // - Displays a dialog for warnings found while loading or validating the + // settings. Displays messages for whatever warnings were found while + // validating the settings. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. See _ShowDialog for details + void App::_ShowLoadWarningsDialog() + { + auto title = _resourceLoader.GetLocalizedString(L"SettingsValidateErrorTitle"); + auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); + + Controls::TextBlock warningsTextBlock; + // Make sure you can copy-paste + warningsTextBlock.IsTextSelectionEnabled(true); + // Make sure the lines of text wrap + warningsTextBlock.TextWrapping(TextWrapping::Wrap); + + const auto& warnings = _settings->GetWarnings(); + for (const auto& warning : warnings) + { + // Try looking up the warning message key for each warning. + const auto warningText = _GetWarningText(warning, _resourceLoader); + if (!warningText.empty()) + { + warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, Resources())); + } + } + + _ShowDialog(winrt::box_value(title), warningsTextBlock, buttonText); + } + // Method Description: // - Show a dialog with "About" information. Displays the app's Display // Name, version, getting started link, documentation link, and release @@ -261,7 +424,11 @@ namespace winrt::TerminalApp::implementation { const winrt::hstring titleKey = L"InitialJsonParseErrorTitle"; const winrt::hstring textKey = L"InitialJsonParseErrorText"; - _ShowOkDialog(titleKey, textKey); + _ShowLoadErrorsDialog(titleKey, textKey); + } + else if (_settingsLoadedResult == S_FALSE) + { + _ShowLoadWarningsDialog(); } } @@ -314,7 +481,8 @@ namespace winrt::TerminalApp::implementation auto keyBindings = _settings->GetKeybindings(); const GUID defaultProfileGuid = _settings->GlobalSettings().GetDefaultProfile(); - auto const profileCount = gsl::narrow_cast(_settings->GetProfiles().size()); // the number of profiles should not change in the loop for this to work + // the number of profiles should not change in the loop for this to work + auto const profileCount = gsl::narrow_cast(_settings->GetProfiles().size()); for (int profileIndex = 0; profileIndex < profileCount; profileIndex++) { const auto& profile = _settings->GetProfiles()[profileIndex]; @@ -324,7 +492,8 @@ namespace winrt::TerminalApp::implementation if (profileIndex < 9) { // enum value for ShortcutAction::NewTabProfileX; 0==NewTabProfile0 - auto profileKeyChord = keyBindings.GetKeyBinding(static_cast(profileIndex + static_cast(ShortcutAction::NewTabProfile0))); + const auto action = static_cast(profileIndex + static_cast(ShortcutAction::NewTabProfile0)); + auto profileKeyChord = keyBindings.GetKeyBinding(action); // make sure we find one to display if (profileKeyChord) @@ -512,13 +681,20 @@ namespace winrt::TerminalApp::implementation { auto newSettings = CascadiaSettings::LoadAll(saveOnLoad); _settings = std::move(newSettings); - hr = S_OK; + const auto& warnings = _settings->GetWarnings(); + hr = warnings.size() == 0 ? S_OK : S_FALSE; } catch (const winrt::hresult_error& e) { hr = e.code(); + _settingsLoadExceptionText = e.message(); LOG_HR(hr); } + catch (const ::TerminalApp::SettingsException& ex) + { + hr = E_INVALIDARG; + _settingsLoadExceptionText = _GetErrorText(ex.Error(), _resourceLoader); + } catch (...) { hr = wil::ResultFromCaughtException(); @@ -631,11 +807,17 @@ namespace winrt::TerminalApp::implementation _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { const winrt::hstring titleKey = L"ReloadJsonParseErrorTitle"; const winrt::hstring textKey = L"ReloadJsonParseErrorText"; - _ShowOkDialog(titleKey, textKey); + _ShowLoadErrorsDialog(titleKey, textKey); }); return; } + else if (_settingsLoadedResult == S_FALSE) + { + _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + _ShowLoadWarningsDialog(); + }); + } // Here, we successfully reloaded the settings, and created a new // TerminalSettings object. @@ -1385,7 +1567,8 @@ namespace winrt::TerminalApp::implementation // Takes into account a special case for an error condition for a comma // Arguments: // - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator - void App::_SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord) + void App::_SetAcceleratorForMenuItem(Controls::MenuFlyoutItem& menuItem, + const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord) { #ifdef DEP_MICROSOFT_UI_XAML_708_FIXED // work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA @@ -1426,7 +1609,8 @@ namespace winrt::TerminalApp::implementation // - the terminal settings // Return value: // - the desired connection - TerminalConnection::ITerminalConnection App::_CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings) + TerminalConnection::ITerminalConnection App::_CreateConnectionFromSettings(GUID profileGuid, + winrt::Microsoft::Terminal::Settings::TerminalSettings settings) { const auto* const profile = _settings->FindProfile(profileGuid); TerminalConnection::ITerminalConnection connection{ nullptr }; @@ -1437,9 +1621,12 @@ namespace winrt::TerminalApp::implementation connectionType = profile->GetConnectionType(); } - if (profile->HasConnectionType() && profile->GetConnectionType() == AzureConnectionType && TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) + if (profile->HasConnectionType() && + profile->GetConnectionType() == AzureConnectionType && + TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) { - connection = TerminalConnection::AzureConnection(settings.InitialRows(), settings.InitialCols()); + connection = TerminalConnection::AzureConnection(settings.InitialRows(), + settings.InitialCols()); } else { @@ -1484,6 +1671,6 @@ namespace winrt::TerminalApp::implementation // These macros will define them both for you. DEFINE_EVENT(App, TitleChanged, _titleChangeHandlers, TerminalControl::TitleChangedEventArgs); DEFINE_EVENT(App, LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs); - DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement); - DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, UIElement); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, ElementTheme); } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 0d52350357d..3d34f9656c3 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -63,6 +63,7 @@ namespace winrt::TerminalApp::implementation std::unique_ptr<::TerminalApp::CascadiaSettings> _settings; HRESULT _settingsLoadedResult; + winrt::hstring _settingsLoadExceptionText{}; bool _loadedInitialSettings; std::shared_mutex _dialogLock; @@ -80,6 +81,8 @@ namespace winrt::TerminalApp::implementation const winrt::hstring& closeButtonText); void _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); void _ShowAboutDialog(); + void _ShowLoadWarningsDialog(); + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); [[nodiscard]] HRESULT _TryLoadSettings(const bool saveOnLoad) noexcept; void _LoadSettings(); diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index 4cb4d0eea66..7b5ce85ed39 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -44,6 +44,11 @@ the MIT License. See LICENSE in the project root for license information. --> + + + diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 2f0602d3475..6fe28a9b4f5 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -643,3 +643,132 @@ Profile CascadiaSettings::_CreateDefaultProfile(const std::wstring_view name) return newProfile; } + +// Method Description: +// - Gets our list of warnings we found during loading. These are things that we +// knew were bad when we called `_ValidateSettings` last. +// Return Value: +// - a reference to our list of warnings. +std::vector& CascadiaSettings::GetWarnings() +{ + return _warnings; +} + +// Method Description: +// - Attempts to validate this settings structure. If there are critical errors +// found, they'll be thrown as a SettingsLoadError. Non-critical errors, such +// as not finding the default profile, will only result in an error. We'll add +// all these warnings to our list of warnings, and the application can chose +// to display these to the user. +// Arguments: +// - +// Return Value: +// - +void CascadiaSettings::_ValidateSettings() +{ + _warnings.clear(); + + // Make sure to check that profiles exists at all first and foremost: + _ValidateProfilesExist(); + + // Then do some validation on the profiles. The order of these does not + // terribly matter. + _ValidateNoDuplicateProfiles(); + _ValidateDefaultProfileExists(); +} + +// Method Description: +// - Checks if the settings contain profiles at all. As we'll need to have some +// profiles at all, we'll throw an error if there aren't any profiles. +void CascadiaSettings::_ValidateProfilesExist() +{ + const bool hasProfiles = !_profiles.empty(); + if (!hasProfiles) + { + // Throw an exception. This is an invalid state, and we want the app to + // be able to gracefully use the default settings. + + // We can't add the warning to the list of warnings here, because this + // object is not going to be returned at any point. + + throw ::TerminalApp::SettingsException(::TerminalApp::SettingsLoadErrors::NoProfiles); + } +} + +// Method Description: +// - Checks if the "globals.defaultProfile" is set to one of the profiles we +// actually have. If the value is unset, or the value is set to something that +// doesn't exist in the list of profiles, we'll arbitrarily pick the first +// profile to use temporarily as the default. +// - Appends a SettingsLoadWarnings::MissingDefaultProfile to our list of +// warnings if we failed to find the default. +void CascadiaSettings::_ValidateDefaultProfileExists() +{ + const auto defaultProfileGuid = GlobalSettings().GetDefaultProfile(); + const bool nullDefaultProfile = defaultProfileGuid == GUID{}; + bool defaultProfileNotInProfiles = true; + for (const auto& profile : _profiles) + { + if (profile.GetGuid() == defaultProfileGuid) + { + defaultProfileNotInProfiles = false; + break; + } + } + + if (nullDefaultProfile || defaultProfileNotInProfiles) + { + _warnings.push_back(::TerminalApp::SettingsLoadWarnings::MissingDefaultProfile); + // Use the first profile as the new default + + // _temporarily_ set the default profile to the first profile. Because + // we're adding a warning, this settings change won't be re-serialized. + GlobalSettings().SetDefaultProfile(_profiles[0].GetGuid()); + } +} + +// Method Description: +// - Checks to make sure there aren't any duplicate profiles in the list of +// profiles. If so, we'll remove the subsequent entries (temporarily), as they +// won't be accessible anyways. +// - Appends a SettingsLoadWarnings::DuplicateProfile to our list of warnings if +// we find any such duplicate. +void CascadiaSettings::_ValidateNoDuplicateProfiles() +{ + bool foundDupe = false; + + std::vector indiciesToDelete{}; + + // Helper to establish an ordering on guids + struct GuidEquality + { + bool operator()(const GUID& lhs, const GUID& rhs) const + { + return memcmp(&lhs, &rhs, sizeof(rhs)) < 0; + } + }; + std::set uniqueGuids{}; + + // Try collecting all the unique guids. If we ever encounter a guid that's + // already in the set, then we need to delete that profile. + for (int i = 0; i < _profiles.size(); i++) + { + if (!uniqueGuids.insert(_profiles.at(i).GetGuid()).second) + { + foundDupe = true; + indiciesToDelete.push_back(i); + } + } + + // Remove all the duplicates we've marked + // Walk backwards, so we don't accidentally shift any of the elements + for (auto iter = indiciesToDelete.rbegin(); iter != indiciesToDelete.rend(); iter++) + { + _profiles.erase(_profiles.begin() + *iter); + } + + if (foundDupe) + { + _warnings.push_back(::TerminalApp::SettingsLoadWarnings::DuplicateProfile); + } +} diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 6cb941dfea2..6334705bdc4 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: -- CascadiaSettings.hpp +- CascadiaSettings.h Abstract: - This class acts as the container for all app settings. It's composed of two @@ -18,10 +18,17 @@ Author(s): #pragma once #include #include "GlobalAppSettings.h" +#include "TerminalWarnings.h" #include "Profile.h" static constexpr GUID AzureConnectionType = { 0xd9fcfdfa, 0xa479, 0x412c, { 0x83, 0xb7, 0xc5, 0x64, 0xe, 0x61, 0xcd, 0x62 } }; +// fwdecl unittest classes +namespace TerminalAppLocalTests +{ + class SettingsTests; +} + namespace TerminalApp { class CascadiaSettings; @@ -53,9 +60,12 @@ class TerminalApp::CascadiaSettings final void CreateDefaults(); + std::vector& GetWarnings(); + private: GlobalAppSettings _globals; std::vector _profiles; + std::vector _warnings{}; void _CreateDefaultKeybindings(); void _CreateDefaultSchemes(); @@ -65,8 +75,15 @@ class TerminalApp::CascadiaSettings final static void _WriteSettings(const std::string_view content); static std::optional _ReadSettings(); + void _ValidateSettings(); + void _ValidateProfilesExist(); + void _ValidateDefaultProfileExists(); + void _ValidateNoDuplicateProfiles(); + static bool _isPowerShellCoreInstalledInPath(const std::wstring_view programFileEnv, std::filesystem::path& cmdline); static bool _isPowerShellCoreInstalled(std::filesystem::path& cmdline); static void _AppendWslProfiles(std::vector& profileStorage); static Profile _CreateDefaultProfile(const std::wstring_view name); + + friend class TerminalAppLocalTests::SettingsTests; }; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index 40e81ada29a..dfaf68aa05c 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -41,7 +41,10 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa std::optional fileData = _ReadSettings(); const bool foundFile = fileData.has_value(); - if (foundFile) + // Make sure the file isn't totally empty. If it is, we'll treat the file + // like it doesn't exist at all. + const bool fileHasData = foundFile && !fileData.value().empty(); + if (foundFile && fileHasData) { const auto actualData = fileData.value(); @@ -59,18 +62,20 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // `parse` will return false if it fails. if (!reader->parse(actualDataStart, actualData.c_str() + actualData.size(), &root, &errs)) { - // TODO:GH#990 display this exception text to the user, in a - // copy-pasteable way. + // This will be caught by App::_TryLoadSettings, who will display + // the text to the user. throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs)); } resultPtr = FromJson(root); - if (resultPtr->GlobalSettings().GetDefaultProfile() == GUID{}) - { - throw winrt::hresult_invalid_argument(); - } + // If this throws, the app will catch it and use the default settings (temporarily) + resultPtr->_ValidateSettings(); + + const bool foundWarnings = resultPtr->_warnings.size() > 0; - if (saveOnLoad) + // Don't save on load if there were warnings - we tried to gracefully + // handle them. + if (saveOnLoad && !foundWarnings) { // Logically compare the json we've parsed from the file to what // we'd serialize at runtime. If the values are different, then diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 8173dcac8e8..c461da5b42b 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -1,17 +1,17 @@  - @@ -118,17 +118,39 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Settings could not be loaded from file - temporarily using the default settings. Check for syntax errors, including trailing commas. + Settings could not be loaded from file. Check for syntax errors, including trailing commas. + + + + Could not find your default profile in your list of profiles - using the first profile. Check to make sure the defaultProfile matches the GUID of one of your profiles. + + + + Found multiple profiles with the same GUID in your settings file - ignoring duplicates. Make sure each profile's GUID is unique. + + + + No profiles were found in your settings. + + + + Settings could not be reloaded from file. Check for syntax errors, including trailing commas. + + + + +Temporarily using the Windows Terminal default settings. + Failed to load settings + + Encountered errors while loading user settings + Ok - - Settings could not be reloaded from file. Check for syntax errors, including trailing commas. - Failed to reload settings @@ -171,4 +193,4 @@ Settings - \ No newline at end of file + diff --git a/src/cascadia/TerminalApp/TerminalWarnings.h b/src/cascadia/TerminalApp/TerminalWarnings.h new file mode 100644 index 00000000000..b926f1c1ab7 --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalWarnings.h @@ -0,0 +1,57 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- TerminalWarnings.h + +Abstract: +- This file contains definitions for warnings, errors and exceptions used by the + Windows Terminal + +Author(s): +- Mike Griese - August 2019 + +--*/ +#pragma once + +namespace TerminalApp +{ + // SettingsLoadWarnings are scenarios where the settings contained + // information we knew was invalid, but we could recover from. + enum class SettingsLoadWarnings : uint32_t + { + MissingDefaultProfile = 0, + DuplicateProfile = 1 + }; + + // SettingsLoadWarnings are scenarios where the settings had invalid state + // that we could not recover from. + enum class SettingsLoadErrors : uint32_t + { + NoProfiles = 0 + }; + + // This is a helper class to wrap up a SettingsLoadErrors into a proper + // exception type. + class SettingsException : public std::runtime_error + { + public: + SettingsException(const SettingsLoadErrors& error) : + std::runtime_error{ nullptr }, + _error{ error } {}; + + // We don't use the what() method - we want to be able to display + // localizable error messages. Catchers of this exception should use + // _GetErrorText (in App.cpp) to get the localized exception string. + const char* what() const override + { + return "Exception while loading or validating Terminal settings"; + }; + + SettingsLoadErrors Error() const noexcept { return _error; }; + + private: + const SettingsLoadErrors _error; + }; +}; diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj index 2c608df6d6b..80c61d203fb 100644 --- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj @@ -68,6 +68,7 @@ + diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index eb4ae8db0b7..d50e2e16394 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -43,6 +43,7 @@ #include #include #include +#include // WIL #include From d55ecae199c4366667a7670ca303f9949f5f1fbf Mon Sep 17 00:00:00 2001 From: Kayla Cinnamon <48369326+cinnamon-msft@users.noreply.github.com> Date: Fri, 16 Aug 2019 14:29:13 -0700 Subject: [PATCH 043/154] Add default keybinding for opening dropdown (#2365) * added keybinding for opening dropdown * fixed spacing issues * tabs spaces sadness fix * code formatting * renamed references to openNewTabDropdown and updated documentation * removed newline --- doc/cascadia/SettingsSchema.md | 1 + src/cascadia/TerminalApp/App.cpp | 12 ++++++++++++ src/cascadia/TerminalApp/App.h | 1 + src/cascadia/TerminalApp/AppKeyBindings.cpp | 4 ++++ src/cascadia/TerminalApp/AppKeyBindings.h | 1 + src/cascadia/TerminalApp/AppKeyBindings.idl | 3 +++ .../TerminalApp/AppKeyBindingsSerialization.cpp | 2 ++ src/cascadia/TerminalApp/CascadiaSettings.cpp | 5 +++++ 8 files changed, 29 insertions(+) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index d72255700bd..4ef60e9dc05 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -87,6 +87,7 @@ Bindings listed below are per the implementation in `src/cascadia/TerminalApp/Ap - copyTextWithoutNewlines - paste - newTab +- openNewTabDropdown - duplicateTab - newTabProfile0 - newTabProfile1 diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index fdc8f795da7..cbe289af1fe 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -575,6 +575,17 @@ namespace winrt::TerminalApp::implementation _newTabButton.Flyout(newTabFlyout); } + // Function Description: + // Called when the openNewTabDropdown keybinding is used. + // Adds the flyout show option to left-align the dropdown with the split button. + // Shows the dropdown flyout. + void App::_OpenNewTabDropdown() + { + Controls::Primitives::FlyoutShowOptions options{}; + options.Placement(Controls::Primitives::FlyoutPlacementMode::BottomEdgeAlignedLeft); + _newTabButton.Flyout().ShowAt(_newTabButton, options); + } + // Function Description: // - Called when the settings button is clicked. ShellExecutes the settings // file, as to open it in the default editor for .json files. Does this in @@ -645,6 +656,7 @@ namespace winrt::TerminalApp::implementation // They should all be hooked up here, regardless of whether or not // there's an actual keychord for them. bindings.NewTab([this]() { _OpenNewTab(std::nullopt); }); + bindings.OpenNewTabDropdown([this]() { _OpenNewTabDropdown(); }); bindings.DuplicateTab([this]() { _DuplicateTabViewItem(); }); bindings.CloseTab([this]() { _CloseFocusedTab(); }); bindings.ClosePane([this]() { _CloseFocusedPane(); }); diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 3d34f9656c3..c6620e9904a 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -75,6 +75,7 @@ namespace winrt::TerminalApp::implementation std::atomic _settingsReloadQueued{ false }; void _CreateNewTabFlyout(); + void _OpenNewTabDropdown(); fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& titleElement, const winrt::Windows::Foundation::IInspectable& contentElement, diff --git a/src/cascadia/TerminalApp/AppKeyBindings.cpp b/src/cascadia/TerminalApp/AppKeyBindings.cpp index 916e086f268..8a6f25b2105 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindings.cpp @@ -58,6 +58,9 @@ namespace winrt::TerminalApp::implementation case ShortcutAction::NewTab: _NewTabHandlers(); return true; + case ShortcutAction::OpenNewTabDropdown: + _OpenNewTabDropdownHandlers(); + return true; case ShortcutAction::DuplicateTab: _DuplicateTabHandlers(); return true; @@ -220,6 +223,7 @@ namespace winrt::TerminalApp::implementation DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs); + DEFINE_EVENT(AppKeyBindings, OpenNewTabDropdown,_OpenNewTabDropdownHandlers,TerminalApp::OpenNewTabDropdownEventArgs); DEFINE_EVENT(AppKeyBindings, DuplicateTab, _DuplicateTabHandlers, TerminalApp::DuplicateTabEventArgs); DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs); DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs); diff --git a/src/cascadia/TerminalApp/AppKeyBindings.h b/src/cascadia/TerminalApp/AppKeyBindings.h index 0e178065ec8..8f51c924144 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.h +++ b/src/cascadia/TerminalApp/AppKeyBindings.h @@ -42,6 +42,7 @@ namespace winrt::TerminalApp::implementation DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); DECLARE_EVENT(PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); DECLARE_EVENT(NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs); + DECLARE_EVENT(OpenNewTabDropdown,_OpenNewTabDropdownHandlers,TerminalApp::OpenNewTabDropdownEventArgs); DECLARE_EVENT(DuplicateTab, _DuplicateTabHandlers, TerminalApp::DuplicateTabEventArgs); DECLARE_EVENT(NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs); DECLARE_EVENT(NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs); diff --git a/src/cascadia/TerminalApp/AppKeyBindings.idl b/src/cascadia/TerminalApp/AppKeyBindings.idl index de195214de9..225e9e47954 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.idl +++ b/src/cascadia/TerminalApp/AppKeyBindings.idl @@ -17,6 +17,7 @@ namespace TerminalApp CopyTextWithoutNewlines, PasteText, NewTab, + OpenNewTabDropdown, DuplicateTab, NewTabProfile0, NewTabProfile1, @@ -64,6 +65,7 @@ namespace TerminalApp delegate void CopyTextEventArgs(Boolean trimWhitespace); delegate void PasteTextEventArgs(); delegate void NewTabEventArgs(); + delegate void OpenNewTabDropdownEventArgs(); delegate void DuplicateTabEventArgs(); delegate void NewTabWithProfileEventArgs(Int32 profileIndex); delegate void NewWindowEventArgs(); @@ -95,6 +97,7 @@ namespace TerminalApp event CopyTextEventArgs CopyText; event PasteTextEventArgs PasteText; event NewTabEventArgs NewTab; + event OpenNewTabDropdownEventArgs OpenNewTabDropdown; event DuplicateTabEventArgs DuplicateTab; event NewTabWithProfileEventArgs NewTabWithProfile; event NewWindowEventArgs NewWindow; diff --git a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp index 796f5957edd..4e0e752976c 100644 --- a/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindingsSerialization.cpp @@ -17,6 +17,7 @@ static constexpr std::string_view CopyTextKey{ "copy" }; static constexpr std::string_view CopyTextWithoutNewlinesKey{ "copyTextWithoutNewlines" }; static constexpr std::string_view PasteTextKey{ "paste" }; static constexpr std::string_view NewTabKey{ "newTab" }; +static constexpr std::string_view OpenNewTabDropdownKey{ "openNewTabDropdown" }; static constexpr std::string_view DuplicateTabKey{ "duplicateTab" }; static constexpr std::string_view NewTabWithProfile0Key{ "newTabProfile0" }; static constexpr std::string_view NewTabWithProfile1Key{ "newTabProfile1" }; @@ -74,6 +75,7 @@ static const std::map> commandName { CopyTextWithoutNewlinesKey, ShortcutAction::CopyTextWithoutNewlines }, { PasteTextKey, ShortcutAction::PasteText }, { NewTabKey, ShortcutAction::NewTab }, + { OpenNewTabDropdownKey, ShortcutAction::OpenNewTabDropdown }, { DuplicateTabKey, ShortcutAction::DuplicateTab }, { NewTabWithProfile0Key, ShortcutAction::NewTabProfile0 }, { NewTabWithProfile1Key, ShortcutAction::NewTabProfile1 }, diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 6fe28a9b4f5..823c7693c2d 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -296,6 +296,11 @@ void CascadiaSettings::_CreateDefaultKeybindings() keyBindings.SetKeyBinding(ShortcutAction::NewTab, KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift, static_cast('T') }); + + keyBindings.SetKeyBinding(ShortcutAction::OpenNewTabDropdown, + KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift, + static_cast(' ') }); + keyBindings.SetKeyBinding(ShortcutAction::DuplicateTab, KeyChord{ KeyModifiers::Ctrl | KeyModifiers::Shift, static_cast('D') }); From c70fb49ab58367f49fadad9adf348f97b0a6f694 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Aug 2019 16:33:45 -0500 Subject: [PATCH 044/154] Add a spec draft for Keybindings Arguments (#1349) * Add a spec draft for Keybindings Arguments. Specs #1142. Just read the spec :) * Apply suggestions from code review Co-Authored-By: Carlos Zamora * Include notes on reliability, security, and `Handle`ing Keybinding Args * Add some extra details from review * Split up ActionArgs and ActionEventArgs * Clarify _not_ handling an action * Add some notes on parsing args * Add some future considerations on extensions * Updating spec to remove the bulk of the `IActionArgs` and `IActionEventArgs` implementations, as they're redundant. --- doc/cascadia/Keybindings-Arguments.md | 362 ++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 doc/cascadia/Keybindings-Arguments.md diff --git a/doc/cascadia/Keybindings-Arguments.md b/doc/cascadia/Keybindings-Arguments.md new file mode 100644 index 00000000000..1dcd08ba1ad --- /dev/null +++ b/doc/cascadia/Keybindings-Arguments.md @@ -0,0 +1,362 @@ +--- +author: Mike Griese @zadjii-msft +created on: 2019-06-19 +last updated: 2019-07-14 +issue id: 1142 +--- + +# Arbitrary Keybindings Arguments + +## Abstract + +The goal of this change is to both simplify the keybindings, and also enable far +more flexibility when editing a user's keybindings. + +Currently, we have many actions that are very similar in implementation - for +example, `newTabProfile0`, `newTabProfile1`, `newTabProfile2`, etc. All these +actions are _fundamentally_ the same function. However, we've needed to define 9 +different actions to enable the user to provide different values to the `newTab` +function. + +With this change, we'll be able to remove these _essentially_ duplicated events, +and allow the user to specify arbitrary arguments to these functions. + +## Inspiration + +Largely inspired by the keybindings in VsCode and Sublime Text. Additionally, +much of the content regarding keybinding events being "handled" was designed as +a solution for [#2285]. + +## Solution Design + +We'll need to introduce args to some actions that we already have defined. These +are the actions I'm thinking about when writing this spec: + +```csharp + // These events already exist like this: + delegate void NewTabWithProfileEventArgs(Int32 profileIndex); + delegate void SwitchToTabEventArgs(Int32 profileIndex); + delegate void ResizePaneEventArgs(Direction direction); + delegate void MoveFocusEventArgs(Direction direction); + + // These events either exist in another form or don't exist. + delegate void CopyTextEventArgs(Boolean copyWhitespace); + delegate void ScrollEventArgs(Int32 numLines); + delegate void SplitProfileEventArgs(Orientation splitOrientation, Int32 profileIndex); +``` + +Ideally, after this change, the bindings for these actions would look something +like the following: + +```js +{ "keys": ["ctrl+shift+1"], "command": "newTabProfile", "args": { "profileIndex":0 } }, +{ "keys": ["ctrl+shift+2"], "command": "newTabProfile", "args": { "profileIndex":1 } }, +// etc... + +{ "keys": ["alt+1"], "command": "switchToTab", "args": { "index":0 } }, +{ "keys": ["alt+2"], "command": "switchToTab", "args": { "index":1 } }, +// etc... + +{ "keys": ["alt+shift+down"], "command": "resizePane", "args": { "direction":"down" } }, +{ "keys": ["alt+shift+up"], "command": "resizePane", "args": { "direction":"up" } }, +// etc... + +{ "keys": ["alt+down"], "command": "moveFocus", "args": { "direction":"down" } }, +{ "keys": ["alt+up"], "command": "moveFocus", "args": { "direction":"up" } }, +// etc... + +{ "keys": ["ctrl+c"], "command": "copy", "args": { "copyWhitespace":true } }, +{ "keys": ["ctrl+shift+c"], "command": "copy", "args": { "copyWhitespace":false } }, + +{ "keys": ["ctrl+shift+down"], "command": "scroll", "args": { "numLines":1 } }, +{ "keys": ["ctrl+shift+up"], "command": "scroll", "args": { "numLines":-1 } }, + +{ "keys": ["ctrl+alt+1"], "command": "splitProfile", "args": { "orientation":"vertical", "profileIndex": 0 } }, +{ "keys": ["ctrl+alt+shift+1"], "command": "splitProfile", "args": { "orientation":"horizontal", "profileIndex": 0 } }, +{ "keys": ["ctrl+alt+2"], "command": "splitProfile", "args": { "orientation":"vertical", "profileIndex": 1 } }, +{ "keys": ["ctrl+alt+shift+2"], "command": "splitProfile", "args": { "orientation":"horizontal", "profileIndex": 1 } }, +// etc... +``` + +Note that instead of having 9 different `newTabProfile` actions, we have a +singular `newTabProfile` action, and that action requires a `profileIndex` in +the `args` object. + +Also, pay attention to the last set of keybindings, the `splitProfile` ones. +This is a function that requires two arguments, both a `orientation` and a +`profileIndex`. Before this change we would have needed to create 20 separate +actions (10 profile indicies * 2 directions) to handle these cases. Now it can +be done with a single action that can be much more flexible in its +implementation. + +### Parsing KeyBinding Arguments + +We'll add two new interfaces: `IActionArgs` and `IActionEventArgs`. Classes that +implement `IActionArgs` will contain all the per-action args, like +`CopyWhitespace` or `ProfileIndex`. `IActionArgs` by itself will be an empty +interface, but all other arguments will derive from it. `IActionEventArgs` will +have a single property `Handled`, which will be used for indicating if a +particular event was processed or not. When parsing args, we'll build +`IActionArgs` to contain all the parameters. When dispatching events, we'll +build `IActionEventArgs` using the `IActionArgs` to set all the parameter values. + +All current keybinding events will be changed from their current types to +`TypedEventHandler`s. These `TypedEventHandler`s second param will always be an +instance of `IActionEventArgs`. So for example: + +```csharp + +delegate void CopyTextEventArgs(); +delegate void NewTabEventArgs(); +delegate void NewTabWithProfileEventArgs(Int32 profileIndex); +// ... + +[default_interface] +runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings +{ + event CopyTextEventArgs CopyText; + event NewTabEventArgs NewTab; + event NewTabWithProfileEventArgs NewTabWithProfile; +``` + +Becomes: + +```csharp +interface IActionArgs { /* Empty */ } + +runtimeclass ActionEventArgs +{ + Boolean Handled; + ActionArgs Args; +} + +runtimeclass CopyTextArgs : IActionArgs +{ + Boolean CopyWhitespace; +} + +runtimeclass NewTabWithProfileArgs : IActionArgs +{ + Int32 ProfileIndex; +} +runtimeclass NewTabWithProfileEventArgs : NewTabWithProfileArgs, IActionArgs { } + +[default_interface] +runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings +{ + event Windows.Foundation.TypedEventHandler CopyText; + event Windows.Foundation.TypedEventHandler NewTab; + event Windows.Foundation.TypedEventHandler NewTabWithProfile; +``` + +In this above example, the `CopyTextArgs` class actually contains all the +potential arguments to the Copy action. `ActionEventArgs` is the class that +holds any `ActionArgs`. When we parse the arguments, we'll build a +`CopyTextArgs`, and when we're dispatching the event, we'll build a +`ActionEventArgs` that holds a `CopyTextArgs` as its `Args` value, and dispatch +the `ActionEventArgs` object. + + +We'll also change our existing map in the `AppKeyBindings` implementation. +Currently, it's a `std::unordered_map`, which +uses the `KeyChord` to lookup the `ShortcutAction`. We'll need to introduce a +new type `ActionAndArgs`: + +```csharp +runtimeclass ActionAndArgs +{ + ShortcutAction Action; + IActionArgs Args; +} +``` + +and we'll change the map in `AppKeyBindings` to a `std::unordered_map`. + +When we're parsing keybindings, we'll need to construct args for each of the +events to go with each binding. When we find some key chord bound to a given +Action, we'll construct the `IActionArgs` for that action. For many actions, +these args will be an empty class. However, when we do find an action that needs +additional parsing, `AppKeyBindingsSerialization` will do the extra work to +parse the args for that action. + +We'll keep a collection of functions that can be used for quickly determining +how to parse the args for an action if necessary. This map will be a +`std::unordered_map>`. For +most actions which don't require args, the function in this map will be set to +nullptr, and we'll know that the action doesn't need to parse any more args. +However, for actions that _do_ require args, we'll set up a global function that +can be used to parse a json blob into an `IActionArgs`. + +Once the `IActionArgs` is built for the keybinding, we'll set it in +`AppKeyBindings` with a updated `AppKeyBindings::SetKeyBinding` call. +`SetKeyBinding`'s signature will be updated to take a `ActionAndArgs` instead. +Should an action not need arguments, the `Args` member can be left `null` in the +`ActionAndArgs`. + +### Executing KeyBinding Actions with Arguments + +When we're handling a keybinding in `AppKeyBindings::_DoAction`, we'll trigger +the event handlers with the `IActionArgs` we've stored in the map with the +`ShortcutAction`. + +Then, in `App`, we'll handle each of these events. We set up lambdas as event +handlers for each event in `App::_HookupKeyBindings`. In each of those +functions, We'll inspect the `IActionArgs` parameter, and use args from its +implementation to call callbacks in the `App` class. We will update `App` to +have methods defined with the actual keybinding function signatures. + +Instead of: + +```c++ + void App::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept + { + // ... + bindings.NewTabWithProfile([this](const auto index) { _OpenNewTab({ index }); }); + } +``` + +The code will look like: + +```c++ + void App::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept + { + // ... + bindings.NewTabWithProfile({ this, &App::_OpenNewTab }); + } + // ... + void App::_OpenNewTab(const TerminalApp::AppKeyBindings& sender, const NewTabEventArgs& args) + { + auto profileIndex = args.ProfileIndex(); + args.Handled(true); + // ... + } +``` + +### Handling Keybinding Events + +Commmon to all implementations of `IActionArgs` is the `Handled` property. This +will let the app indicate if it was able to actually process a keybinding event +or not. While in the large majority of cases, the events will all be marked +handled, there are some scenarios where the Terminal will need to know if the +event could not be performed. For example, in the case of the `copy` event, the +Terminal is only capable of copying text if there's actually a selection active. +If there isn't a selection active, the `App` should make sure to not mark the +event as not handled (it will leave `args.Handled(false)`). The App should only +mark an event handled if it has actually dispatched the event. + +When an event is handled, we'll make sure to return `true` from +`AppKeyBindings::TryKeyChord`, so that the terminal does not actually process +that keypress. For events that were not handled by the application, the terminal +will get another chance to dispatch the keypress. + +### Serializing KeyBinding Arguments + +Similar to how we parse arguments from the json, we'll need to update the +`AppKeyBindingsSerialization` code to be able to serialize the arguments from a +particular `IActionArgs`. + +## UI/UX Design + +### Keybindings in the New Tab Dropdown + +Small modifications will need to be made to the code responsible for the new tab +dropdown. The new tab dropdown currently also displays the keybindings for each +profile in the new tab dropdown. It does this by querying for the keybinding +associated with each action. As we'll be removing the old `ShortcutAction`s that +this dropdown uses, we'll need a new way to find which key chord corresponds to +opening a given profile. + +We'll need to be able to not only lookup a keybinding by `ShortcutAction`, but +also by a `ShortcutAction` and `IActionArgs`. We'll need to update the +`AppKeyBindings::GetKeyBinding` method to also accept a `IActionArgs`. We'll +also probably want each `IActionArgs` implementation to define an +`Equals(IActionArgs)` method, so that we can easily check if two different +`IActionArgs` are the same in this method. + +## Capabilities +### Accessibility + +N/A + +### Security + +This should not introduce any _new_ security concerns. We're relying on the +security of jsoncpp for parsing json. Adding new keys to the settings file +will rely on jsoncpp's ability to securely parse those json values. + +### Reliability + +We'll need to make sure that invalid keybindings are ignored. Currently, we +already gracefully ignore keybindings that have invalid `keys` or invalid +`commands`. We'll need to add additional validation on invalid sets of `args`. +When we're parsing the args from a Json blob, we'll make sure to only ever look +for keys we're expecting, and ignore everything else. + +If a keybinding requires certain args, but those args are not provided, we'll +need to make sure those args each have reasonable default values to use. If for +any reason a reasonable default can't be used for a keybinding argument, then +we'll need to make sure to display an error dialog to the user for that +scenario. + +When we're re-serializing settings, we'll only know about the keybinding arg +keys that were successfully parsed. Other keys will be lost on re-serialization. + +### Compatibility + +This change will need to carefully be crafted to enable upgrading the legacy +keybindings seamlessly. For most actions, the upgrade should be seamless. Since +they already don't have args, their serializations will remain exactly the same. + +However, for the following actions that we'll be removing in favor of actions +with arguments, we'll need to leave legacy deserialization in place to be able +to find these old actions, and automatically build the correct `IActionArgs` +for them: + +* `newTabProfile` + - We'll need to make sure to build args with the right `profileIndex` + corresponding to the old action. +* `switchToTab` + - We'll need to make sure to build args with the right `index` corresponding + to the old action. +* `resizePane` and `moveFocus` + - We'll need to make sure to build args with the right `direction` + corresponding to the old action. +* `scroll` + - We'll need to make sure to build args with the right `amount` value + corresponding to the old action. `Up` will be -1, and `Down` will be 1. + +### Performance, Power, and Efficiency + +N/A + +## Potential Issues + +N/A + +## Future considerations + +* Should we support some sort of conversion from num keys to an automatic arg? + For example, by default, Alt+<N> to focuses the + Nth tab. Currently, those are 8 separate entries in the keybindings. Should we + enable some way for them be combined into a single binding entry, where the + binding automatically recieves the number pressed as an arg? I couldn't find + any prior art of this, so it doesn't seem worth it to try and invent + currently. This might be something that we want to loop back on, but for the + time being, it remains out of scope of this PR. +* When we inevitable support extensions, we'll need to allow extensions to also + be able to support their own custom keybindings and args. We'll probably want + to pass the settings to the extension to have the extension parse its own + settings. We'll want to be able to ask the extension for its own set of + `ActionAndArgs`[1] that it builds from the `keybindings`. Once we + have that set of actions, we'll be able to store them locally, and dispatch + them quickly. + - [1] We probably won't be able to use the `ActionAndArgs` class directly, + since that class is specific to the actions we define. We'll need another + way for extenstions to be able to uniquely identify their own actions. + +## Resources + +N/A + +[#2285]: https://github.com/microsoft/terminal/issues/2285 From 734fc1dcc6de4315d4cc91944c5ea83b7b8a7e1a Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 16 Aug 2019 17:43:51 -0500 Subject: [PATCH 045/154] Don't copy text if there's no selection (#2446) This commit also transitions our keybinding events and event handlers to a TypedEventHandler model with an "event args" class, as specified in the keybinding arguments specification (#1349). In short, every event can be marked Handled independently, and a Handled event will stop bubbling out to the terminal. An unhandled event will be passed off to the terminal as a standard keypress. This unifies our keybinding event model and provides a convenient place for binding arguments to live. Fixes #2285. Related to #1349, #1142. --- src/cascadia/TerminalApp/ActionArgs.cpp | 13 + src/cascadia/TerminalApp/ActionArgs.h | 69 +++ src/cascadia/TerminalApp/ActionArgs.idl | 54 +++ src/cascadia/TerminalApp/App.cpp | 56 +-- src/cascadia/TerminalApp/App.h | 28 +- .../TerminalApp/AppActionHandlers.cpp | 183 ++++++++ src/cascadia/TerminalApp/AppKeyBindings.cpp | 400 ++++++++++++------ src/cascadia/TerminalApp/AppKeyBindings.h | 49 +-- src/cascadia/TerminalApp/AppKeyBindings.idl | 82 ++-- .../TerminalApp/lib/TerminalAppLib.vcxproj | 14 +- src/cascadia/TerminalControl/TermControl.cpp | 17 +- src/cascadia/TerminalControl/TermControl.h | 3 +- src/cascadia/TerminalControl/TermControl.idl | 6 +- src/cascadia/inc/cppwinrt_utils.h | 36 ++ 14 files changed, 773 insertions(+), 237 deletions(-) create mode 100644 src/cascadia/TerminalApp/ActionArgs.cpp create mode 100644 src/cascadia/TerminalApp/ActionArgs.h create mode 100644 src/cascadia/TerminalApp/ActionArgs.idl create mode 100644 src/cascadia/TerminalApp/AppActionHandlers.cpp diff --git a/src/cascadia/TerminalApp/ActionArgs.cpp b/src/cascadia/TerminalApp/ActionArgs.cpp new file mode 100644 index 00000000000..ff3e2298185 --- /dev/null +++ b/src/cascadia/TerminalApp/ActionArgs.cpp @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "ActionArgs.h" + +#include "ActionEventArgs.g.cpp" +#include "CopyTextArgs.g.cpp" +#include "NewTabWithProfileArgs.g.cpp" +#include "SwitchToTabArgs.g.cpp" +#include "ResizePaneArgs.g.cpp" +#include "MoveFocusArgs.g.cpp" diff --git a/src/cascadia/TerminalApp/ActionArgs.h b/src/cascadia/TerminalApp/ActionArgs.h new file mode 100644 index 00000000000..3bd56fe0d1b --- /dev/null +++ b/src/cascadia/TerminalApp/ActionArgs.h @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +// HEY YOU: When adding ActionArgs types, make sure to add the corresponding +// *.g.cpp to ActionArgs.cpp! +#include "ActionEventArgs.g.h" +#include "CopyTextArgs.g.h" +#include "NewTabWithProfileArgs.g.h" +#include "SwitchToTabArgs.g.h" +#include "ResizePaneArgs.g.h" +#include "MoveFocusArgs.g.h" + +#include "../../cascadia/inc/cppwinrt_utils.h" + +// Notes on defining ActionArgs and ActionEventArgs: +// * All properties specific to an action should be defined as an ActionArgs +// class that implements IActionArgs +// * ActionEventArgs holds a single IActionArgs. For events that don't need +// additional args, this can be nullptr. + +namespace winrt::TerminalApp::implementation +{ + struct ActionEventArgs : public ActionEventArgsT + { + ActionEventArgs() = default; + ActionEventArgs(const TerminalApp::IActionArgs& args) : + _ActionArgs{ args } {}; + GETSET_PROPERTY(IActionArgs, ActionArgs, nullptr); + GETSET_PROPERTY(bool, Handled, false); + }; + + struct CopyTextArgs : public CopyTextArgsT + { + CopyTextArgs() = default; + GETSET_PROPERTY(bool, TrimWhitespace, false); + }; + + struct NewTabWithProfileArgs : public NewTabWithProfileArgsT + { + NewTabWithProfileArgs() = default; + GETSET_PROPERTY(int32_t, ProfileIndex, 0); + }; + + struct SwitchToTabArgs : public SwitchToTabArgsT + { + SwitchToTabArgs() = default; + GETSET_PROPERTY(int32_t, TabIndex, 0); + }; + + struct ResizePaneArgs : public ResizePaneArgsT + { + ResizePaneArgs() = default; + GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left); + }; + + struct MoveFocusArgs : public MoveFocusArgsT + { + MoveFocusArgs() = default; + GETSET_PROPERTY(TerminalApp::Direction, Direction, TerminalApp::Direction::Left); + }; + +} + +namespace winrt::TerminalApp::factory_implementation +{ + BASIC_FACTORY(ActionEventArgs); +} diff --git a/src/cascadia/TerminalApp/ActionArgs.idl b/src/cascadia/TerminalApp/ActionArgs.idl new file mode 100644 index 00000000000..58be7ea26b6 --- /dev/null +++ b/src/cascadia/TerminalApp/ActionArgs.idl @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace TerminalApp +{ + // An empty interface must specify an explicit [uuid] to ensure uniqueness. + // We also manually have to specify a "version" attribute to make the compiler happy. + [uuid("191C2BDE-1A60-4BAB-9765-D850F0EF2CAC")][version(1)] interface IActionArgs{}; + + interface IActionEventArgs + { + Boolean Handled; + IActionArgs ActionArgs { get; }; + }; + + enum Direction + { + Left = 0, + Right, + Up, + Down + }; + + [default_interface] runtimeclass ActionEventArgs : IActionEventArgs + { + ActionEventArgs(IActionArgs args); + }; + + [default_interface] runtimeclass CopyTextArgs : IActionArgs + { + Boolean TrimWhitespace { get; }; + }; + + [default_interface] runtimeclass NewTabWithProfileArgs : IActionArgs + { + Int32 ProfileIndex { get; }; + }; + + [default_interface] runtimeclass SwitchToTabArgs : IActionArgs + { + Int32 TabIndex { get; }; + }; + + [default_interface] runtimeclass ResizePaneArgs : IActionArgs + { + Direction Direction { get; }; + }; + + [default_interface] runtimeclass MoveFocusArgs : IActionArgs + { + Direction Direction { get; }; + }; + +} diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index cbe289af1fe..da2fe34c8ff 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -655,26 +655,27 @@ namespace winrt::TerminalApp::implementation // Hook up the KeyBinding object's events to our handlers. // They should all be hooked up here, regardless of whether or not // there's an actual keychord for them. - bindings.NewTab([this]() { _OpenNewTab(std::nullopt); }); - bindings.OpenNewTabDropdown([this]() { _OpenNewTabDropdown(); }); - bindings.DuplicateTab([this]() { _DuplicateTabViewItem(); }); - bindings.CloseTab([this]() { _CloseFocusedTab(); }); - bindings.ClosePane([this]() { _CloseFocusedPane(); }); - bindings.NewTabWithProfile([this](const auto index) { _OpenNewTab({ index }); }); - bindings.ScrollUp([this]() { _Scroll(-1); }); - bindings.ScrollDown([this]() { _Scroll(1); }); - bindings.NextTab([this]() { _SelectNextTab(true); }); - bindings.PrevTab([this]() { _SelectNextTab(false); }); - bindings.SplitVertical([this]() { _SplitVertical(std::nullopt); }); - bindings.SplitHorizontal([this]() { _SplitHorizontal(std::nullopt); }); - bindings.ScrollUpPage([this]() { _ScrollPage(-1); }); - bindings.ScrollDownPage([this]() { _ScrollPage(1); }); - bindings.SwitchToTab([this](const auto index) { _SelectTab({ index }); }); - bindings.OpenSettings([this]() { _OpenSettings(); }); - bindings.ResizePane([this](const auto direction) { _ResizePane(direction); }); - bindings.MoveFocus([this](const auto direction) { _MoveFocus(direction); }); - bindings.CopyText([this](const auto trimWhitespace) { _CopyText(trimWhitespace); }); - bindings.PasteText([this]() { _PasteText(); }); + + bindings.NewTab({ this, &App::_HandleNewTab }); + bindings.OpenNewTabDropdown({ this, &App::_HandleOpenNewTabDropdown }); + bindings.DuplicateTab({ this, &App::_HandleDuplicateTab }); + bindings.CloseTab({ this, &App::_HandleCloseTab }); + bindings.ClosePane({ this, &App::_HandleClosePane }); + bindings.ScrollUp({ this, &App::_HandleScrollUp }); + bindings.ScrollDown({ this, &App::_HandleScrollDown }); + bindings.NextTab({ this, &App::_HandleNextTab }); + bindings.PrevTab({ this, &App::_HandlePrevTab }); + bindings.SplitVertical({ this, &App::_HandleSplitVertical }); + bindings.SplitHorizontal({ this, &App::_HandleSplitHorizontal }); + bindings.ScrollUpPage({ this, &App::_HandleScrollUpPage }); + bindings.ScrollDownPage({ this, &App::_HandleScrollDownPage }); + bindings.OpenSettings({ this, &App::_HandleOpenSettings }); + bindings.PasteText({ this, &App::_HandlePasteText }); + bindings.NewTabWithProfile({ this, &App::_HandleNewTabWithProfile }); + bindings.SwitchToTab({ this, &App::_HandleSwitchToTab }); + bindings.ResizePane({ this, &App::_HandleResizePane }); + bindings.MoveFocus({ this, &App::_HandleMoveFocus }); + bindings.CopyText({ this, &App::_HandleCopyText }); } // Method Description: @@ -1233,10 +1234,12 @@ namespace winrt::TerminalApp::implementation // Arguments: // - trimTrailingWhitespace: enable removing any whitespace from copied selection // and get text to appear on separate lines. - void App::_CopyText(const bool trimTrailingWhitespace) + // Return Value: + // - true iff we we able to copy text (if a selection was active) + bool App::_CopyText(const bool trimTrailingWhitespace) { const auto control = _GetFocusedControl(); - control.CopySelectionToClipboard(trimTrailingWhitespace); + return control.CopySelectionToClipboard(trimTrailingWhitespace); } // Method Description: @@ -1261,13 +1264,18 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Sets focus to the desired tab. - void App::_SelectTab(const int tabIndex) + // - Sets focus to the desired tab. Returns false if the provided tabIndex + // is greater than the number of tabs we have. + // Return Value: + // true iff we were able to select that tab index, false otherwise + bool App::_SelectTab(const int tabIndex) { if (tabIndex >= 0 && tabIndex < gsl::narrow_cast(_tabs.size())) { _SetFocusedTabIndex(tabIndex); + return true; } + return false; } // Method Description: diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index c6620e9904a..5d993e3410b 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -113,13 +113,13 @@ namespace winrt::TerminalApp::implementation void _CloseFocusedTab(); void _CloseFocusedPane(); void _SelectNextTab(const bool bMoveRight); - void _SelectTab(const int tabIndex); + bool _SelectTab(const int tabIndex); void _SetFocusedTabIndex(int tabIndex); int _GetFocusedTabIndex() const; void _Scroll(int delta); - void _CopyText(const bool trimTrailingWhitespace); + bool _CopyText(const bool trimTrailingWhitespace); void _PasteText(); void _SplitVertical(const std::optional& profileGuid); void _SplitHorizontal(const std::optional& profileGuid); @@ -150,6 +150,30 @@ namespace winrt::TerminalApp::implementation void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs); static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord); + +#pragma region ActionHandlers + // These are all defined in AppActionHandlers.cpp + void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); +#pragma endregion }; } diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp new file mode 100644 index 00000000000..cec06ee8f0f --- /dev/null +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "App.h" + +#include "TerminalPage.h" +#include "Utils.h" + +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Text; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::System; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Microsoft::Terminal::Settings; +using namespace winrt::Microsoft::Terminal::TerminalControl; +using namespace winrt::Microsoft::Terminal::TerminalConnection; +using namespace ::TerminalApp; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +namespace winrt::TerminalApp::implementation +{ + void App::_HandleNewTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _OpenNewTab(std::nullopt); + args.Handled(true); + } + void App::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _OpenNewTabDropdown(); + args.Handled(true); + } + + void App::_HandleDuplicateTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _DuplicateTabViewItem(); + args.Handled(true); + } + + void App::_HandleCloseTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _CloseFocusedTab(); + args.Handled(true); + } + + void App::_HandleClosePane(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _CloseFocusedPane(); + args.Handled(true); + } + + void App::_HandleScrollUp(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _Scroll(-1); + args.Handled(true); + } + + void App::_HandleScrollDown(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _Scroll(1); + args.Handled(true); + } + + void App::_HandleNextTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _SelectNextTab(true); + args.Handled(true); + } + + void App::_HandlePrevTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _SelectNextTab(false); + args.Handled(true); + } + + void App::_HandleSplitVertical(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _SplitVertical(std::nullopt); + args.Handled(true); + } + + void App::_HandleSplitHorizontal(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _SplitHorizontal(std::nullopt); + args.Handled(true); + } + + void App::_HandleScrollUpPage(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _ScrollPage(-1); + args.Handled(true); + } + + void App::_HandleScrollDownPage(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _ScrollPage(1); + args.Handled(true); + } + + void App::_HandleOpenSettings(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _OpenSettings(); + args.Handled(true); + } + + void App::_HandlePasteText(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + _PasteText(); + args.Handled(true); + } + + void App::_HandleNewTabWithProfile(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + _OpenNewTab({ realArgs.ProfileIndex() }); + args.Handled(true); + } + } + + void App::_HandleSwitchToTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + const auto handled = _SelectTab({ realArgs.TabIndex() }); + args.Handled(handled); + } + } + + void App::_HandleResizePane(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + _ResizePane(realArgs.Direction()); + args.Handled(true); + } + } + + void App::_HandleMoveFocus(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + _MoveFocus(realArgs.Direction()); + args.Handled(true); + } + } + + void App::_HandleCopyText(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) + { + if (const auto& realArgs = args.ActionArgs().try_as()) + { + const auto handled = _CopyText(realArgs.TrimWhitespace()); + args.Handled(handled); + } + } + +} diff --git a/src/cascadia/TerminalApp/AppKeyBindings.cpp b/src/cascadia/TerminalApp/AppKeyBindings.cpp index 8a6f25b2105..7e35e139eac 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.cpp +++ b/src/cascadia/TerminalApp/AppKeyBindings.cpp @@ -9,7 +9,6 @@ using namespace winrt::Microsoft::Terminal; using namespace winrt::TerminalApp; -using namespace winrt::Windows::Data::Json; namespace winrt::TerminalApp::implementation { @@ -47,146 +46,337 @@ namespace winrt::TerminalApp::implementation switch (action) { case ShortcutAction::CopyText: - _CopyTextHandlers(true); - return true; + { + auto args = winrt::make_self(); + args->TrimWhitespace(true); + auto eventArgs = winrt::make_self(*args); + _CopyTextHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::CopyTextWithoutNewlines: - _CopyTextHandlers(false); - return true; + { + auto args = winrt::make_self(); + args->TrimWhitespace(false); + auto eventArgs = winrt::make_self(*args); + _CopyTextHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::PasteText: - _PasteTextHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _PasteTextHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTab: - _NewTabHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _NewTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::OpenNewTabDropdown: - _OpenNewTabDropdownHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _OpenNewTabDropdownHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::DuplicateTab: - _DuplicateTabHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _DuplicateTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::OpenSettings: - _OpenSettingsHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _OpenSettingsHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile0: - _NewTabWithProfileHandlers(0); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(0); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile1: - _NewTabWithProfileHandlers(1); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(1); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile2: - _NewTabWithProfileHandlers(2); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(2); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile3: - _NewTabWithProfileHandlers(3); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(3); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile4: - _NewTabWithProfileHandlers(4); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(4); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile5: - _NewTabWithProfileHandlers(5); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(5); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile6: - _NewTabWithProfileHandlers(6); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(6); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile7: - _NewTabWithProfileHandlers(7); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(7); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewTabProfile8: - _NewTabWithProfileHandlers(8); - return true; + { + auto args = winrt::make_self(); + args->ProfileIndex(8); + auto eventArgs = winrt::make_self(*args); + _NewTabWithProfileHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NewWindow: - _NewWindowHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _NewWindowHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::CloseWindow: - _CloseWindowHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _CloseWindowHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::CloseTab: - _CloseTabHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _CloseTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ClosePane: - _ClosePaneHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _ClosePaneHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ScrollUp: - _ScrollUpHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _ScrollUpHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ScrollDown: - _ScrollDownHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _ScrollDownHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ScrollUpPage: - _ScrollUpPageHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _ScrollUpPageHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ScrollDownPage: - _ScrollDownPageHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _ScrollDownPageHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::NextTab: - _NextTabHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _NextTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::PrevTab: - _PrevTabHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _PrevTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SplitVertical: - _SplitVerticalHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _SplitVerticalHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SplitHorizontal: - _SplitHorizontalHandlers(); - return true; + { + auto eventArgs = winrt::make_self(); + _SplitHorizontalHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab0: - _SwitchToTabHandlers(0); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(0); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab1: - _SwitchToTabHandlers(1); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(1); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab2: - _SwitchToTabHandlers(2); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(2); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab3: - _SwitchToTabHandlers(3); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(3); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab4: - _SwitchToTabHandlers(4); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(4); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab5: - _SwitchToTabHandlers(5); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(5); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab6: - _SwitchToTabHandlers(6); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(6); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab7: - _SwitchToTabHandlers(7); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(7); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::SwitchToTab8: - _SwitchToTabHandlers(8); - return true; + { + auto args = winrt::make_self(); + args->TabIndex(8); + auto eventArgs = winrt::make_self(*args); + _SwitchToTabHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ResizePaneLeft: - _ResizePaneHandlers(Direction::Left); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Left); + auto eventArgs = winrt::make_self(*args); + _ResizePaneHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ResizePaneRight: - _ResizePaneHandlers(Direction::Right); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Right); + auto eventArgs = winrt::make_self(*args); + _ResizePaneHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ResizePaneUp: - _ResizePaneHandlers(Direction::Up); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Up); + auto eventArgs = winrt::make_self(*args); + _ResizePaneHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::ResizePaneDown: - _ResizePaneHandlers(Direction::Down); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Down); + auto eventArgs = winrt::make_self(*args); + _ResizePaneHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::MoveFocusLeft: - _MoveFocusHandlers(Direction::Left); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Left); + auto eventArgs = winrt::make_self(*args); + _MoveFocusHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::MoveFocusRight: - _MoveFocusHandlers(Direction::Right); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Right); + auto eventArgs = winrt::make_self(*args); + _MoveFocusHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::MoveFocusUp: - _MoveFocusHandlers(Direction::Up); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Up); + auto eventArgs = winrt::make_self(*args); + _MoveFocusHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } case ShortcutAction::MoveFocusDown: - _MoveFocusHandlers(Direction::Down); - return true; + { + auto args = winrt::make_self(); + args->Direction(Direction::Down); + auto eventArgs = winrt::make_self(*args); + _MoveFocusHandlers(*this, *eventArgs); + return eventArgs->Handled(); + } default: return false; } @@ -217,32 +407,4 @@ namespace winrt::TerminalApp::implementation return keyModifiers; } - - // -------------------------------- Events --------------------------------- - // clang-format off - DEFINE_EVENT(AppKeyBindings, CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); - DEFINE_EVENT(AppKeyBindings, PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); - DEFINE_EVENT(AppKeyBindings, NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs); - DEFINE_EVENT(AppKeyBindings, OpenNewTabDropdown,_OpenNewTabDropdownHandlers,TerminalApp::OpenNewTabDropdownEventArgs); - DEFINE_EVENT(AppKeyBindings, DuplicateTab, _DuplicateTabHandlers, TerminalApp::DuplicateTabEventArgs); - DEFINE_EVENT(AppKeyBindings, NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs); - DEFINE_EVENT(AppKeyBindings, NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs); - DEFINE_EVENT(AppKeyBindings, CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs); - DEFINE_EVENT(AppKeyBindings, CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs); - DEFINE_EVENT(AppKeyBindings, ClosePane, _ClosePaneHandlers, TerminalApp::ClosePaneEventArgs); - DEFINE_EVENT(AppKeyBindings, SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs); - DEFINE_EVENT(AppKeyBindings, NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs); - DEFINE_EVENT(AppKeyBindings, PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs); - DEFINE_EVENT(AppKeyBindings, SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs); - DEFINE_EVENT(AppKeyBindings, SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs); - DEFINE_EVENT(AppKeyBindings, IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs); - DEFINE_EVENT(AppKeyBindings, DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs); - DEFINE_EVENT(AppKeyBindings, ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs); - DEFINE_EVENT(AppKeyBindings, ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs); - DEFINE_EVENT(AppKeyBindings, ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs); - DEFINE_EVENT(AppKeyBindings, ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs); - DEFINE_EVENT(AppKeyBindings, OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs); - DEFINE_EVENT(AppKeyBindings, ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs); - DEFINE_EVENT(AppKeyBindings, MoveFocus, _MoveFocusHandlers, TerminalApp::MoveFocusEventArgs); - // clang-format on } diff --git a/src/cascadia/TerminalApp/AppKeyBindings.h b/src/cascadia/TerminalApp/AppKeyBindings.h index 8f51c924144..0ad405378ef 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.h +++ b/src/cascadia/TerminalApp/AppKeyBindings.h @@ -4,6 +4,7 @@ #pragma once #include "AppKeyBindings.g.h" +#include "ActionArgs.h" #include "..\inc\cppwinrt_utils.h" namespace winrt::TerminalApp::implementation @@ -39,30 +40,30 @@ namespace winrt::TerminalApp::implementation static Windows::System::VirtualKeyModifiers ConvertVKModifiers(winrt::Microsoft::Terminal::Settings::KeyModifiers modifiers); // clang-format off - DECLARE_EVENT(CopyText, _CopyTextHandlers, TerminalApp::CopyTextEventArgs); - DECLARE_EVENT(PasteText, _PasteTextHandlers, TerminalApp::PasteTextEventArgs); - DECLARE_EVENT(NewTab, _NewTabHandlers, TerminalApp::NewTabEventArgs); - DECLARE_EVENT(OpenNewTabDropdown,_OpenNewTabDropdownHandlers,TerminalApp::OpenNewTabDropdownEventArgs); - DECLARE_EVENT(DuplicateTab, _DuplicateTabHandlers, TerminalApp::DuplicateTabEventArgs); - DECLARE_EVENT(NewTabWithProfile, _NewTabWithProfileHandlers, TerminalApp::NewTabWithProfileEventArgs); - DECLARE_EVENT(NewWindow, _NewWindowHandlers, TerminalApp::NewWindowEventArgs); - DECLARE_EVENT(CloseWindow, _CloseWindowHandlers, TerminalApp::CloseWindowEventArgs); - DECLARE_EVENT(CloseTab, _CloseTabHandlers, TerminalApp::CloseTabEventArgs); - DECLARE_EVENT(ClosePane, _ClosePaneHandlers, TerminalApp::ClosePaneEventArgs); - DECLARE_EVENT(SwitchToTab, _SwitchToTabHandlers, TerminalApp::SwitchToTabEventArgs); - DECLARE_EVENT(NextTab, _NextTabHandlers, TerminalApp::NextTabEventArgs); - DECLARE_EVENT(PrevTab, _PrevTabHandlers, TerminalApp::PrevTabEventArgs); - DECLARE_EVENT(SplitVertical, _SplitVerticalHandlers, TerminalApp::SplitVerticalEventArgs); - DECLARE_EVENT(SplitHorizontal, _SplitHorizontalHandlers, TerminalApp::SplitHorizontalEventArgs); - DECLARE_EVENT(IncreaseFontSize, _IncreaseFontSizeHandlers, TerminalApp::IncreaseFontSizeEventArgs); - DECLARE_EVENT(DecreaseFontSize, _DecreaseFontSizeHandlers, TerminalApp::DecreaseFontSizeEventArgs); - DECLARE_EVENT(ScrollUp, _ScrollUpHandlers, TerminalApp::ScrollUpEventArgs); - DECLARE_EVENT(ScrollDown, _ScrollDownHandlers, TerminalApp::ScrollDownEventArgs); - DECLARE_EVENT(ScrollUpPage, _ScrollUpPageHandlers, TerminalApp::ScrollUpPageEventArgs); - DECLARE_EVENT(ScrollDownPage, _ScrollDownPageHandlers, TerminalApp::ScrollDownPageEventArgs); - DECLARE_EVENT(OpenSettings, _OpenSettingsHandlers, TerminalApp::OpenSettingsEventArgs); - DECLARE_EVENT(ResizePane, _ResizePaneHandlers, TerminalApp::ResizePaneEventArgs); - DECLARE_EVENT(MoveFocus, _MoveFocusHandlers, TerminalApp::MoveFocusEventArgs); + TYPED_EVENT(CopyText, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(PasteText, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(NewTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(OpenNewTabDropdown,TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(DuplicateTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(NewTabWithProfile, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(NewWindow, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(CloseWindow, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(CloseTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ClosePane, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(SwitchToTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(NextTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(PrevTab, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(SplitVertical, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(SplitHorizontal, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(IncreaseFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(DecreaseFontSize, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ScrollUp, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ScrollDown, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ScrollUpPage, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ScrollDownPage, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(OpenSettings, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(ResizePane, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); + TYPED_EVENT(MoveFocus, TerminalApp::AppKeyBindings, TerminalApp::ActionEventArgs); // clang-format on private: diff --git a/src/cascadia/TerminalApp/AppKeyBindings.idl b/src/cascadia/TerminalApp/AppKeyBindings.idl index 225e9e47954..8786d44ab17 100644 --- a/src/cascadia/TerminalApp/AppKeyBindings.idl +++ b/src/cascadia/TerminalApp/AppKeyBindings.idl @@ -1,16 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "../ActionArgs.idl"; namespace TerminalApp { - enum Direction - { - Left = 0, - Right, - Up, - Down - }; - enum ShortcutAction { CopyText = 0, @@ -62,31 +55,6 @@ namespace TerminalApp OpenSettings }; - delegate void CopyTextEventArgs(Boolean trimWhitespace); - delegate void PasteTextEventArgs(); - delegate void NewTabEventArgs(); - delegate void OpenNewTabDropdownEventArgs(); - delegate void DuplicateTabEventArgs(); - delegate void NewTabWithProfileEventArgs(Int32 profileIndex); - delegate void NewWindowEventArgs(); - delegate void CloseWindowEventArgs(); - delegate void CloseTabEventArgs(); - delegate void ClosePaneEventArgs(); - delegate void NextTabEventArgs(); - delegate void PrevTabEventArgs(); - delegate void SplitVerticalEventArgs(); - delegate void SplitHorizontalEventArgs(); - delegate void SwitchToTabEventArgs(Int32 profileIndex); - delegate void IncreaseFontSizeEventArgs(); - delegate void DecreaseFontSizeEventArgs(); - delegate void ScrollUpEventArgs(); - delegate void ScrollDownEventArgs(); - delegate void ScrollUpPageEventArgs(); - delegate void ScrollDownPageEventArgs(); - delegate void OpenSettingsEventArgs(); - delegate void ResizePaneEventArgs(Direction direction); - delegate void MoveFocusEventArgs(Direction direction); - [default_interface] runtimeclass AppKeyBindings : Microsoft.Terminal.Settings.IKeyBindings { AppKeyBindings(); @@ -94,29 +62,29 @@ namespace TerminalApp void SetKeyBinding(ShortcutAction action, Microsoft.Terminal.Settings.KeyChord chord); Microsoft.Terminal.Settings.KeyChord GetKeyBinding(ShortcutAction action); - event CopyTextEventArgs CopyText; - event PasteTextEventArgs PasteText; - event NewTabEventArgs NewTab; - event OpenNewTabDropdownEventArgs OpenNewTabDropdown; - event DuplicateTabEventArgs DuplicateTab; - event NewTabWithProfileEventArgs NewTabWithProfile; - event NewWindowEventArgs NewWindow; - event CloseWindowEventArgs CloseWindow; - event CloseTabEventArgs CloseTab; - event ClosePaneEventArgs ClosePane; - event SwitchToTabEventArgs SwitchToTab; - event NextTabEventArgs NextTab; - event PrevTabEventArgs PrevTab; - event SplitVerticalEventArgs SplitVertical; - event SplitHorizontalEventArgs SplitHorizontal; - event IncreaseFontSizeEventArgs IncreaseFontSize; - event DecreaseFontSizeEventArgs DecreaseFontSize; - event ScrollUpEventArgs ScrollUp; - event ScrollDownEventArgs ScrollDown; - event ScrollUpPageEventArgs ScrollUpPage; - event ScrollDownPageEventArgs ScrollDownPage; - event OpenSettingsEventArgs OpenSettings; - event ResizePaneEventArgs ResizePane; - event MoveFocusEventArgs MoveFocus; + event Windows.Foundation.TypedEventHandler CopyText; + event Windows.Foundation.TypedEventHandler PasteText; + event Windows.Foundation.TypedEventHandler NewTab; + event Windows.Foundation.TypedEventHandler OpenNewTabDropdown; + event Windows.Foundation.TypedEventHandler DuplicateTab; + event Windows.Foundation.TypedEventHandler NewTabWithProfile; + event Windows.Foundation.TypedEventHandler NewWindow; + event Windows.Foundation.TypedEventHandler CloseWindow; + event Windows.Foundation.TypedEventHandler CloseTab; + event Windows.Foundation.TypedEventHandler ClosePane; + event Windows.Foundation.TypedEventHandler SwitchToTab; + event Windows.Foundation.TypedEventHandler NextTab; + event Windows.Foundation.TypedEventHandler PrevTab; + event Windows.Foundation.TypedEventHandler SplitVertical; + event Windows.Foundation.TypedEventHandler SplitHorizontal; + event Windows.Foundation.TypedEventHandler IncreaseFontSize; + event Windows.Foundation.TypedEventHandler DecreaseFontSize; + event Windows.Foundation.TypedEventHandler ScrollUp; + event Windows.Foundation.TypedEventHandler ScrollDown; + event Windows.Foundation.TypedEventHandler ScrollUpPage; + event Windows.Foundation.TypedEventHandler ScrollDownPage; + event Windows.Foundation.TypedEventHandler OpenSettings; + event Windows.Foundation.TypedEventHandler ResizePane; + event Windows.Foundation.TypedEventHandler MoveFocus; } } diff --git a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj index 80c61d203fb..b53faad8eb4 100644 --- a/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/lib/TerminalAppLib.vcxproj @@ -70,7 +70,12 @@ - + + ../ActionArgs.idl + + + ../AppKeyBindings.idl + ../App.xaml @@ -109,9 +114,15 @@ ../AppKeyBindings.idl + + ../ActionArgs.idl + ../App.xaml + + ../App.xaml + @@ -129,6 +140,7 @@ ../App.xaml + ../MinMaxCloseControl.xaml Code diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index a6469af3ab1..3adda2dc771 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1391,15 +1391,20 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // Arguments: // - trimTrailingWhitespace: enable removing any whitespace from copied selection // and get text to appear on separate lines. - void TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace) + bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace) { - // extract text from buffer - const auto copiedData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); + if (_terminal != nullptr && _terminal->IsAreaSelected()) + { + // extract text from buffer + const auto copiedData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); - _terminal->ClearSelection(); + _terminal->ClearSelection(); - // send data up for clipboard - _clipboardCopyHandlers(copiedData); + // send data up for clipboard + _clipboardCopyHandlers(copiedData); + return true; + } + return false; } // Method Description: diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 84104c2ed99..d4af3cc2272 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -38,7 +38,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void UpdateSettings(Settings::IControlSettings newSettings); hstring Title(); - void CopySelectionToClipboard(bool trimTrailingWhitespace); + + bool CopySelectionToClipboard(bool trimTrailingWhitespace); void PasteTextFromClipboard(); void Close(); bool ShouldCloseOnExit() const noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index d9d23806324..e37b6bca393 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -13,8 +13,7 @@ namespace Microsoft.Terminal.TerminalControl void HandleClipboardData(String data); } - [default_interface] - runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl + [default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl { TermControl(); TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection); @@ -29,7 +28,8 @@ namespace Microsoft.Terminal.TerminalControl event Windows.Foundation.TypedEventHandler PasteFromClipboard; String Title { get; }; - void CopySelectionToClipboard(Boolean trimTrailingWhitespace); + + Boolean CopySelectionToClipboard(Boolean trimTrailingWhitespace); void PasteTextFromClipboard(); void Close(); Boolean ShouldCloseOnExit { get; }; diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index 8609698dfc5..810436114e7 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -57,6 +57,42 @@ private: winrt::event_token className::name(Windows::Foundation::TypedEventHandler const& handler) { return eventHandler.add(handler); } \ void className::name(winrt::event_token const& token) noexcept { eventHandler.remove(token); } +// This is a helper macro for both declaring the signature of an event, and +// defining the body. Winrt events need a method for adding a callback to the +// event and removing the callback. This macro will both declare the method +// signatures and define them both for you, because they don't really vary from +// event to event. +// Use this in a classes header if you have a Windows.Foundation.TypedEventHandler +#define TYPED_EVENT(name, sender, args) \ +public: \ + winrt::event_token name(Windows::Foundation::TypedEventHandler const& handler) { return _##name##Handlers.add(handler); } \ + void name(winrt::event_token const& token) noexcept { _##name##Handlers.remove(token); } \ + \ +private: \ + winrt::event> _##name##Handlers; + +// Use this macro to quick implement both the getter and setter for a property. +// This should only be used for simple types where there's no logic in the +// getter/setter beyond just accessing/updating the value. +#define GETSET_PROPERTY(type, name, ...) \ +public: \ + type name() const { return _##name; } \ + void name(const type& value) { _##name = value; } \ + \ +private: \ + type _##name{ __VA_ARGS__ }; + +// Use this macro for quickly defining the factory_implementation part of a +// class. CppWinrt requires these for the compiler, but more often than not, +// they require no customization. See +// https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/implements#marker-types +// and https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/static-lifetime +// for examples of when you might _not_ want to use this. +#define BASIC_FACTORY(typeName) \ + struct typeName : typeName##T \ + { \ + }; + // This is a helper method for deserializing a SAFEARRAY of // COM objects and converting it to a vector that // owns the extracted COM objects From bd47dcc8988efc8721a446869f6df1b7b082846c Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 19 Aug 2019 11:03:45 -0700 Subject: [PATCH 046/154] Accessibility: Refactor IRenderData with IUiaData (#2296) * Refactor IRenderData with IUiaData * remove duplicate tracking of active selection --- src/cascadia/TerminalControl/TermControl.cpp | 4 +- src/cascadia/TerminalControl/TermControl.h | 2 +- .../TermControlAutomationPeer.cpp | 2 +- src/cascadia/TerminalCore/Terminal.hpp | 26 ++- .../TerminalCore/terminalrenderdata.cpp | 9 +- src/host/renderData.cpp | 204 +++++++++--------- src/host/renderData.hpp | 31 ++- .../UiaTextRangeTests.cpp | 181 ++++++++-------- src/interactivity/win32/windowUiaProvider.cpp | 5 +- src/renderer/inc/IRenderData.hpp | 32 +-- src/types/IBaseData.h | 39 ++++ src/types/IUiaData.h | 48 +++++ src/types/ScreenInfoUiaProvider.cpp | 6 +- src/types/ScreenInfoUiaProvider.h | 8 +- src/types/UiaTextRange.cpp | 82 +++---- src/types/UiaTextRange.hpp | 88 ++++---- src/types/lib/types.vcxproj | 2 + src/types/lib/types.vcxproj.filters | 6 + 18 files changed, 432 insertions(+), 343 deletions(-) create mode 100644 src/types/IBaseData.h create mode 100644 src/types/IUiaData.h diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 3adda2dc771..5626488e0fa 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -347,7 +347,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return winrt::make(*this); } - ::Microsoft::Console::Render::IRenderData* TermControl::GetRenderData() const + ::Microsoft::Console::Types::IUiaData* TermControl::GetUiaData() const { return _terminal.get(); } @@ -1393,7 +1393,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // and get text to appear on separate lines. bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace) { - if (_terminal != nullptr && _terminal->IsAreaSelected()) + if (_terminal != nullptr && _terminal->IsSelectionActive()) { // extract text from buffer const auto copiedData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index d4af3cc2272..5876a8d43df 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -55,7 +55,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation ~TermControl(); Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); - ::Microsoft::Console::Render::IRenderData* GetRenderData() const; + ::Microsoft::Console::Types::IUiaData* GetUiaData() const; static Windows::Foundation::Point GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi); diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index ab887080784..565770facca 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner) : TermControlAutomationPeerT(owner), // pass owner to FrameworkElementAutomationPeer - _uiaProvider{ owner.GetRenderData(), nullptr, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; + _uiaProvider{ owner.GetUiaData(), nullptr, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; winrt::hstring TermControlAutomationPeer::GetClassNameCore() const { diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 81b397ebb35..e8c2b5d601d 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -11,6 +11,7 @@ #include "../../terminal/input/terminalInput.hpp" #include "../../types/inc/Viewport.hpp" +#include "../../types/IUiaData.h" #include "../../cascadia/terminalcore/ITerminalApi.hpp" #include "../../cascadia/terminalcore/ITerminalInput.hpp" @@ -30,7 +31,8 @@ namespace Microsoft::Terminal::Core class Microsoft::Terminal::Core::Terminal final : public Microsoft::Terminal::Core::ITerminalApi, public Microsoft::Terminal::Core::ITerminalInput, - public Microsoft::Console::Render::IRenderData + public Microsoft::Console::Render::IRenderData, + public Microsoft::Console::Types::IUiaData { public: Terminal(); @@ -86,11 +88,17 @@ class Microsoft::Terminal::Core::Terminal final : int GetScrollOffset() override; #pragma endregion -#pragma region IRenderData - // These methods are defined in TerminalRenderData.cpp +#pragma region IBaseData(base to IRenderData and IUiaData) Microsoft::Console::Types::Viewport GetViewport() noexcept override; const TextBuffer& GetTextBuffer() noexcept override; const FontInfo& GetFontInfo() noexcept override; + + void LockConsole() noexcept override; + void UnlockConsole() noexcept override; +#pragma endregion + +#pragma region IRenderData + // These methods are defined in TerminalRenderData.cpp const TextAttribute GetDefaultBrushColors() noexcept override; const COLORREF GetForegroundColor(const TextAttribute& attr) const noexcept override; const COLORREF GetBackgroundColor(const TextAttribute& attr) const noexcept override; @@ -104,8 +112,11 @@ class Microsoft::Terminal::Core::Terminal final : bool IsCursorDoubleWidth() const noexcept override; const std::vector GetOverlays() const noexcept override; const bool IsGridLineDrawingAllowed() noexcept override; +#pragma endregion + +#pragma region IUiaData std::vector GetSelectionRects() noexcept override; - bool IsAreaSelected() const override; + const bool IsSelectionActive() const noexcept; void ClearSelection() override; void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; @@ -118,13 +129,11 @@ class Microsoft::Terminal::Core::Terminal final : _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, unsigned int _start, unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, + std::function _coordToEndpoint, + std::function _endpointToCoord, std::function Clone) override; const std::wstring GetConsoleTitle() const noexcept override; - void LockConsole() noexcept override; - void UnlockConsole() noexcept override; #pragma endregion void SetWriteInputCallback(std::function pfn) noexcept; @@ -137,7 +146,6 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp - const bool IsSelectionActive() const noexcept; void DoubleClickSelection(const COORD position); void TripleClickSelection(const COORD position); void SetSelectionAnchor(const COORD position); diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index b32d0561ce5..36c75b560de 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -117,11 +117,6 @@ std::vector Terminal::GetSelectionRects() n return result; } -bool Terminal::IsAreaSelected() const -{ - return _selectionActive; -} - void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd) { SetSelectionAnchor(coordStart); @@ -137,8 +132,8 @@ HRESULT Terminal::SearchForText(_In_ BSTR /*text*/, _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/, unsigned int /*_start*/, unsigned int /*_end*/, - std::function /*_coordToEndpoint*/, - std::function /*_endpointToCoord*/, + std::function /*_coordToEndpoint*/, + std::function /*_endpointToCoord*/, std::function /*Clone*/) { return E_NOTIMPL; diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 7f9910a9dc7..685bfadfd98 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -7,8 +7,8 @@ #include "dbcs.h" #include "handle.h" - #include "..\interactivity\inc\ServiceLocator.hpp" + #include "search.h" #include "..\types\UiaTextRange.hpp" @@ -16,6 +16,8 @@ using namespace Microsoft::Console::Types; using Microsoft::Console::Interactivity::ServiceLocator; + +#pragma region IBaseData // Routine Description: // - Retrieves the viewport that applies over the data available in the GetTextBuffer() call // Return Value: @@ -47,6 +49,48 @@ const FontInfo& RenderData::GetFontInfo() noexcept return gci.GetActiveOutputBuffer().GetCurrentFont(); } +// Method Description: +// - Retrieves one rectangle per line describing the area of the viewport +// that should be highlighted in some way to represent a user-interactive selection +// Return Value: +// - Vector of Viewports describing the area selected +std::vector RenderData::GetSelectionRects() noexcept +{ + std::vector result; + + try + { + for (const auto& select : Selection::Instance().GetSelectionRects()) + { + result.emplace_back(Viewport::FromInclusive(select)); + } + } + CATCH_LOG(); + + return result; +} + +// Method Description: +// - Lock the console for reading the contents of the buffer. Ensures that the +// contents of the console won't be changed in the middle of a paint +// operation. +// Callers should make sure to also call RenderData::UnlockConsole once +// they're done with any querying they need to do. +void RenderData::LockConsole() noexcept +{ + ::LockConsole(); +} + +// Method Description: +// - Unlocks the console after a call to RenderData::LockConsole. +void RenderData::UnlockConsole() noexcept +{ + ::UnlockConsole(); +} + +#pragma endregion + +#pragma region IRenderData // Routine Description: // - Retrieves the brush colors that should be used in absence of any other color data from // cells in the text buffer. @@ -227,34 +271,79 @@ bool RenderData::IsCursorDoubleWidth() const noexcept return gci.GetActiveOutputBuffer().CursorIsDoubleWidth(); } -// Method Description: -// - Retrieves one rectangle per line describing the area of the viewport -// that should be highlighted in some way to represent a user-interactive selection +// Routine Description: +// - Checks the user preference as to whether grid line drawing is allowed around the edges of each cell. +// - This is for backwards compatibility with old behaviors in the legacy console. // Return Value: -// - Vector of Viewports describing the area selected -std::vector RenderData::GetSelectionRects() noexcept +// - If true, line drawing information retrieved from the text buffer can/should be displayed. +// - If false, it should be ignored and never drawn +const bool RenderData::IsGridLineDrawingAllowed() noexcept { - std::vector result; - - try + const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + // If virtual terminal output is set, grid line drawing is a must. It is always allowed. + if (WI_IsFlagSet(gci.GetActiveOutputBuffer().OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { - for (const auto& select : Selection::Instance().GetSelectionRects()) + return true; + } + else + { + // If someone explicitly asked for worldwide line drawing, enable it. + if (gci.IsGridRenderingAllowedWorldwide()) { - result.emplace_back(Viewport::FromInclusive(select)); + return true; + } + else + { + // Otherwise, for compatibility reasons with legacy applications that used the additional CHAR_INFO bits by accident or for their own purposes, + // we must enable grid line drawing only in a DBCS output codepage. (Line drawing historically only worked in DBCS codepages.) + // The only known instance of this is Image for Windows by TeraByte, Inc. (TeryByte Unlimited) which used the bits accidentally and for no purpose + // (according to the app developer) in conjunction with the Borland Turbo C cgscrn library. + return !!IsAvailableEastAsianCodePage(gci.OutputCP); } } - CATCH_LOG(); +} - return result; +// Routine Description: +// - Retrieves the title information to be displayed in the frame/edge of the window +// Return Value: +// - String with title information +const std::wstring RenderData::GetConsoleTitle() const noexcept +{ + const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + return gci.GetTitleAndPrefix(); +} + +// Routine Description: +// - Converts a text attribute into the foreground RGB value that should be presented, applying +// relevant table translation information and preferences. +// Return Value: +// - ARGB color value +const COLORREF RenderData::GetForegroundColor(const TextAttribute& attr) const noexcept +{ + const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + return gci.LookupForegroundColor(attr); +} + +// Routine Description: +// - Converts a text attribute into the background RGB value that should be presented, applying +// relevant table translation information and preferences. +// Return Value: +// - ARGB color value +const COLORREF RenderData::GetBackgroundColor(const TextAttribute& attr) const noexcept +{ + const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + return gci.LookupBackgroundColor(attr); } +#pragma endregion +#pragma region IUiaData // Routine Description: // - Determines whether the selection area is empty. // Arguments: // - // Return Value: // - True if the selection variables contain valid selection data. False otherwise. -bool RenderData::IsAreaSelected() const +const bool RenderData::IsSelectionActive() const { return Selection::Instance().IsAreaSelected(); } @@ -292,8 +381,8 @@ HRESULT RenderData::SearchForText(_In_ BSTR text, _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, unsigned int _start, unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, + std::function _coordToEndpoint, + std::function _endpointToCoord, std::function Clone) { typedef unsigned int Endpoint; @@ -335,85 +424,4 @@ HRESULT RenderData::SearchForText(_In_ BSTR text, } return hr; } - -// Routine Description: -// - Checks the user preference as to whether grid line drawing is allowed around the edges of each cell. -// - This is for backwards compatibility with old behaviors in the legacy console. -// Return Value: -// - If true, line drawing information retrieved from the text buffer can/should be displayed. -// - If false, it should be ignored and never drawn -const bool RenderData::IsGridLineDrawingAllowed() noexcept -{ - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - // If virtual terminal output is set, grid line drawing is a must. It is always allowed. - if (WI_IsFlagSet(gci.GetActiveOutputBuffer().OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING)) - { - return true; - } - else - { - // If someone explicitly asked for worldwide line drawing, enable it. - if (gci.IsGridRenderingAllowedWorldwide()) - { - return true; - } - else - { - // Otherwise, for compatibility reasons with legacy applications that used the additional CHAR_INFO bits by accident or for their own purposes, - // we must enable grid line drawing only in a DBCS output codepage. (Line drawing historically only worked in DBCS codepages.) - // The only known instance of this is Image for Windows by TeraByte, Inc. (TeryByte Unlimited) which used the bits accidentally and for no purpose - // (according to the app developer) in conjunction with the Borland Turbo C cgscrn library. - return !!IsAvailableEastAsianCodePage(gci.OutputCP); - } - } -} - -// Routine Description: -// - Retrieves the title information to be displayed in the frame/edge of the window -// Return Value: -// - String with title information -const std::wstring RenderData::GetConsoleTitle() const noexcept -{ - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - return gci.GetTitleAndPrefix(); -} - -// Routine Description: -// - Converts a text attribute into the foreground RGB value that should be presented, applying -// relevant table translation information and preferences. -// Return Value: -// - ARGB color value -const COLORREF RenderData::GetForegroundColor(const TextAttribute& attr) const noexcept -{ - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - return gci.LookupForegroundColor(attr); -} - -// Routine Description: -// - Converts a text attribute into the background RGB value that should be presented, applying -// relevant table translation information and preferences. -// Return Value: -// - ARGB color value -const COLORREF RenderData::GetBackgroundColor(const TextAttribute& attr) const noexcept -{ - const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - return gci.LookupBackgroundColor(attr); -} - -// Method Description: -// - Lock the console for reading the contents of the buffer. Ensures that the -// contents of the console won't be changed in the middle of a paint -// operation. -// Callers should make sure to also call RenderData::UnlockConsole once -// they're done with any querying they need to do. -void RenderData::LockConsole() noexcept -{ - ::LockConsole(); -} - -// Method Description: -// - Unlocks the console after a call to RenderData::LockConsole. -void RenderData::UnlockConsole() noexcept -{ - ::UnlockConsole(); -} +#pragma endregion diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 668a5f9f826..0a2054b9239 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -15,13 +15,25 @@ Author(s): #pragma once #include "..\renderer\inc\IRenderData.hpp" +#include "..\types\IUiaData.h" -class RenderData final : public Microsoft::Console::Render::IRenderData +class RenderData final : + public Microsoft::Console::Render::IRenderData, + public Microsoft::Console::Types::IUiaData { public: +#pragma region BaseData Microsoft::Console::Types::Viewport GetViewport() noexcept override; const TextBuffer& GetTextBuffer() noexcept override; const FontInfo& GetFontInfo() noexcept override; + + std::vector GetSelectionRects() noexcept override; + + void LockConsole() noexcept override; + void UnlockConsole() noexcept override; +#pragma endregion + +#pragma region IRenderData const TextAttribute GetDefaultBrushColors() noexcept override; const COLORREF GetForegroundColor(const TextAttribute& attr) const noexcept override; @@ -40,8 +52,11 @@ class RenderData final : public Microsoft::Console::Render::IRenderData const bool IsGridLineDrawingAllowed() noexcept override; - std::vector GetSelectionRects() noexcept override; - bool IsAreaSelected() const override; + const std::wstring GetConsoleTitle() const noexcept override; +#pragma endregion + +#pragma region IUiaData + const bool IsSelectionActive() const override; void ClearSelection() override; void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; @@ -54,12 +69,8 @@ class RenderData final : public Microsoft::Console::Render::IRenderData _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, unsigned int _start, unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, + std::function _coordToEndpoint, + std::function _endpointToCoord, std::function Clone); - - const std::wstring GetConsoleTitle() const noexcept override; - - void LockConsole() noexcept override; - void UnlockConsole() noexcept override; +#pragma endregion }; diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index 1fb699c3bf4..85d521a2e95 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -7,7 +7,6 @@ #include "CommonState.hpp" #include "..\types\UiaTextRange.hpp" -#include "..\host\renderData.hpp" #include "../../../buffer/out/textBuffer.hpp" using namespace WEX::Common; @@ -76,7 +75,7 @@ class UiaTextRangeTests SCREEN_INFORMATION* _pScreenInfo; TextBuffer* _pTextBuffer; UiaTextRange* _range; - RenderData* _pRenderData; + IUiaData* _pUiaData; TEST_METHOD_SETUP(MethodSetup) { @@ -90,7 +89,7 @@ class UiaTextRangeTests // set up pointers _pScreenInfo = &gci.GetActiveOutputBuffer(); _pTextBuffer = &_pScreenInfo->GetTextBuffer(); - _pRenderData = &gci.renderData; + _pUiaData = &gci.renderData; // fill text buffer with text for (UINT i = 0; i < _pTextBuffer->TotalRowCount(); ++i) @@ -105,7 +104,7 @@ class UiaTextRangeTests // set up default range _range = new UiaTextRange{ - _pRenderData, + _pUiaData, &_dummyProvider, 0, 0, @@ -125,7 +124,7 @@ class UiaTextRangeTests _pScreenInfo = nullptr; _pTextBuffer = nullptr; - _pRenderData = nullptr; + _pUiaData = nullptr; return true; } @@ -139,26 +138,26 @@ class UiaTextRangeTests { // make a degenerate range and verify that it reports degenerate UiaTextRange degenerate{ - _pRenderData, + _pUiaData, &_dummyProvider, 20, 19, true }; VERIFY_IS_TRUE(degenerate.IsDegenerate()); - VERIFY_ARE_EQUAL(0u, degenerate._rowCountInRange(_pRenderData)); + VERIFY_ARE_EQUAL(0u, degenerate._rowCountInRange(_pUiaData)); VERIFY_ARE_EQUAL(degenerate._start, degenerate._end); // make a non-degenerate range and verify that it reports as such UiaTextRange notDegenerate1{ - _pRenderData, + _pUiaData, &_dummyProvider, 20, 20, false }; VERIFY_IS_FALSE(notDegenerate1.IsDegenerate()); - VERIFY_ARE_EQUAL(1u, notDegenerate1._rowCountInRange(_pRenderData)); + VERIFY_ARE_EQUAL(1u, notDegenerate1._rowCountInRange(_pUiaData)); } TEST_METHOD(CanCheckIfScreenInfoRowIsInViewport) @@ -219,7 +218,7 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (auto i = 0; i < 300; ++i) { - VERIFY_ARE_EQUAL(i / rowWidth, _range->_endpointToTextBufferRow(_pRenderData, i)); + VERIFY_ARE_EQUAL(i / rowWidth, _range->_endpointToTextBufferRow(_pUiaData, i)); } } @@ -228,9 +227,9 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (unsigned int i = 0; i < 5; ++i) { - VERIFY_ARE_EQUAL(i * rowWidth, _range->_textBufferRowToEndpoint(_pRenderData, i)); + VERIFY_ARE_EQUAL(i * rowWidth, _range->_textBufferRowToEndpoint(_pUiaData, i)); // make sure that the translation is reversible - VERIFY_ARE_EQUAL(i, _range->_endpointToTextBufferRow(_pRenderData, _range->_textBufferRowToEndpoint(_pRenderData, i))); + VERIFY_ARE_EQUAL(i, _range->_endpointToTextBufferRow(_pUiaData, _range->_textBufferRowToEndpoint(_pUiaData, i))); } } @@ -239,7 +238,7 @@ class UiaTextRangeTests const auto rowWidth = _getRowWidth(); for (unsigned int i = 0; i < 5; ++i) { - VERIFY_ARE_EQUAL(i, _range->_textBufferRowToScreenInfoRow(_pRenderData, _range->_screenInfoRowToTextBufferRow(_pRenderData, i))); + VERIFY_ARE_EQUAL(i, _range->_textBufferRowToScreenInfoRow(_pUiaData, _range->_screenInfoRowToTextBufferRow(_pUiaData, i))); } } @@ -249,7 +248,7 @@ class UiaTextRangeTests for (auto i = 0; i < 300; ++i) { const auto column = i % rowWidth; - VERIFY_ARE_EQUAL(column, _range->_endpointToColumn(_pRenderData, i)); + VERIFY_ARE_EQUAL(column, _range->_endpointToColumn(_pUiaData, i)); } } @@ -257,13 +256,13 @@ class UiaTextRangeTests { const auto totalRows = _pTextBuffer->TotalRowCount(); VERIFY_ARE_EQUAL(totalRows, - _range->_getTotalRows(_pRenderData)); + _range->_getTotalRows(_pUiaData)); } TEST_METHOD(CanGetRowWidth) { const auto rowWidth = _getRowWidth(); - VERIFY_ARE_EQUAL(rowWidth, _range->_getRowWidth(_pRenderData)); + VERIFY_ARE_EQUAL(rowWidth, _range->_getRowWidth(_pUiaData)); } TEST_METHOD(CanNormalizeRow) @@ -280,7 +279,7 @@ class UiaTextRangeTests for (auto it = rowMappings.begin(); it != rowMappings.end(); ++it) { - VERIFY_ARE_EQUAL(static_cast(it->second), _range->_normalizeRow(_pRenderData, it->first)); + VERIFY_ARE_EQUAL(static_cast(it->second), _range->_normalizeRow(_pUiaData, it->first)); } } @@ -330,7 +329,7 @@ class UiaTextRangeTests for (auto data : testData) { VERIFY_ARE_EQUAL(std::get<4>(data), - UiaTextRange::_compareScreenCoords(_pRenderData, + UiaTextRange::_compareScreenCoords(_pUiaData, std::get<0>(data), std::get<1>(data), std::get<2>(data), @@ -401,8 +400,8 @@ class UiaTextRangeTests }, 5, 5, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, 2) + 6, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, 2) + 6 + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 2) + 6, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, 2) + 6 }, { @@ -418,8 +417,8 @@ class UiaTextRangeTests }, 5, 0, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex }, { @@ -435,8 +434,8 @@ class UiaTextRangeTests }, 5, 5, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + 4, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + 4 + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + 4 }, { @@ -452,8 +451,8 @@ class UiaTextRangeTests }, -5, -5, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + (lastColumnIndex - 4), - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + (lastColumnIndex - 4) + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + (lastColumnIndex - 4), + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + (lastColumnIndex - 4) } }; // clang-format on @@ -462,7 +461,7 @@ class UiaTextRangeTests { Log::Comment(std::get<0>(data).c_str()); int amountMoved; - std::pair newEndpoints = UiaTextRange::_moveByCharacter(_pRenderData, + std::pair newEndpoints = UiaTextRange::_moveByCharacter(_pUiaData, std::get<2>(data), std::get<1>(data), &amountMoved); @@ -502,8 +501,8 @@ class UiaTextRangeTests }, -4, 0, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex }, { @@ -519,8 +518,8 @@ class UiaTextRangeTests }, 4, 4, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 4) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 4) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 4) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 4) + lastColumnIndex }, { @@ -536,8 +535,8 @@ class UiaTextRangeTests }, 3, 0, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex }, { @@ -553,8 +552,8 @@ class UiaTextRangeTests }, -3, -3, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow - 3) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow - 3) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow - 3) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow - 3) + lastColumnIndex }, { @@ -570,8 +569,8 @@ class UiaTextRangeTests }, -1, 0, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex + 5, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex + 5, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex }, { @@ -587,8 +586,8 @@ class UiaTextRangeTests }, 1, 0, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex } }; // clang-format on @@ -597,7 +596,7 @@ class UiaTextRangeTests { Log::Comment(std::get<0>(data).c_str()); int amountMoved; - std::pair newEndpoints = UiaTextRange::_moveByLine(_pRenderData, + std::pair newEndpoints = UiaTextRange::_moveByLine(_pUiaData, std::get<2>(data), std::get<1>(data), &amountMoved); @@ -640,8 +639,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex, false }, @@ -659,8 +658,8 @@ class UiaTextRangeTests -5, -3, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex, false }, @@ -678,8 +677,8 @@ class UiaTextRangeTests -5, -4, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, false }, @@ -697,8 +696,8 @@ class UiaTextRangeTests -7, -7, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 3, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 3, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 3, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 3, true }, @@ -716,8 +715,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -735,8 +734,8 @@ class UiaTextRangeTests 5, 3, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -754,8 +753,8 @@ class UiaTextRangeTests 5, 4, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -773,8 +772,8 @@ class UiaTextRangeTests 7, 7, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 12, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 12, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 12, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 12, true }, }; @@ -785,7 +784,7 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitCharacter(_pRenderData, + result = UiaTextRange::_moveEndpointByUnitCharacter(_pUiaData, std::get<2>(data), std::get<4>(data), std::get<1>(data), @@ -830,8 +829,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + lastColumnIndex, false }, @@ -849,8 +848,8 @@ class UiaTextRangeTests -2, -2, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + lastColumnIndex, false }, @@ -868,8 +867,8 @@ class UiaTextRangeTests 2, 2, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex, false }, @@ -887,8 +886,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 1) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 1) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + lastColumnIndex, false }, @@ -906,8 +905,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex, false }, @@ -925,8 +924,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + lastColumnIndex, false }, @@ -944,8 +943,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -963,8 +962,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -982,8 +981,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, true }, @@ -1001,8 +1000,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, true } }; @@ -1013,7 +1012,7 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitLine(_pRenderData, + result = UiaTextRange::_moveEndpointByUnitLine(_pUiaData, std::get<2>(data), std::get<4>(data), std::get<1>(data), @@ -1058,8 +1057,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex + 4, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -1077,8 +1076,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + 4, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + 4, false }, @@ -1096,8 +1095,8 @@ class UiaTextRangeTests 1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 3) + firstColumnIndex + 2, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 3) + firstColumnIndex + 2, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, false }, @@ -1115,8 +1114,8 @@ class UiaTextRangeTests -1, 0, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow + 5) + 6, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow + 5) + 6, false }, @@ -1134,8 +1133,8 @@ class UiaTextRangeTests -1, -1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_End, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, topRow) + firstColumnIndex, true }, @@ -1153,8 +1152,8 @@ class UiaTextRangeTests 1, 1, TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, - UiaTextRange::_screenInfoRowToEndpoint(_pRenderData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, + UiaTextRange::_screenInfoRowToEndpoint(_pUiaData, bottomRow) + lastColumnIndex, true } }; @@ -1165,7 +1164,7 @@ class UiaTextRangeTests Log::Comment(std::get<0>(data).c_str()); std::tuple result; int amountMoved; - result = UiaTextRange::_moveEndpointByUnitDocument(_pRenderData, + result = UiaTextRange::_moveEndpointByUnitDocument(_pUiaData, std::get<2>(data), std::get<4>(data), std::get<1>(data), diff --git a/src/interactivity/win32/windowUiaProvider.cpp b/src/interactivity/win32/windowUiaProvider.cpp index 084b834b277..ea0d81db471 100644 --- a/src/interactivity/win32/windowUiaProvider.cpp +++ b/src/interactivity/win32/windowUiaProvider.cpp @@ -5,6 +5,7 @@ #include "windowUiaProvider.hpp" #include "../types/ScreenInfoUiaProvider.h" +#include "../types/IUiaData.h" #include "../host/renderData.hpp" #include "../inc/ServiceLocator.hpp" @@ -35,9 +36,9 @@ WindowUiaProvider* WindowUiaProvider::Create(IConsoleWindow* baseWindow) Globals& g = ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); - Microsoft::Console::Render::IRenderData* renderData = &gci.renderData; + IUiaData* uiaData = &gci.renderData; - pScreenInfoProvider = new Microsoft::Console::Types::ScreenInfoUiaProvider(renderData, pWindowProvider); + pScreenInfoProvider = new Microsoft::Console::Types::ScreenInfoUiaProvider(uiaData, pWindowProvider); pWindowProvider->_pScreenInfoProvider = pScreenInfoProvider; // TODO GitHub #1914: Re-attach Tracing to UIA Tree diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 40c8ed2862a..fb7eaf24589 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -16,11 +16,9 @@ Author(s): #include "../../host/conimeinfo.h" #include "../../buffer/out/TextAttribute.hpp" -#include "../../types/inc/viewport.hpp" +#include "../../types/IBaseData.h" -class TextBuffer; class Cursor; -struct ITextRangeProvider; namespace Microsoft::Console::Render { @@ -39,13 +37,10 @@ namespace Microsoft::Console::Render const Microsoft::Console::Types::Viewport region; }; - class IRenderData + class IRenderData : public Microsoft::Console::Types::IBaseData { public: virtual ~IRenderData() = 0; - virtual Microsoft::Console::Types::Viewport GetViewport() noexcept = 0; - virtual const TextBuffer& GetTextBuffer() noexcept = 0; - virtual const FontInfo& GetFontInfo() noexcept = 0; virtual const TextAttribute GetDefaultBrushColors() noexcept = 0; virtual const COLORREF GetForegroundColor(const TextAttribute& attr) const noexcept = 0; @@ -63,30 +58,7 @@ namespace Microsoft::Console::Render virtual const std::vector GetOverlays() const noexcept = 0; virtual const bool IsGridLineDrawingAllowed() noexcept = 0; - - // TODO GitHub #1992: Move some of these functions to IAccessibilityData (or IUiaData) - virtual std::vector GetSelectionRects() noexcept = 0; - virtual bool IsAreaSelected() const = 0; - virtual void ClearSelection() = 0; - virtual void SelectNewRegion(const COORD coordStart, const COORD coordEnd) = 0; - - // TODO GitHub #605: Search functionality - // For now, just adding it here to make UiaTextRange easier to create (Accessibility) - // We should actually abstract this out better once Windows Terminal has Search - virtual HRESULT SearchForText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, - unsigned int _start, - unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, - std::function Clone) = 0; - virtual const std::wstring GetConsoleTitle() const noexcept = 0; - - virtual void LockConsole() noexcept = 0; - virtual void UnlockConsole() noexcept = 0; }; // See docs/virtual-dtors.md for an explanation of why this is weird. diff --git a/src/types/IBaseData.h b/src/types/IBaseData.h new file mode 100644 index 00000000000..e82bfa8c170 --- /dev/null +++ b/src/types/IBaseData.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- IUiaData.hpp + +Abstract: +- This serves as the interface defining all information needed for the UI Automation Tree and the Renderer + +Author(s): +- Carlos Zamora (CaZamor) Aug-2019 +--*/ + +#pragma once + +#include "inc/viewport.hpp" + +class TextBuffer; + +namespace Microsoft::Console::Types +{ + class IBaseData + { + public: + virtual ~IBaseData() = 0; + virtual Microsoft::Console::Types::Viewport GetViewport() noexcept = 0; + virtual const TextBuffer& GetTextBuffer() noexcept = 0; + virtual const FontInfo& GetFontInfo() noexcept = 0; + + virtual std::vector GetSelectionRects() noexcept = 0; + + virtual void LockConsole() noexcept = 0; + virtual void UnlockConsole() noexcept = 0; + }; + + // See docs/virtual-dtors.md for an explanation of why this is weird. + inline IBaseData::~IBaseData() {} +} diff --git a/src/types/IUiaData.h b/src/types/IUiaData.h new file mode 100644 index 00000000000..73a693de8c8 --- /dev/null +++ b/src/types/IUiaData.h @@ -0,0 +1,48 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- IUiaData.hpp + +Abstract: +- This serves as the interface defining all information needed for the UI Automation Tree + +Author(s): +- Carlos Zamora (CaZamor) Aug-2019 +--*/ + +#pragma once + +#include "IBaseData.h" + +struct ITextRangeProvider; + +namespace Microsoft::Console::Types +{ + class IUiaData : public IBaseData + { + public: + virtual ~IUiaData() = 0; + + virtual const bool IsSelectionActive() const = 0; + virtual void ClearSelection() = 0; + virtual void SelectNewRegion(const COORD coordStart, const COORD coordEnd) = 0; + + // TODO GitHub #605: Search functionality + // For now, just adding it here to make UiaTextRange easier to create (Accessibility) + // We should actually abstract this out better once Windows Terminal has Search + virtual HRESULT SearchForText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, + unsigned int _start, + unsigned int _end, + std::function _coordToEndpoint, + std::function _endpointToCoord, + std::function Clone) = 0; + }; + + // See docs/virtual-dtors.md for an explanation of why this is weird. + inline IUiaData::~IUiaData() {} +} diff --git a/src/types/ScreenInfoUiaProvider.cpp b/src/types/ScreenInfoUiaProvider.cpp index fbc4c190a33..4d22a3bd120 100644 --- a/src/types/ScreenInfoUiaProvider.cpp +++ b/src/types/ScreenInfoUiaProvider.cpp @@ -31,7 +31,7 @@ SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int return psa; } -ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, +ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, _In_ WindowUiaProviderBase* const pUiaParent, _In_ std::function GetBoundingRect) : _pUiaParent(pUiaParent), @@ -44,7 +44,7 @@ ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IR //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); } -ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, +ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, _In_ WindowUiaProviderBase* const pUiaParent) : _pUiaParent(pUiaParent), _signalFiringMapping{}, @@ -387,7 +387,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF *ppRetVal = nullptr; HRESULT hr = S_OK; - if (!_pData->IsAreaSelected()) + if (!_pData->IsSelectionActive()) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //apiMsg.AreaSelected = false; diff --git a/src/types/ScreenInfoUiaProvider.h b/src/types/ScreenInfoUiaProvider.h index 7d4cb374f1f..93bae88c096 100644 --- a/src/types/ScreenInfoUiaProvider.h +++ b/src/types/ScreenInfoUiaProvider.h @@ -23,7 +23,7 @@ Author(s): #include "precomp.h" #include "../buffer/out/textBuffer.hpp" -#include "../renderer/inc/IRenderData.hpp" +#include "IUiaData.h" namespace Microsoft::Console::Types { @@ -36,12 +36,12 @@ namespace Microsoft::Console::Types public ITextProvider { public: - ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + ScreenInfoUiaProvider(_In_ IUiaData* pData, _In_ WindowUiaProviderBase* const pUiaParent, _In_ std::function GetBoundingRect); // TODO GitHub 2120: pUiaParent should not be allowed to be null - ScreenInfoUiaProvider(_In_ Microsoft::Console::Render::IRenderData* pData, + ScreenInfoUiaProvider(_In_ IUiaData* pData, _In_ WindowUiaProviderBase* const pUiaParent); virtual ~ScreenInfoUiaProvider(); @@ -93,7 +93,7 @@ namespace Microsoft::Console::Types WindowUiaProviderBase* const _pUiaParent; // weak reference to IRenderData - Microsoft::Console::Render::IRenderData* _pData; + IUiaData* _pData; // this is used to prevent the object from // signaling an event while it is already in the diff --git a/src/types/UiaTextRange.cpp b/src/types/UiaTextRange.cpp index 5439c9a8841..6df92d307f4 100644 --- a/src/types/UiaTextRange.cpp +++ b/src/types/UiaTextRange.cpp @@ -14,7 +14,7 @@ using namespace Microsoft::Console::Types::UiaTextRangeTracing; IdType UiaTextRange::id = 1; -UiaTextRange::MoveState::MoveState(Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::MoveState::MoveState(IUiaData* pData, const UiaTextRange& range, const MovementDirection direction) : StartScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(pData, range.GetStart()) }, @@ -65,7 +65,7 @@ UiaTextRange::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, // This is a debugging function that prints out the current // relationship between screen info rows, text buffer rows, and // endpoints. -void UiaTextRange::_outputRowConversions(Microsoft::Console::Render::IRenderData* pData) +void UiaTextRange::_outputRowConversions(IUiaData* pData) { try { @@ -101,7 +101,7 @@ void UiaTextRange::_outputObjectState() } #endif // _DEBUG -std::deque UiaTextRange::GetSelectionRanges(_In_ Microsoft::Console::Render::IRenderData* pData, +std::deque UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* pProvider) { std::deque ranges; @@ -139,7 +139,7 @@ std::deque UiaTextRange::GetSelectionRanges(_In_ Microsoft::Conso return ranges; } -UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) { UiaTextRange* range = nullptr; @@ -160,7 +160,7 @@ UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* return range; } -UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor) { @@ -181,7 +181,7 @@ UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* return range; } -UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, @@ -208,7 +208,7 @@ UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* return range; } -UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point) { @@ -230,7 +230,7 @@ UiaTextRange* UiaTextRange::Create(_In_ Microsoft::Console::Render::IRenderData* } // degenerate range constructor. -UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ IRawElementProviderSimple* const pProvider) : +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) : _cRefs{ 1 }, _pProvider{ THROW_HR_IF_NULL(E_INVALIDARG, pProvider) }, _start{ 0 }, @@ -248,7 +248,7 @@ UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, Tracing::s_TraceUia(nullptr, ApiCall::Constructor, &apiMsg);*/ } -UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor) : UiaTextRange(pData, pProvider) @@ -263,7 +263,7 @@ UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, #endif } -UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, @@ -283,7 +283,7 @@ UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, } // returns a degenerate text range of the start of the row closest to the y value of point -UiaTextRange::UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point) : UiaTextRange(pData, pProvider) @@ -1209,7 +1209,7 @@ IFACEMETHODIMP UiaTextRange::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** p #pragma endregion -const COORD UiaTextRange::_getScreenBufferCoords(Microsoft::Console::Render::IRenderData* pData) +const COORD UiaTextRange::_getScreenBufferCoords(IUiaData* pData) { return pData->GetTextBuffer().GetSize().Dimensions(); } @@ -1231,7 +1231,7 @@ COORD UiaTextRange::_getScreenFontSize() const // - // Return Value: // - The number of rows -const unsigned int UiaTextRange::_getTotalRows(Microsoft::Console::Render::IRenderData* pData) +const unsigned int UiaTextRange::_getTotalRows(IUiaData* pData) { return pData->GetTextBuffer().TotalRowCount(); } @@ -1242,7 +1242,7 @@ const unsigned int UiaTextRange::_getTotalRows(Microsoft::Console::Render::IRend // - // Return Value: // - The row width -const unsigned int UiaTextRange::_getRowWidth(Microsoft::Console::Render::IRenderData* pData) +const unsigned int UiaTextRange::_getRowWidth(IUiaData* pData) { // make sure that we can't leak a 0 return std::max(static_cast(_getScreenBufferCoords(pData).X), 1u); @@ -1254,7 +1254,7 @@ const unsigned int UiaTextRange::_getRowWidth(Microsoft::Console::Render::IRende // - endpoint - the endpoint to translate // Return Value: // - the column value -const Column UiaTextRange::_endpointToColumn(Microsoft::Console::Render::IRenderData* pData, const Endpoint endpoint) +const Column UiaTextRange::_endpointToColumn(IUiaData* pData, const Endpoint endpoint) { return endpoint % _getRowWidth(pData); } @@ -1265,7 +1265,7 @@ const Column UiaTextRange::_endpointToColumn(Microsoft::Console::Render::IRender // - endpoint - the endpoint to convert // Return Value: // - the text buffer row value -const TextBufferRow UiaTextRange::_endpointToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, +const TextBufferRow UiaTextRange::_endpointToTextBufferRow(IUiaData* pData, const Endpoint endpoint) { return endpoint / _getRowWidth(pData); @@ -1278,7 +1278,7 @@ const TextBufferRow UiaTextRange::_endpointToTextBufferRow(Microsoft::Console::R // - // Return Value: // - The number of rows in the range. -const unsigned int UiaTextRange::_rowCountInRange(Microsoft::Console::Render::IRenderData* pData) const +const unsigned int UiaTextRange::_rowCountInRange(IUiaData* pData) const { if (_degenerate) { @@ -1302,7 +1302,7 @@ const unsigned int UiaTextRange::_rowCountInRange(Microsoft::Console::Render::IR // - row - the TextBufferRow to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, +const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(IUiaData* pData, const TextBufferRow row) { const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); @@ -1316,7 +1316,7 @@ const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(Microsoft::Conso // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(Microsoft::Console::Render::IRenderData* pData, const ScreenInfoRow row) +const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) { const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); @@ -1343,7 +1343,7 @@ const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(const ScreenInfoRow // - the non-normalized row index // Return Value: // - the normalized row index -const Row UiaTextRange::_normalizeRow(Microsoft::Console::Render::IRenderData* pData, const Row row) +const Row UiaTextRange::_normalizeRow(IUiaData* pData, const Row row) { const unsigned int totalRows = _getTotalRows(pData); return ((row + totalRows) % totalRows); @@ -1385,7 +1385,7 @@ const unsigned int UiaTextRange::_getViewportWidth(const SMALL_RECT viewport) // - row - the screen info row to check // Return Value: // - true if the row is within the bounds of the viewport -const bool UiaTextRange::_isScreenInfoRowInViewport(Microsoft::Console::Render::IRenderData* pData, +const bool UiaTextRange::_isScreenInfoRowInViewport(IUiaData* pData, const ScreenInfoRow row) { return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); @@ -1412,7 +1412,7 @@ const bool UiaTextRange::_isScreenInfoRowInViewport(const ScreenInfoRow row, // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent TextBufferRow. -const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, +const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(IUiaData* pData, const ScreenInfoRow row) { const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); @@ -1425,7 +1425,7 @@ const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(Microsoft::Conso // - row - the TextBufferRow to convert // Return Value: // - the equivalent Endpoint, starting at the beginning of the TextBufferRow. -const Endpoint UiaTextRange::_textBufferRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, const TextBufferRow row) +const Endpoint UiaTextRange::_textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row) { return _getRowWidth(pData) * row; } @@ -1436,7 +1436,7 @@ const Endpoint UiaTextRange::_textBufferRowToEndpoint(Microsoft::Console::Render // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent Endpoint. -const Endpoint UiaTextRange::_screenInfoRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, +const Endpoint UiaTextRange::_screenInfoRowToEndpoint(IUiaData* pData, const ScreenInfoRow row) { return _textBufferRowToEndpoint(pData, _screenInfoRowToTextBufferRow(pData, row)); @@ -1448,7 +1448,7 @@ const Endpoint UiaTextRange::_screenInfoRowToEndpoint(Microsoft::Console::Render // - endpoint - the endpoint to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, +const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(IUiaData* pData, const Endpoint endpoint) { return _textBufferRowToScreenInfoRow(pData, _endpointToTextBufferRow(pData, endpoint)); @@ -1463,7 +1463,7 @@ const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(Microsoft::Console::R // - // Notes: // - alters coords. may throw an exception. -void UiaTextRange::_addScreenInfoRowBoundaries(Microsoft::Console::Render::IRenderData* pData, +void UiaTextRange::_addScreenInfoRowBoundaries(IUiaData* pData, const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const { @@ -1540,7 +1540,7 @@ const unsigned int UiaTextRange::_getFirstScreenInfoRowIndex() // - // Return Value: // - the index of the last row (0-indexed) of the screen info -const unsigned int UiaTextRange::_getLastScreenInfoRowIndex(Microsoft::Console::Render::IRenderData* pData) +const unsigned int UiaTextRange::_getLastScreenInfoRowIndex(IUiaData* pData) { return _getTotalRows(pData) - 1; } @@ -1562,7 +1562,7 @@ const Column UiaTextRange::_getFirstColumnIndex() // - // Return Value: // - the index of the last column (0-indexed) of the screen info rows -const Column UiaTextRange::_getLastColumnIndex(Microsoft::Console::Render::IRenderData* pData) +const Column UiaTextRange::_getLastColumnIndex(IUiaData* pData) { return _getRowWidth(pData) - 1; } @@ -1578,7 +1578,7 @@ const Column UiaTextRange::_getLastColumnIndex(Microsoft::Console::Render::IRend // -1 if A < B // 1 if A > B // 0 if A == B -const int UiaTextRange::_compareScreenCoords(Microsoft::Console::Render::IRenderData* pData, +const int UiaTextRange::_compareScreenCoords(IUiaData* pData, const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, @@ -1627,7 +1627,7 @@ const int UiaTextRange::_compareScreenCoords(Microsoft::Console::Render::IRender // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByCharacter(Microsoft::Console::Render::IRenderData* pData, +std::pair UiaTextRange::_moveByCharacter(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1642,7 +1642,7 @@ std::pair UiaTextRange::_moveByCharacter(Microsoft::Console: } } -std::pair UiaTextRange::_moveByCharacterForward(Microsoft::Console::Render::IRenderData* pData, +std::pair UiaTextRange::_moveByCharacterForward(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1688,7 +1688,7 @@ std::pair UiaTextRange::_moveByCharacterForward(Microsoft::C return std::make_pair(std::move(start), std::move(end)); } -std::pair UiaTextRange::_moveByCharacterBackward(Microsoft::Console::Render::IRenderData* pData, +std::pair UiaTextRange::_moveByCharacterBackward(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1745,7 +1745,7 @@ std::pair UiaTextRange::_moveByCharacterBackward(Microsoft:: // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByLine(Microsoft::Console::Render::IRenderData* pData, +std::pair UiaTextRange::_moveByLine(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1792,7 +1792,7 @@ std::pair UiaTextRange::_moveByLine(Microsoft::Console::Rend // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByDocument(Microsoft::Console::Render::IRenderData* pData, +std::pair UiaTextRange::_moveByDocument(IUiaData* pData, const int /*moveCount*/, const MoveState moveState, _Out_ int* const pAmountMoved) @@ -1819,7 +1819,7 @@ std::pair UiaTextRange::_moveByDocument(Microsoft::Console:: // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitCharacter(Microsoft::Console::Render::IRenderData* pData, +std::tuple UiaTextRange::_moveEndpointByUnitCharacter(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, @@ -1836,7 +1836,7 @@ std::tuple UiaTextRange::_moveEndpointByUnitCharacter( } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterForward(Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::_moveEndpointByUnitCharacterForward(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, @@ -1925,7 +1925,7 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(Microsoft::Console::Render::IR } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterBackward(Microsoft::Console::Render::IRenderData* pData, +UiaTextRange::_moveEndpointByUnitCharacterBackward(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, @@ -2025,7 +2025,7 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(Microsoft::Console::Render::I // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitLine(Microsoft::Console::Render::IRenderData* pData, +std::tuple UiaTextRange::_moveEndpointByUnitLine(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, @@ -2184,7 +2184,7 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(Micro // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitDocument(Microsoft::Console::Render::IRenderData* pData, +std::tuple UiaTextRange::_moveEndpointByUnitDocument(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, @@ -2251,12 +2251,12 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(M return std::make_tuple(start, end, degenerate); } -COORD UiaTextRange::_endpointToCoord(Microsoft::Console::Render::IRenderData* pData, const Endpoint endpoint) +COORD UiaTextRange::_endpointToCoord(IUiaData* pData, const Endpoint endpoint) { return { gsl::narrow(_endpointToColumn(pData, endpoint)), gsl::narrow(_endpointToScreenInfoRow(pData, endpoint)) }; } -Endpoint UiaTextRange::_coordToEndpoint(Microsoft::Console::Render::IRenderData* pData, +Endpoint UiaTextRange::_coordToEndpoint(IUiaData* pData, const COORD coord) { return _screenInfoRowToEndpoint(pData, coord.Y) + coord.X; diff --git a/src/types/UiaTextRange.hpp b/src/types/UiaTextRange.hpp index a8a100ff4cb..e8ff01cde43 100644 --- a/src/types/UiaTextRange.hpp +++ b/src/types/UiaTextRange.hpp @@ -22,7 +22,7 @@ Author(s): #include "inc/viewport.hpp" #include "../buffer/out/textBuffer.hpp" -#include "../renderer/inc/IRenderData.hpp" +#include "IUiaData.h" #include #include @@ -116,7 +116,7 @@ namespace Microsoft::Console::Types // direction moving MovementDirection Direction; - MoveState(Microsoft::Console::Render::IRenderData* pData, + MoveState(IUiaData* pData, const UiaTextRange& range, const MovementDirection direction); @@ -137,26 +137,26 @@ namespace Microsoft::Console::Types }; public: - static std::deque GetSelectionRanges(_In_ Microsoft::Console::Render::IRenderData* pData, _In_ IRawElementProviderSimple* pProvider); + static std::deque GetSelectionRanges(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* pProvider); // degenerate range - static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + static UiaTextRange* Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider); // degenerate range at cursor position - static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + static UiaTextRange* Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor); // specific endpoint range - static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + static UiaTextRange* Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate); // range from a UiaPoint - static UiaTextRange* Create(_In_ Microsoft::Console::Render::IRenderData* pData, + static UiaTextRange* Create(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point); @@ -168,7 +168,7 @@ namespace Microsoft::Console::Types const bool IsDegenerate() const; // TODO GitHub #605: - // only used for RenderData::FindText. Remove after Search added properly + // only used for UiaData::FindText. Remove after Search added properly void SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate); // IUnknown methods @@ -219,10 +219,10 @@ namespace Microsoft::Console::Types protected: #if _DEBUG - void _outputRowConversions(Microsoft::Console::Render::IRenderData* pData); + void _outputRowConversions(IUiaData* pData); void _outputObjectState(); #endif - Microsoft::Console::Render::IRenderData* const _pData; + IUiaData* const _pData; IRawElementProviderSimple* const _pProvider; @@ -231,23 +231,23 @@ namespace Microsoft::Console::Types private: // degenerate range - UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider); // degenerate range at cursor position - UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Cursor& cursor); // specific endpoint range - UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const Endpoint start, const Endpoint end, const bool degenerate); // range from a UiaPoint - UiaTextRange(_In_ Microsoft::Console::Render::IRenderData* pData, + UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider, const UiaPoint point); @@ -283,50 +283,50 @@ namespace Microsoft::Console::Types // then both endpoints will contain the same value. bool _degenerate; - static const COORD _getScreenBufferCoords(Microsoft::Console::Render::IRenderData* pData); + static const COORD _getScreenBufferCoords(IUiaData* pData); COORD _getScreenFontSize() const; - static const unsigned int _getTotalRows(Microsoft::Console::Render::IRenderData* pData); - static const unsigned int _getRowWidth(Microsoft::Console::Render::IRenderData* pData); + static const unsigned int _getTotalRows(IUiaData* pData); + static const unsigned int _getRowWidth(IUiaData* pData); static const unsigned int _getFirstScreenInfoRowIndex(); - static const unsigned int _getLastScreenInfoRowIndex(Microsoft::Console::Render::IRenderData* pData); + static const unsigned int _getLastScreenInfoRowIndex(IUiaData* pData); static const Column _getFirstColumnIndex(); - static const Column _getLastColumnIndex(Microsoft::Console::Render::IRenderData* pData); + static const Column _getLastColumnIndex(IUiaData* pData); - const unsigned int _rowCountInRange(Microsoft::Console::Render::IRenderData* pData) const; + const unsigned int _rowCountInRange(IUiaData* pData) const; - static const TextBufferRow _endpointToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + static const TextBufferRow _endpointToTextBufferRow(IUiaData* pData, const Endpoint endpoint); - static const ScreenInfoRow _textBufferRowToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + static const ScreenInfoRow _textBufferRowToScreenInfoRow(IUiaData* pData, const TextBufferRow row); - static const TextBufferRow _screenInfoRowToTextBufferRow(Microsoft::Console::Render::IRenderData* pData, + static const TextBufferRow _screenInfoRowToTextBufferRow(IUiaData* pData, const ScreenInfoRow row); - static const Endpoint _textBufferRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, const TextBufferRow row); + static const Endpoint _textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row); - static const ScreenInfoRow _endpointToScreenInfoRow(Microsoft::Console::Render::IRenderData* pData, + static const ScreenInfoRow _endpointToScreenInfoRow(IUiaData* pData, const Endpoint endpoint); - static const Endpoint _screenInfoRowToEndpoint(Microsoft::Console::Render::IRenderData* pData, + static const Endpoint _screenInfoRowToEndpoint(IUiaData* pData, const ScreenInfoRow row); - static COORD _endpointToCoord(Microsoft::Console::Render::IRenderData* pData, + static COORD _endpointToCoord(IUiaData* pData, const Endpoint endpoint); - static Endpoint _coordToEndpoint(Microsoft::Console::Render::IRenderData* pData, + static Endpoint _coordToEndpoint(IUiaData* pData, const COORD coord); - static const Column _endpointToColumn(Microsoft::Console::Render::IRenderData* pData, + static const Column _endpointToColumn(IUiaData* pData, const Endpoint endpoint); - static const Row _normalizeRow(Microsoft::Console::Render::IRenderData* pData, const Row row); + static const Row _normalizeRow(IUiaData* pData, const Row row); - static const ViewportRow _screenInfoRowToViewportRow(Microsoft::Console::Render::IRenderData* pData, + static const ViewportRow _screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row); static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, const SMALL_RECT viewport); - static const bool _isScreenInfoRowInViewport(Microsoft::Console::Render::IRenderData* pData, + static const bool _isScreenInfoRowInViewport(IUiaData* pData, const ScreenInfoRow row); static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row, const SMALL_RECT viewport); @@ -334,71 +334,71 @@ namespace Microsoft::Console::Types static const unsigned int _getViewportHeight(const SMALL_RECT viewport); static const unsigned int _getViewportWidth(const SMALL_RECT viewport); - void _addScreenInfoRowBoundaries(Microsoft::Console::Render::IRenderData* pData, + void _addScreenInfoRowBoundaries(IUiaData* pData, const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const; - static const int _compareScreenCoords(Microsoft::Console::Render::IRenderData* pData, + static const int _compareScreenCoords(IUiaData* pData, const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, const Column colB); - static std::pair _moveByCharacter(Microsoft::Console::Render::IRenderData* pData, + static std::pair _moveByCharacter(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByCharacterForward(Microsoft::Console::Render::IRenderData* pData, + static std::pair _moveByCharacterForward(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + static std::pair _moveByCharacterBackward(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByLine(Microsoft::Console::Render::IRenderData* pData, + static std::pair _moveByLine(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); - static std::pair _moveByDocument(Microsoft::Console::Render::IRenderData* pData, + static std::pair _moveByDocument(IUiaData* pData, const int moveCount, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacter(Microsoft::Console::Render::IRenderData* pData, + _moveEndpointByUnitCharacter(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterForward(Microsoft::Console::Render::IRenderData* pData, + _moveEndpointByUnitCharacterForward(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterBackward(Microsoft::Console::Render::IRenderData* pData, + _moveEndpointByUnitCharacterBackward(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitLine(Microsoft::Console::Render::IRenderData* pData, + _moveEndpointByUnitLine(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ int* const pAmountMoved); static std::tuple - _moveEndpointByUnitDocument(Microsoft::Console::Render::IRenderData* pData, + _moveEndpointByUnitDocument(IUiaData* pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, diff --git a/src/types/lib/types.vcxproj b/src/types/lib/types.vcxproj index 5459cd572b7..a0ca927f641 100644 --- a/src/types/lib/types.vcxproj +++ b/src/types/lib/types.vcxproj @@ -24,6 +24,7 @@
+ @@ -32,6 +33,7 @@ + diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index 35b6f908f1a..804d8dd5007 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -158,6 +158,12 @@ Header Files + + Header Files + + + Header Files + From 38156311e8f083614fb15ff627dabb2d3bf845b4 Mon Sep 17 00:00:00 2001 From: inventivejon <50767589+inventivejon@users.noreply.github.com> Date: Mon, 19 Aug 2019 20:20:06 +0200 Subject: [PATCH 047/154] sample: Fix static "cmd.exe" in miniterm (#2461) --- samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs b/samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs index f54805ba349..57159ce4a55 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/Processes/ProcessFactory.cs @@ -18,7 +18,7 @@ static class ProcessFactory internal static Process Start(string command, IntPtr attributes, IntPtr hPC) { var startupInfo = ConfigureProcessThread(hPC, attributes); - var processInfo = RunProcess(ref startupInfo, "cmd.exe"); + var processInfo = RunProcess(ref startupInfo, command); return new Process(startupInfo, processInfo); } From 71eaf621bcafe0536ddae9a9f74ab94a52c16731 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Mon, 19 Aug 2019 15:59:01 -0700 Subject: [PATCH 048/154] Add support for HTML copy (#1224) * Move Clipboard::GenHTML to TextBuffer (add params) Refactor RetrieveSelectedTextFromBuffer Modify CopyToClipboardEventArgs to include HTML data * minor code format fix * PR Changes NOTE: refactoring text buffer code is a separate task. New issue to be created. * Refactor TextBuffer::GenHTML (#2038) Fixes #1846. * nit change * x86 build fix * nit changes --- src/buffer/out/textBuffer.cpp | 177 +++++++++++++ src/buffer/out/textBuffer.hpp | 5 + src/cascadia/TerminalApp/App.cpp | 18 +- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 19 +- src/cascadia/TerminalControl/TermControl.h | 21 +- src/cascadia/TerminalControl/TermControl.idl | 9 +- src/cascadia/TerminalCore/Terminal.hpp | 2 +- .../TerminalCore/TerminalSelection.cpp | 20 +- src/interactivity/win32/Clipboard.cpp | 244 +----------------- src/interactivity/win32/clipboard.hpp | 1 - 11 files changed, 247 insertions(+), 271 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index b3c1fa7ecf3..831c672a586 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -6,10 +6,12 @@ #include "textBuffer.hpp" #include "CharRow.hpp" +#include "../types/inc/utils.hpp" #include "../types/inc/convert.hpp" #pragma hdrstop +using namespace Microsoft::Console; using namespace Microsoft::Console::Types; // Routine Description: @@ -1043,3 +1045,178 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe return data; } + +// Routine Description: +// - Generates a CF_HTML compliant structure based on the passed in text and color data +// Arguments: +// - rows - the text and color data we will format & encapsulate +// - fontHeightPoints - the unscaled font height +// - fontFaceName - the name of the font used +// - htmlTitle - value used in title tag of html header. Used to name the application +// Return Value: +// - string containing the generated HTML +std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPoints, const PCWCHAR fontFaceName, const std::string& htmlTitle) +{ + try + { + std::ostringstream htmlBuilder; + + // First we have to add some standard + // HTML boiler plate required for CF_HTML + // as part of the HTML Clipboard format + const std::string htmlHeader = + "" + htmlTitle + ""; + htmlBuilder << htmlHeader; + + htmlBuilder << ""; + + // apply global style in div element + { + htmlBuilder << "
"; + } + + // copy text and info color from buffer + bool hasWrittenAnyText = false; + std::optional fgColor = std::nullopt; + std::optional bkColor = std::nullopt; + for (UINT row = 0; row < rows.text.size(); row++) + { + size_t startOffset = 0; + + if (row != 0) + { + htmlBuilder << "
"; + } + + for (UINT col = 0; col < rows.text[row].length(); col++) + { + // do not include \r nor \n as they don't have attributes + // and are not HTML friendly. For line break use '
' instead. + bool isLastCharInRow = + col == rows.text[row].length() - 1 || + rows.text[row][col + 1] == '\r' || + rows.text[row][col + 1] == '\n'; + + bool colorChanged = false; + if (!fgColor.has_value() || rows.FgAttr[row][col] != fgColor.value()) + { + fgColor = rows.FgAttr[row][col]; + colorChanged = true; + } + + if (!bkColor.has_value() || rows.BkAttr[row][col] != bkColor.value()) + { + bkColor = rows.BkAttr[row][col]; + colorChanged = true; + } + + const auto writeAccumulatedChars = [&](bool includeCurrent) { + if (col > startOffset) + { + // note: this should be escaped (for '<', '>', and '&'), + // however MS Word doesn't appear to support HTML entities + htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text[row].data() + startOffset, col - startOffset + includeCurrent)); + startOffset = col; + } + }; + + if (colorChanged) + { + writeAccumulatedChars(false); + + if (hasWrittenAnyText) + { + htmlBuilder << ""; + } + + htmlBuilder << ""; + } + + hasWrittenAnyText = true; + + if (isLastCharInRow) + { + writeAccumulatedChars(true); + break; + } + } + } + + if (hasWrittenAnyText) + { + // last opened span wasn't closed in loop above, so close it now + htmlBuilder << ""; + } + + htmlBuilder << "
"; + + htmlBuilder << ""; + + constexpr std::string_view HtmlFooter = ""; + htmlBuilder << HtmlFooter; + + // once filled with values, there will be exactly 157 bytes in the clipboard header + constexpr size_t ClipboardHeaderSize = 157; + + // these values are byte offsets from start of clipboard + const size_t htmlStartPos = ClipboardHeaderSize; + const size_t htmlEndPos = ClipboardHeaderSize + gsl::narrow(htmlBuilder.tellp()); + const size_t fragStartPos = ClipboardHeaderSize + gsl::narrow(htmlHeader.length()); + const size_t fragEndPos = htmlEndPos - HtmlFooter.length(); + + // header required by HTML 0.9 format + std::ostringstream clipHeaderBuilder; + clipHeaderBuilder << "Version:0.9\r\n"; + clipHeaderBuilder << std::setfill('0'); + clipHeaderBuilder << "StartHTML:" << std::setw(10) << htmlStartPos << "\r\n"; + clipHeaderBuilder << "EndHTML:" << std::setw(10) << htmlEndPos << "\r\n"; + clipHeaderBuilder << "StartFragment:" << std::setw(10) << fragStartPos << "\r\n"; + clipHeaderBuilder << "EndFragment:" << std::setw(10) << fragEndPos << "\r\n"; + clipHeaderBuilder << "StartSelection:" << std::setw(10) << fragStartPos << "\r\n"; + clipHeaderBuilder << "EndSelection:" << std::setw(10) << fragEndPos << "\r\n"; + + return clipHeaderBuilder.str() + htmlBuilder.str(); + } + catch (...) + { + LOG_HR(wil::ResultFromCaughtException()); + return {}; + } +} diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 9cfd50bba0a..97b3375606d 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -145,6 +145,11 @@ class TextBuffer final std::function GetForegroundColor, std::function GetBackgroundColor) const; + static std::string GenHTML(const TextAndColor& rows, + const int fontHeightPoints, + const PCWCHAR fontFaceName, + const std::string& htmlTitle); + private: std::deque _storage; Cursor _cursor; diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index da2fe34c8ff..accaf8a7f4f 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1528,16 +1528,24 @@ namespace winrt::TerminalApp::implementation // terminal control raises it's CopyToClipboard event. // Arguments: // - copiedData: the new string content to place on the clipboard. - void App::_CopyToClipboardHandler(const winrt::hstring& copiedData) + void App::_CopyToClipboardHandler(const IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData) { _root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() { DataPackage dataPack = DataPackage(); dataPack.RequestedOperation(DataPackageOperation::Copy); - dataPack.SetText(copiedData); - Clipboard::SetContent(dataPack); - // TODO: MSFT 20642290 and 20642291 - // rtf copy and html copy + // copy text to dataPack + dataPack.SetText(copiedData.Text()); + + // copy html to dataPack + const auto htmlData = copiedData.Html(); + if (!htmlData.empty()) + { + dataPack.SetHtmlFormat(htmlData); + } + + Clipboard::SetContent(dataPack); }); } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 5d993e3410b..82ad67954e3 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -146,7 +146,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl(); - void _CopyToClipboardHandler(const winrt::hstring& copiedData); + void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData); void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs); static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 5626488e0fa..cc9ac5c5f25 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1396,12 +1396,23 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation if (_terminal != nullptr && _terminal->IsSelectionActive()) { // extract text from buffer - const auto copiedData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); + const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); + + // convert text: vector --> string + std::wstring textData; + for (const auto& text : bufferData.text) + { + textData += text; + } + + // convert text to HTML format + const auto htmlData = TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), "Windows Terminal"); _terminal->ClearSelection(); // send data up for clipboard - _clipboardCopyHandlers(copiedData); + auto copyArgs = winrt::make_self(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData)); + _clipboardCopyHandlers(*this, *copyArgs); return true; } return false; @@ -1802,9 +1813,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation // These macros will define them both for you. DEFINE_EVENT(TermControl, TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs); DEFINE_EVENT(TermControl, ConnectionClosed, _connectionClosedHandlers, TerminalControl::ConnectionClosedEventArgs); - DEFINE_EVENT(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::CopyToClipboardEventArgs); DEFINE_EVENT(TermControl, ScrollPositionChanged, _scrollPositionChangedHandlers, TerminalControl::ScrollPositionChangedEventArgs); - // clang-format on DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TermControl, CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs); + // clang-format on } diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 5876a8d43df..969584407cd 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -4,6 +4,7 @@ #pragma once #include "TermControl.g.h" +#include "CopyToClipboardEventArgs.g.h" #include "PasteFromClipboardEventArgs.g.h" #include #include @@ -14,6 +15,22 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { + struct CopyToClipboardEventArgs : + public CopyToClipboardEventArgsT + { + public: + CopyToClipboardEventArgs(hstring text, hstring html) : + _text(text), + _html(html) {} + + hstring Text() { return _text; }; + hstring Html() { return _html; }; + + private: + hstring _text; + hstring _html; + }; + struct PasteFromClipboardEventArgs : public PasteFromClipboardEventArgsT { @@ -64,9 +81,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation DECLARE_EVENT(TitleChanged, _titleChangedHandlers, TerminalControl::TitleChangedEventArgs); DECLARE_EVENT(ConnectionClosed, _connectionClosedHandlers, TerminalControl::ConnectionClosedEventArgs); DECLARE_EVENT(ScrollPositionChanged, _scrollPositionChangedHandlers, TerminalControl::ScrollPositionChangedEventArgs); - DECLARE_EVENT(CopyToClipboard, _clipboardCopyHandlers, TerminalControl::CopyToClipboardEventArgs); - DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(PasteFromClipboard, _clipboardPasteHandlers, TerminalControl::TermControl, TerminalControl::PasteFromClipboardEventArgs); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs); // clang-format on private: diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index e37b6bca393..9f9ac42484d 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -6,7 +6,12 @@ namespace Microsoft.Terminal.TerminalControl delegate void TitleChangedEventArgs(String newTitle); delegate void ConnectionClosedEventArgs(); delegate void ScrollPositionChangedEventArgs(Int32 viewTop, Int32 viewHeight, Int32 bufferLength); - delegate void CopyToClipboardEventArgs(String copiedData); + + runtimeclass CopyToClipboardEventArgs + { + String Text { get; }; + String Html { get; }; + } runtimeclass PasteFromClipboardEventArgs { @@ -24,7 +29,7 @@ namespace Microsoft.Terminal.TerminalControl event TitleChangedEventArgs TitleChanged; event ConnectionClosedEventArgs ConnectionClosed; - event CopyToClipboardEventArgs CopyToClipboard; + event Windows.Foundation.TypedEventHandler CopyToClipboard; event Windows.Foundation.TypedEventHandler PasteFromClipboard; String Title { get; }; diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index e8c2b5d601d..7dbe42363ef 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -152,7 +152,7 @@ class Microsoft::Terminal::Core::Terminal final : void SetEndSelectionPosition(const COORD position); void SetBoxSelection(const bool isEnabled) noexcept; - const std::wstring RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const; + const TextBuffer::TextAndColor RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const; #pragma endregion private: diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 33b0faf13b5..866f3fb13a7 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -261,24 +261,16 @@ void Terminal::ClearSelection() // and get text to appear on separate lines. // Return Value: // - wstring text from buffer. If extended to multiple lines, each line is separated by \r\n -const std::wstring Terminal::RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const +const TextBuffer::TextAndColor Terminal::RetrieveSelectedTextFromBuffer(bool trimTrailingWhitespace) const { std::function GetForegroundColor = std::bind(&Terminal::GetForegroundColor, this, std::placeholders::_1); std::function GetBackgroundColor = std::bind(&Terminal::GetBackgroundColor, this, std::placeholders::_1); - auto data = _buffer->GetTextForClipboard(!_boxSelection, - trimTrailingWhitespace, - _GetSelectionRects(), - GetForegroundColor, - GetBackgroundColor); - - std::wstring result; - for (const auto& text : data.text) - { - result += text; - } - - return result; + return _buffer->GetTextForClipboard(!_boxSelection, + trimTrailingWhitespace, + _GetSelectionRects(), + GetForegroundColor, + GetBackgroundColor); } // Method Description: diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index 874afd37dd7..54a48b4048a 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -239,246 +239,6 @@ TextBuffer::TextAndColor Clipboard::RetrieveTextFromBuffer(const SCREEN_INFORMAT GetBackgroundColor); } -// Routine Description: -// - Generates a CF_HTML compliant structure based on the passed in text and color data -// Arguments: -// - rows - the text and color data we will format & encapsulate -// Return Value: -// - string containing the generated HTML -std::string Clipboard::GenHTML(const TextBuffer::TextAndColor& rows) -{ - std::string szClipboard; // we will build the data going back in this string buffer - - try - { - std::string const szHtmlClipFormat = - "Version:0.9\r\n" - "StartHTML:%010d\r\n" - "EndHTML:%010d\r\n" - "StartFragment:%010d\r\n" - "EndFragment:%010d\r\n" - "StartSelection:%010d\r\n" - "EndSelection:%010d\r\n"; - - // measure clip header - size_t const cbHeader = 157; // when formats are expanded, there will be 157 bytes in the header. - - std::string const szHtmlHeader = - "Windows Console Host"; - size_t const cbHtmlHeader = szHtmlHeader.size(); - - std::string const szHtmlFragStart = ""; - std::string const szHtmlFragEnd = ""; - std::string const szHtmlFooter = ""; - size_t const cbHtmlFooter = szHtmlFooter.size(); - - std::string const szDivOuterBackgroundPattern = R"X(
)X"; - - size_t const cbDivOuter = 55; - std::string szDivOuter; - szDivOuter.reserve(cbDivOuter); - - std::string const szSpanFontSizePattern = R"X()X"; - - const auto& fontData = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetCurrentFont(); - int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / ServiceLocator::LocateGlobals().dpi; - size_t const cbSpanFontSize = 28 + (iFontHeightPoints / 10) + 1; - - std::string szSpanFontSize; - szSpanFontSize.resize(cbSpanFontSize + 1); // reserve space for null after string for sprintf - sprintf_s(szSpanFontSize.data(), cbSpanFontSize + 1, szSpanFontSizePattern.data(), iFontHeightPoints); - szSpanFontSize.resize(cbSpanFontSize); //chop off null at end - - std::string const szSpanStartPattern = R"X()X"; - - size_t const cbSpanStart = 53; // when format is expanded, there will be 53 bytes per color pattern. - std::string szSpanStart; - szSpanStart.resize(cbSpanStart + 1); // +1 for null terminator - - std::string const szSpanStartFontPattern = R"X()X"; - size_t const cbSpanStartFontPattern = 41; - - std::string const szSpanStartFontConstant = R"X()X"; - size_t const cbSpanStartFontConstant = 37; - - std::string szSpanStartFont; - size_t cbSpanStartFont; - bool fDeleteSpanStartFont = false; - - std::wstring const wszFontFaceName = fontData.GetFaceName(); - size_t const cchFontFaceName = wszFontFaceName.size(); - if (cchFontFaceName > 0) - { - // measure and create buffer to convert face name to UTF8 - int const cbNeeded = WideCharToMultiByte(CP_UTF8, 0, wszFontFaceName.data(), static_cast(cchFontFaceName), nullptr, 0, nullptr, nullptr); - std::string szBuffer; - szBuffer.resize(cbNeeded); - - // do conversion - WideCharToMultiByte(CP_UTF8, 0, wszFontFaceName.data(), static_cast(cchFontFaceName), szBuffer.data(), cbNeeded, nullptr, nullptr); - - // format converted font name into pattern - std::string const szFinalFontPattern = R"X()X"; - size_t const cbBytesNeeded = szFinalFontPattern.length(); - - fDeleteSpanStartFont = true; - szSpanStartFont = szFinalFontPattern; - cbSpanStartFont = cbBytesNeeded; - } - else - { - szSpanStartFont = szSpanStartFontConstant; - cbSpanStartFont = cbSpanStartFontConstant; - } - - std::string const szSpanEnd = ""; - std::string const szDivEnd = "
"; - - // Start building the HTML formated string to return - // First we have to add the required header and then - // some standard HTML boiler plate required for CF_HTML - // as part of the HTML Clipboard format - szClipboard.append(cbHeader, 'H'); // reserve space for a header we fill in later - szClipboard.append(szHtmlHeader); - szClipboard.append(szHtmlFragStart); - - COLORREF iBgColor = rows.BkAttr.at(0).at(0); - - szDivOuter.resize(cbDivOuter + 1); - sprintf_s(szDivOuter.data(), cbDivOuter + 1, szDivOuterBackgroundPattern.data(), GetRValue(iBgColor), GetGValue(iBgColor), GetBValue(iBgColor)); - szDivOuter.resize(cbDivOuter); - szClipboard.append(szDivOuter); - - // copy font face start - szClipboard.append(szSpanStartFont); - - // copy font size start - szClipboard.append(szSpanFontSize); - - bool bColorFound = false; - - // copy all text into the final clipboard data handle. There should be no nulls between rows of - // characters, but there should be a \0 at the end. - for (UINT iRow = 0; iRow < rows.text.size(); iRow++) - { - size_t cbStartOffset = 0; - size_t cchCharsToPrint = 0; - - COLORREF const Blackness = RGB(0x00, 0x00, 0x00); - COLORREF fgColor = Blackness; - COLORREF bkColor = Blackness; - - for (UINT iCol = 0; iCol < rows.text.at(iRow).length(); iCol++) - { - bool fColorDelta = false; - - if (!bColorFound) - { - fgColor = rows.FgAttr.at(iRow).at(iCol); - bkColor = rows.BkAttr.at(iRow).at(iCol); - bColorFound = true; - fColorDelta = true; - } - else if ((rows.FgAttr.at(iRow).at(iCol) != fgColor) || (rows.BkAttr.at(iRow).at(iCol) != bkColor)) - { - fgColor = rows.FgAttr.at(iRow).at(iCol); - bkColor = rows.BkAttr.at(iRow).at(iCol); - fColorDelta = true; - } - - if (fColorDelta) - { - if (cchCharsToPrint > 0) - { - // write accumulated characters to stream .... - std::string TempBuff; - int const cbTempCharsNeeded = WideCharToMultiByte(CP_UTF8, 0, rows.text[iRow].data() + cbStartOffset, static_cast(cchCharsToPrint), nullptr, 0, nullptr, nullptr); - TempBuff.resize(cbTempCharsNeeded); - WideCharToMultiByte(CP_UTF8, 0, rows.text[iRow].data() + cbStartOffset, static_cast(cchCharsToPrint), TempBuff.data(), cbTempCharsNeeded, nullptr, nullptr); - szClipboard.append(TempBuff); - cbStartOffset += cchCharsToPrint; - cchCharsToPrint = 0; - - // close previous span - szClipboard += szSpanEnd; - } - - // start new span - - // format with color then copy formatted string - szSpanStart.resize(cbSpanStart + 1); // add room for null - sprintf_s(szSpanStart.data(), cbSpanStart + 1, szSpanStartPattern.data(), GetRValue(fgColor), GetGValue(fgColor), GetBValue(fgColor), GetRValue(bkColor), GetGValue(bkColor), GetBValue(bkColor)); - szSpanStart.resize(cbSpanStart); // chop null from sprintf - szClipboard.append(szSpanStart); - } - - // accumulate 1 character - cchCharsToPrint++; - } - - PCWCHAR pwchAccumulateStart = rows.text.at(iRow).data() + cbStartOffset; - - // write accumulated characters to stream - std::string CharsConverted; - int cbCharsConverted = WideCharToMultiByte(CP_UTF8, 0, pwchAccumulateStart, static_cast(cchCharsToPrint), nullptr, 0, nullptr, nullptr); - CharsConverted.resize(cbCharsConverted); - WideCharToMultiByte(CP_UTF8, 0, pwchAccumulateStart, static_cast(cchCharsToPrint), CharsConverted.data(), cbCharsConverted, nullptr, nullptr); - szClipboard.append(CharsConverted); - } - - if (bColorFound) - { - // copy end span - szClipboard.append(szSpanEnd); - } - - // after we have copied all text we must wrap up - // with a standard set of HTML boilerplate required - // by CF_HTML - - // copy end font size span - szClipboard.append(szSpanEnd); - - // copy end font face span - szClipboard.append(szSpanEnd); - - // copy end background color span - szClipboard.append(szDivEnd); - - // copy HTML end fragment - szClipboard.append(szHtmlFragEnd); - - // copy HTML footer - szClipboard.append(szHtmlFooter); - - // null terminate the clipboard data - szClipboard += '\0'; - - // we are done generating formating & building HTML for the selection - // prepare the header text with the byte counts now that we know them - size_t const cbHtmlStart = cbHeader; // bytecount to start of HTML context - size_t const cbHtmlEnd = szClipboard.size() - 1; // don't count the null at the end - size_t const cbFragStart = cbHeader + cbHtmlHeader; // bytecount to start of selection fragment - size_t const cbFragEnd = cbHtmlEnd - cbHtmlFooter; - - // push the values into the required HTML 0.9 header format - std::string szHtmlClipHeaderFinal; - szHtmlClipHeaderFinal.resize(cbHeader + 1); // add room for a null - sprintf_s(szHtmlClipHeaderFinal.data(), cbHeader + 1, szHtmlClipFormat.data(), cbHtmlStart, cbHtmlEnd, cbFragStart, cbFragEnd, cbFragStart, cbFragEnd); - szHtmlClipHeaderFinal.resize(cbHeader); // chop off the null - - // overwrite the reserved space with the actual header & offsets we calculated - szClipboard.replace(0, cbHeader, szHtmlClipHeaderFinal.data()); - } - catch (...) - { - LOG_HR(wil::ResultFromCaughtException()); - szClipboard.clear(); // dont return a partial html fragment... - } - - return szClipboard; -} - // Routine Description: // - Copies the text given onto the global system clipboard. // Arguments: @@ -515,7 +275,9 @@ void Clipboard::CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, if (fAlsoCopyHtml) { - std::string HTMLToPlaceOnClip = GenHTML(rows); + const auto& fontData = ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetCurrentFont(); + int const iFontHeightPoints = fontData.GetUnscaledSize().Y * 72 / ServiceLocator::LocateGlobals().dpi; + std::string HTMLToPlaceOnClip = TextBuffer::GenHTML(rows, iFontHeightPoints, fontData.GetFaceName(), "Windows Console Host"); const size_t cbNeededHTML = HTMLToPlaceOnClip.size(); if (cbNeededHTML) { diff --git a/src/interactivity/win32/clipboard.hpp b/src/interactivity/win32/clipboard.hpp index 00da8aaf2df..d187ba51b7c 100644 --- a/src/interactivity/win32/clipboard.hpp +++ b/src/interactivity/win32/clipboard.hpp @@ -45,7 +45,6 @@ namespace Microsoft::Console::Interactivity::Win32 const std::vector& selectionRects); void CopyHTMLToClipboard(const TextBuffer::TextAndColor& rows); - std::string GenHTML(const TextBuffer::TextAndColor& rows); void CopyTextToSystemClipboard(const TextBuffer::TextAndColor& rows, _In_ bool const fAlsoCopyHtml); bool FilterCharacterOnPaste(_Inout_ WCHAR* const pwch); From 98f77818ffae33cba29723e3faa483355ca68652 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 20 Aug 2019 08:53:30 -0500 Subject: [PATCH 049/154] Draft Spec for Cascading Default + User Settings (#1258) * Start working on drafting this spec * Really add a LOT of notes * More spec updates. * Remove `hiddenProfiles` in favor of `profile.hidden` * Add info on how layering will work * add more powershell core info * Finish remaining TODO sections * Apply suggestions from code review Fix simple typos Co-Authored-By: Dustin L. Howett (MSFT) * Lots of feedback from PR * Try and make dynamic settings a bit clearer * more clearly call out serializing only what's different from a default- constructed `Profile` * Add more goals * add a blurb for user-default profile objects * Add updates concerning dynamic profile generation (#1321) * Add updates concerning dynamic profile generation This is based on discussion with @dhowett-msft we had o*line. We're trying to work through a way to prevent dynamic profiles from roaming to machines the dynamic profiles might not exist on. After writing this up, I'm not totally sure that it's a better design. * Add some initial updates from discussion * Pushing some updates here. I haven't given it a once over to ensure it's all consistent but it's worth reviewing @dhowett-msft * Some minor updates from Dustin * Fix a bunch of slightly more minor points in the spec * Move "Profile Ordering" to "Future considerations" * Add some notes on migrating profiles, GUID generation, de-duping profiles, and O R A N G E * Fix the indenting here * Update powershell core to be a dynamic profile, don't even mention other options. * Remaining PR feedback * Apply suggestions from code review Co-Authored-By: Michael Niksa * remove a dead comment --- doc/cascadia/Cascading-Default-Settings.md | 717 +++++++++++++++++++++ 1 file changed, 717 insertions(+) create mode 100644 doc/cascadia/Cascading-Default-Settings.md diff --git a/doc/cascadia/Cascading-Default-Settings.md b/doc/cascadia/Cascading-Default-Settings.md new file mode 100644 index 00000000000..7816468454e --- /dev/null +++ b/doc/cascadia/Cascading-Default-Settings.md @@ -0,0 +1,717 @@ +--- +author: Mike Griese @zadjii-msft +created on: 2019-05-31 +last updated: 2019-07-31 +issue id: 754 +--- + +# Cascading Default + User Settings + +## Abstract + +This spec outlines adding support for a cascading settings model. In this model, +there are two settings files, instead of one. + +1. The default settings file +2. The user's settings file + +The default settings file would be a static, read-only file shipped with the +terminal. The user settings file would then contain all the user's chosen +customizations to the settings. These two files would then be composed together +when the app is launched, so that the runtime settings are the union of both the +defaults and whatever modifications the user has chosen. This will enable the +app to always use a default schema that it knows will be valid, and minimize the +settings that the user needs to customize. + +Should the settings schema ever change, the defaults file will change, without +needing to re-write the user's settings file. + +It also outlines a mechanism by which profiles could be dynamically added or +hidden from the profiles list, based on some external source. + +## Inspiration + +Largely inspired by the settings model that both VS Code (and Sublime Text) use. + +### Goal: Minimize Re-Serializing `profiles.json` + +We want to re-serialize the user settings file, `profiles.json`, as little as +possible. Each time we serialize the file, there's the possiblity that we've +re-ordered the keys, as `jsoncpp` provides no ordering guarantee of the keys. +This isn't great, as each write of the file will randomly re-order the file. + +One of our overarching goals with this change should be to re-serialize the user +settings file as little as possible. + +### Goal: Minimize Content in `profiles.json` + +We want the user to only have to make the minimal number of changes possible to +the user settings file. Additionally, the user should only have to have the +settings that they've changed in that file. If the user wants to change only the +`cursorColor` of a profile, they should only need to set that property in the +user settings file, and not need an entire copy of the `Profile` object in their +user settings file. That would create additional noise that's not relevant to +the user. + +### Goal: Remove the Need to Reset Settings Entirely to get New Settings +One problem with the current settings design is that we only generate "default" +settings for the user when there's no settings file present at all. So, when we +want to do things like update the default profiles to have an icon, or add +support for generating WSL profiles, it will only apply to users for fresh +installs. Otherwise, a user needs to completely delete the settings file to have +the terminal re-generate the default settings. + +This is fairly annoying to the end-user, so ideally we'll find a way to be able +to prevent this scenario. + +### Goal: Prevent Roaming Settings from Failing +Another problem currently is that when settings roam to another machine, it's +possible that the second machine doesn't have the same applications installed as +the first, and some profiles might be totally invalid on the second machine. +Take for example, profiles for WSL distros. If you have and Ubuntu profile on +your first machine, and roam that profile to a second machine without Ubuntu +installed, then the Ubuntu profile would be totally broken on the second +machine. + +While we won't be able to non-destructively prevent all failures of this case, +we should be able to catch it in certain scenarios. + +## Solution Design + +The settings are now composed from two files: a "Default" settings file, and a +"User" settings file. + +When we load the settings, we'll perform the following steps, each mentioned in +greater detail below: +1. Load from disk the `defaults.json` (the default settings) -> DefaultsJson +1. Load from disk the `profiles.json` (the user settings) -> UserJson +1. Parse DefaultsJson to create all the default profiles, schemes, keybindings. +1. [Not covered in this spec] Check the UserJson to find the list of dynamic + profile sources that should run. +1. Run all the _enabled_ dynamic profile generators. Those profiles will be + added to the set of profiles. + - During this step, check if any of the profiles added here don't exist in + UserJson. If they _don't_, the generator created a profile that didn't + exist before. Return a value indicating the user settings should be + re-saved (with the new profiles added). +1. [Not covered in this spec] Layer the UserJson.globals.defaults settings to + every profile in the set, both the defaults, and generated profiles. +1. Apply the user settings from UserJson. Layer the profiles on top of the + existing profiles if possible (if both `guid` and `source` match). If a + profile from the user settings does not already exist, make sure to apply the + UserJson.globals.defaults settings first. Also layer Color schemes and + keybindings. + - If a profile has a `source` key, but there is not an existing profile with + a matching `guid` and `source`, don't create a new Profile object for it. + Either that generator didn't run, or the generator wanted to delete that + profile, so we'll effectively hide the profile. +1. Re-order the list of profiles, to match the ordering in the UserJson. If a + profile doesn't exist in UserJson, it should follow all the profiles in the + UserJson. If a profile listed in UserJson doesn't exist, we can skip it + safely in this step (the profile will be a dynamic profile that didn't get + populated.) +1. Validate the settings. +1. If requested in step 5, write the modified settings back to `profiles.json`. + +### Default Settings + +We'll have a static version of the "Default" file **hardcoded within the +application package**. This `defaults.json` file will live within the +application's package, which will prevent users from being able to edit it. + +```json +// This is an auto-generated file. Place any modifications to your settings in "profiles.json" +``` + +This disclaimer will help identify that the file shouldn't be modified. The file +won't actually be generated, but because it's shipped with our app, it'll be +overridden each time the app is updated. "Auto-generated" should be good enough +to indicate to users that it should not be modified. + +Because the `defaults.json` file is hardcoded within our application, we can use +its text directly, without loading the file from disk. This should help save +some startup time, as we'll only need to load the user settings from disk. + +When we make changes to the default settings, or we make changes to the settings +schema, we should make sure that we update the hardcoded `defaults.json` with +the new values. That way, the `defaults.json` file will always have the complete +set of settings in it. + +### Layering settings + +When we load the settings, we'll do it in three stages. First, we'll deserialize +the default settings that we've hardcoded. We'll then generate any profiles that +might come from dynamic profile sources. Then, we'll intelligently layer the +user's setting upon those we've already loaded. If a user wants to make changes +to some objects, like the default profiles, we'll need to make sure to load from +the user settings into the existing objects we created from the default +settings. + +* We'll need to make sure that any profile in the user settings that has a GUID + matching a default profile loads the user settings into the object created + from the defaults. +* We'll need to make sure that there's only one action bound to each key chord + for a keybinding. If there are any key chords in the user settings that match + a default key chord, we should bind them to the action from the user settings + instead. +* For any color schemes whose name matches the name of a default color scheme, + we'll need to apply the user settings to the existing color scheme. For + example, a user could override the `red` entry of the "Campbell" scheme to be + `#ff9900` if they want. This would then apply to all profiles using the + "Campbell" scheme. +* For profiles that were created from a dynamic profile source, they'll have + both a `guid` and `source` guid that must _both_ match. If a user profile with + a `source` set does not find a matching profile at load time, the profile will + be ignored. See more details in the [Dynamic Profiles](#dynamic-profiles) + section. + +### Hiding Default Profiles + +What if a user doesn't want to see one of the profiles that we've included in +the default profiles? + +We will add a `hidden` key to each profile, which defaults to false. When we +want to mark a profile as hidden, we'd just set that value to `true`, instead of +trying to look up the profile's guid. + +So, if someone wanted to hide the default cmd.exe profile, all they'd have to do +is add `"hidden": true` to the cmd.exe entry in their user settings, like so: + +```js +{ + "profiles": [ + { + // Make changes here to the cmd.exe profile + "guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}", + "hidden": true + } + ], +``` + +#### Hidden Profiles and the Open New Tab shortcuts + +Currently, there are keyboard shortcuts for "Open New Tab With Profile +<N>". These shortcuts will open up the Nth profile in the new tab +dropdown. Considering we're adding the ability to remove profiles from that +list, but keep them in the overall list of profiles, we'll need to make sure +that the handler for that event still opens the Nth _visible_ profile. + +### Serializing User Settings + +How can we tell that a setting should be written back to the user settings file? + +If the value of the setting isn't the same as the defaults, then it could easily +be added to the user's `profiles.json`. We'll have to do a smart serialization +of the various settings models. We'll pass in the default version **of that +model** during the serialization. If that object finds that a particular setting +is the same as a default setting, then we'll skip serializing it. + +What happens if a user has chosen to set the value to _coincidentally_ the same +value as the default value? We should keep that key in the user's settings file, +even though it is the same. + +In order to facilitate this, we'll need to keep the originally parsed user +settings around in memory. When we go to serialize the settings, we'll check if +either the setting exists already in the user settings file, or the setting has +changed. If either is true, then we'll make sure to write that setting back out. + +For serializing settings for the default profiles, we'll check if the setting is +in the user settings file, or if the value of the setting is different from the +version of that `Profile` from the default settings. For user-created profiles, +we'll compare the value of the setting with the value of the _default +constructed_ `Profile` object. This will help ensure that each profile in the +user's settings file maintains the minimal amount of info necessary. + +When we're adding profiles due to their generation in a dynamic profile +generator, we'll need to serialize them, then insert them back into the +originally parsed json object to be serialized. We don't want the automatic +creation of a new profile to automatically trigger re-writing the entire user +settings file, but we do want newly created dynamic profiles to have an entry +the user can easily edit. + +### Dynamic Profiles + +Sometimes, we may want to auto-generate a profile on the user's behalf. Consider +the case of WSL distros on their machine, or VMs running in Azure they may want +to auto-connect to. These _dynamic_ profiles have a source that might be added +or removed after the app is installed, and they will be different from user to +user. + +Currently, these profiles are only generated when a user first launches the +Terminal. If they already have a `profiles.json` file, then we won't run the +auto-generation behavior. This is obviously not great - if any new types of +dynamic profiles are added, then users that already have the Terminal installed +won't get any of these dynamic profiles. Furthemore, if any of the sources of +these dynamic profiles are removed, then the app won't auto-remove the +associated profile. + +In the new model, with a combined defaults & user settings, how should these +dynamic profiles work? + +I propose we add functionality to automatically search for these profile sources +and add/remove them on _every_ Terminal launch. To make this functionality work +appropriately, we'll need to introduce a constraint on dynamic profiles. + +**For any dynamic profiles, they must be able to be generated using a stable +GUID**. For example, any time we try adding the "Ubuntu" profile, we must be +able to generate the same GUID every time. This way, when a dynamic profile +generator runs, it can check if that profile source already has a profile +associated with it, and do nothing (as to not create many duplicate "Ubuntu" +profiles, for example). + +Additionally, each dynamic profile generator **must have a unique source guid** +to associate with the profile. When a dynamic profile is generated, the source's +guid will be added to the profile, to make sure the profile is correlated with +the source it came from. + +We'll generate these dynamic profiles immediately after parsing the default +profiles and settings. When a generator runs, it'll be able to create unique +profile GUIDs for each source it wants to generate a profile for. It'll hand +back a list of Profile objects, with settings set up how the generator likes, +with GUIDs set. + +After a dynamic profile generator runs, we will determine what new profiles need +to be added to the user settings, so we can append those to the list of +profiles. The deserializer will look at the list of generated profiles and check +if each and every one already has a entry in the user settings. The generator +will just blind hand back a list of profiles, and the deserializer will figure +out if any of them need to be added to the user settings. We'll store some sort +of result indicating that we want a save operation to occur. After the rest of +the deserializing is done, the app will then save the `profiles.json` file, +including these new profiles. + +When we're serializing the settings, instead of comparing a dynamic profile to +the default-constructed `Profile`, we'll compare it to the state of the +`Profile` after the dynamic profile generator created it. It'd then only +serialize settings that are different from the auto-generated version. It will +also always make sure that the `guid` of the dynamic profile is included in the +user settings file, as a point for the user to add customizations to the dynamic +profile to. Additionally, we'll also make sure the `source` is always serialized +as well, to keep the profile correlated with the generator that created it. + +We'll need to keep the state of these dynamically generated profiles around in +memory during runtime to be able to ensure the only state we're serializing is +that which is different from the initially generated dynamic profile. + +When the generator is run, and determines that a new profile has been added, +we'll need to make sure to add the profile to the user's settings file. This +will create an easy point for users to customize the dynamic profiles. When +added to the user settings, all that will be added is the `name`, `guid`, and +`source`. + +Additionally, a user might not want a dynamic profile generator to always run. +They might want to keep their Azure connections visible in the list of profiles, +even if its no longer a valid target. Or they might want to not automatically +connect to Azure to find new instances every time they launch the terminal. To +enable scenarios like this, we'll add an additional setting, +`disabledProfileSources`. This is an array of guids. If any guids are in that +list, then those dynamic profile generators _won't_ be run, suppressing those +profiles from appearing in the profiles list. + +If a dynamic profile generator needs to "delete" a profile, this will also work +naturally with the above rules. Lets examine the case where the user has +uninstalled the Ubuntu distro. When the WSL generator runs, it won't create the +Ubuntu profile. When we get to the Ubuntu profile in the user's settings, it'll +have a `source`, but we won't already have a profile with that `guid` and +`source`. So we'll just ignore it, because whatever source for that profile +doesn't want it anymore. Effectively, this will act like it was "deleted", +though the artifacts still remain untouched in the user's json. + +#### What if a dynamic profile is removed, but it's the default? + +I'll direct our attention to [#1348] - Display a specific error for not finding +the default profile. When we're done loading, and we determine that the default +profile doesn't exist in the finalized list of profiles, we'll display a dialog +to the user. This includes both hidden profiles and dynamic profiles that have +been "deleted". We'll temporarily use the _first_ profile instead. + +#### Dynamic profile GUID generation + +In order to help facilitate the generation of stable, unique GUIDs for +dynamically generated profiles, we'll enforce a few methods on each generator. +The Generator should implement a method that returns its _unique_ namespace for +profiles it generates: + +```c++ +class IDynamicProfileGenerator +{ + ... + virtual std::wstring GetNamespace() = 0; + ... +} +``` + +For example, the WSL generator would return `Microsoft.Terminal.WSL`. The +Powershell Core generator would return `Microsoft.Terminal.PowershellCore`. +We'll use these names to be able to generate uuidv5 GUIDs that will be unique +(so long as the names are unique). + +The generator should also be able to ask the app for two other pieces of +functionality: +* The generator should be able to ask the app for the generator's own namespace + GUID +* The generator should be able to ask the app for a uuidv5 in the generator's + namespace, given a specific name key. + +These two functions will be exposed to the generator like so: + +```c++ +GUID GetNamespaceGuid(IDynamicProfileGenerator& generator); +GUID GetGuidForName(IDynamicProfileGenerator& generator, std::wstring& name); +``` + +The generator does not _need_ to use `GetGuidForName` to generate guids for it's +profiles. If the generator can determine another way to generate stable GUIDs +for its profiles, it's free to use whatever method it wants. `GetGuidForName` is +provided as a convenience. + +It's not the responsibility of the dynamic profile generator to fill in the +`source` of the profiles it generates. The deserializer will make sure to go +through and fill in the guid for the generated profiles given the generator's +namespace GUID. + +### Powershell Core & the Defaults + +How do we handle the potential existence of Powershell Core in this model? +Powershell core is unique as far as the default profiles goes - it may or may +not exist on the user's system. Not only that, but depending on the user's +install of Powershell Core, it might have a path in either `Program Files` or +`Program Files(x86)`. + +Additionally, if it _is_ installed, we set it as the default profile instead of +Windows Powershell. + +Powershell core acts much like a dynamic profile. It has an installation source +that may or not be there. So we'll add a dynamic profile generator for +Powershell Core. This will automatically create a profile for Powershell Core if +necessary. + +Unlike the other dynamic profiles, if Powershell Core is present on +_first_ launch of the terminal, we set that as the default profile. This can +still be done - we'll need to do some special-case work when we're loading the +user settings and we _don't_ find any existing settings. When that happens, +we'll generate all the default user settings. Before we commit them, we'll check +if the Powershell Core profile exists, and if it does, we'll set that as the +default profile before writing the settings to disk. + +### Unbinding a Keybinding + +How can a user unbind a key that's part of the default keybindings? What if a +user really wants ctrl+t to fall through to the +commandline application attached to the shell, instead of opening a new tab? + +We'll need to introduce a new keybinding command that should indicate that the +key is unbound. We'll load the user keybindings and layer them on the defaults +as described above. If during the deserializing we find an entry that's bound to +the command `"unbound"` or any other string that we don't understand, instead of +trying to _set_ the keybinding, we'll _clear_ the keybinding with a new method +`AppKeyBindings::ClearKeyBinding(chord)`. + +### Removing the Globals Object + +As a part of #[1005](https://github.com/microsoft/terminal/pull/1005), all the +global settings were moved to their own object within the serialized settings. +This was to try and make the file easier to parse as a user, considering global +settings would be intermingled with profiles, keybindings, color schemes, etc. +Since this change will make the user settings dramatically easier to navigate, +we should probably remove the `globals` object, and have globals at the root +level again. + +### Default `profiles.json` + +Below is an example of what the default user settings file might look like when +it's first generated, taking all the above points into consideration. + +```js +// To view the default settings, open \defaults.json +{ + "defaultProfile" : "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + "profiles": [ + { + // Make changes here to the cmd.exe profile + "guid": "{6239a42c-1de4-49a3-80bd-e8fdd045185c}" + }, + { + // Make changes here to the Windows Powershell profile + "guid": "{086a83cd-e4ef-418b-89b1-3f6523ff9195}", + }, + { + "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + "name" : "Powershell Core", + "source": "{2bde4a90-d05f-401c-9492-e40884ead1d8}", + } + ], + + // Add custom color schemes to this array + "schemes": [], + + // Add any keybinding overrides to this array. + // To unbind a default keybinding, set the command to "unbound" + "keybindings": [] +} + +``` + +Note the following: +* cmd.exe and powershell.exe are both in the file, as to give users an easy + point to extend the settings for those default profiles. +* Powershell Core is included in the file, and the default profile has been set + to its GUID. The `source` has been set, indicating that it came from a dynamic profile source. +* There are a few helpful comments scattered throughout the file to help point + the user in the right direction. + +### Re-ordering profiles + +Since there are shortcuts to open the Nth profile in the list of profiles, we +need to expose a way for the user to change the order of the profiles. This was +not a problem when there was only a single list of profiles, but if the defaults +are applied _first_ to the list of profiles, then the user wouldn't be able to +change the order of the default profiles. Additionally, any profiles they add +would _always_ show up after the defaults. + +To remedy this, we could scan the user profiles in the user settings first, and +create `Profile` objects for each of those profiles first. These `Profile`s +would only be initialized with their GUID temporarily, but they'd be placed into +the list of profiles in the order they appear in the user's settings. Then, we'd +load all the default settings, overlaying any default profiles on the `Profile` +objects that might already exist in the list of profiles. If there are any +default profiles that don't appear in the user's settings, they'll appear +_after_ any profiles in the user's settings. Then, we'll overlay the full user +settings on top of the defaults. + +## UI/UX Design + +### Opening `defaults.json` +How do we open both these files to show to the user (for the interim period +before a proper Settings UI is created)? Currently, the "Settings" button only +opens a single json file, `profiles.json`. We could keep that button doing the +same thing, though we want the user to be able to also view the default settings +file, to be able to inspect what settings they wish to change. + +We could have the "Settings" button open _both_ files at the same +time. I'm not sure that `ShellExecute` (which is used to open these files) +provides any ordering guarantees, so it's possible that the `defaults.json` +would open in the foreground of the default json editor, while making in unclear +that there's another file they should be opening instead. Additionally, if +there's _no_ `.json` editor for the user, I believe the shell will attempt +_twice_ to ask the user to select a program to open the file with, and it might +not be clear that they need to select a program in both dialogs. + +Alternatively, we could make the defaults file totally inaccessible from the +Terminal UI, and instead leave a comment in the auto-generated `profiles.json` +like so: + +```json +// To view the default settings, open the defaults.json file in this directory +``` + +The "Settings" button would then only open the file the user needs to edit, and +provide them instructions on how to open the defaults file. + +There could alternatively be a hidden option for the "Open Settings" button, +where holding Alt while clicking on the button would open the +`defaults.json` instead. + +We could additionally add a `ShortcutAction` (to be bound to a keybinding) that +would `openDefaultSettings`, and we could bind that to +ctrl+alt+\`, similar to `openSettings` on +ctrl+\`. + +### How does this work with the settings UI? + +If we only have one version of the settings models (Globals, Profiles, +ColorShemes, Keybindings) at runtime, and the user changes one of the settings +with the settings UI, how can we tell that settings changed? + +Fortunately, this should be handled cleanly by the algorithm proposed above, in +the "Serializing User Settings" section. We'll only be serializing settings that +have changed from the defaults, so only the actual changes they've made will be +persisted back to the user settings file. + +## Capabilities +### Security + +I don't think this will introduce any new security issues that weren't already +present + +### Reliability +I don't think this will introduce any new reliability concerns that weren't +already present. We will likely improve our reliability, as dynamic profiles +that no longer exist will not cause the terminal to crash on startup anymore. + +### Performance, Power, and Efficiency + +By not writing the defaults to disk, we'll theoretically marginally improve the +load and save times for the `profiles.json` file, by simply having a smaller +file to load. However we'll also be doing more work to process the layering of +defaults and user settings, which will likely slightly increase the load times. +Overall, I expect the difference to be negligible due to these factors. + +One potential concern is long-running dynamic profile generators. Because +they'll need to run on startup, they could negatively impact startup time. You +can read more below, in "Dynamic Profile Generators Need to be Enabled". + +### Accessibility +N/A + +## Potential Issues + +### Profiles with the same `guid` as a dynamic profile but not the same `source` + +What happens if the User settings has a profile with a `guid` that matches a +dynamic or default profile, but the user profile doesn't have a matching source? +This could happen trivially easily if the user deletes the `source` key from a +profile that has dynamically generated. + +We could: +1. Treat the profile as an entirely separate profile + - There's lots of other code that assumes each profile has only a unique GUID, + so we'd have to change the GUID of this profile. This would mean writing out + the user settings, which we'd like to avoid. + - We'll still end up generating the entry for the dynamic profile in the + user's settings, so we'll need to write out the user settings anyways. + - This other profile will likely not have a commandline set, so it might not + work at all. +1. Ignore the profile entirely. + - When the dynamic profile generator runs, we're not going to find another + entry in the user profiles with both a matching `guid` and a matching + `source`. So we'll end up creating _another_ entry in the user profiles for + the dynamic profile. + - How could the user know that the profile is being ignored? There's nothing + in the file itself that indicates obviously that this profile is now + invalid. +1. Treat the user settings as part of the dynamic profile + - In this scenario, the user profile continues to exist as part of the dynamic profile. + - When the dynamic profile generator runs, we're not going to find another + entry in the user profiles with both a matching `guid` and a matching + `source`. So we'll end up creating _another_ entry in the user profiles for + the dynamic profile. + - These two entries will each be layered upon the dynamically generated + profile, so the settings in the second profile entry will override + settings from the first. + - If the user disables the generator, or the profile source is removed, the + dynamic profile will cease to exist. However, the profile without the + `source` entry will remain, though likely will not work. + - How do we order these profiles for the user? When we're parsing the user + profiles list to build an ordering of profiles, do we use the first entry as + the index for that profile? +1. (Variant of the above) Treat the profile as part of the dynamic profile, and + re-insert the `source` key. + - This will re-connect the user profile to the dynamic one. + - We'll need to make sure to do this before determining the new dynamic + profiles to add to the user settings. + - Given all the scenarios are going to cause a user settings write anyways, + this isn't terrible. + - If the user _really_ wants to split the profile in their user settings from + the dynamic one, they're free to always generate a new guid _and_ delete the + `source` key. + +Given the drawbacks associated with options 1-3, I propose we choose option 4 as +our solution to this case. + +### Migrating Existing Settings + +I believe that existing `profiles.json` files will smoothly update to this +model, without breaking. While in the new model, the `profiles.json` file can be +much more sparse, users who have existing `profiles.json` files will have full +settings in their user settings. We'll leave their files largely untouched, as +we won't touch keys that have the same values as defaults that are currently in +the `profiles.json` file. Fortunately though, users should be able to remove +much of the boilerplate from their `profiles.json` files, and trim it down just +to their modifications. + +#### Migrating Powershell Core + +Right now, default-generated Powershell Core profiles exist with a stable guid +we've generated for them. However, when we move Powershell Core to being a +dynamically generated profile, we'll have to ensure that we don't create a +duplicated "dynamic" entry for that profile. If we want to convert the existing +Powershell Core profiles into a dynamic profile, we'll need to make sure to add +a `source` key to the profile. Everything else in the profile can remain the +same. Once the `source` is added, we'll know to treat it as a dynamic profile, +and it'll respond dynamically. + +This is actually something that will automatically be covered by the scenario +mentioned above in "Profiles with the same `guid` as a dynamic profile but not +the same `source`". When we encounter the existing Powershell Core profiles that +don't have a `source`, we'll automatically think they're the dynamically +generated ones, and auto-migrate them. + +#### Migrating Existing WSL Profiles + +Similar to the above, so long as we ensure the WSL dynamic profile generator +generates the _same_ GUIDs as it does currently, all the existing WSL profiles +will automatically be migrated to dynamic profiles. + +### Dynamic Profile Generators Need to be Enabled +With the current proposal, profiles that are generated by a dynamic profile +generator _need_ that generator to be enabled for the profile to appear in the +list of profiles. If the generator isn't enabled, then the important parts of +the profile (name, commandline) will never be set, and the profile's settings +from the user settings will be ignored at runtime. + +For generators where the generation of profiles might be a lengthy process, this +could negatively impact startup time. Take for example, some hypothetical +generator that needs to make web requests to generate dynamic profiles. Because +we need the finalized settings to be able to launch the terminal, we'll be stuck +loading until that generator is complete. + +However, if the user disables that generator entirely, we'll never display that +profile to the user, even if they've done that setup before. + +So the trade-off with this design is that non-existent dynamic profiles will +never roam to machines where they don't exist and aren't valid, but the +generators _must_ be enabled to use the dynamic profiles. + +## Future considerations +* It's possible that a very similar layering loading mechanism could be used to + layer per-machine settings with roaming settings. Currently, there's only one + settings file, and it roams to all your devices. This could be problematic, + for example, if one of your machines has a font installed, but another + doesn't. A proposed solution to that problem was to have both roaming settings + and per-machine settings. The code to layer settings from the defaults and the + user settings could be re-used to handle layer the roaming and per-machine + settings. +* What if an extension wants to generate their own dynamic profiles? We've + already outlined a contract that profile generators would have to follow to + behave correctly. It's possible that we could abstract our implementation into + a WinRT interface that extensions could implement, and be triggered just like + other dynamic profile generators. +* **Multiple settings files** - This could enable us to place color schemes into + a seperate file (like `colorschemes.json`) and put keybindings into their own + file as well, and reduce the number of settings in the user's `profiles.json`. + It's unclear if this is something that we need quite yet, but the same + layering functionality that enables this scenario could also enable more than + two sources for settings. +* **Global Default Profile Settings** - Say a user wants to override what the + defaults for a profile are, so that they can set settings for _all_ their + profiles at once? We could maybe introduce a profile in the user settings file + with a special guid set to `"default`, that we look for first, and treat + specially. We wouldn't include it in the list of profiles. When we're creating + profiles, we'll start with that profile as our prototype, instead of using the + default-constructed `Profile`. When we're serializing profiles, we'd again use + that as the point of comparison to check if a setting's value has changed. + There may be more unknowns with this proposal, so I leave it for a future + feature spec. + - We'll also want to make sure that when we're serializing default/dynamic + profiles, we take into account the state from the global defaults, and we + don't duplicate that inormation into the entries for those types of profiles + in the user profiles. +* **Re-ordering profiles** - Under "Solution Design", we provide an algorithm + for decoding the settings. One of the steps mentioned is parsing the user + settings to determine the ordering of the profiles. It's possible in the + future we may want to give the user more control over this ordering. Maybe + we'll want to allow the user to manually index the profiles. Or, as discussed + in issues like #1571, we may want to allow the user to further customize the + new tab dropdown, beyond just the order of profiles. The re-ordering step + would be a great place to add code to support this re-ordering, with whatever + algorithm we eventually land on. Determining such an algorithm is outside the + scope of this spec, however. + +## Resources +N/A + + + + +[#1348]: https://github.com/microsoft/terminal/issues/1348 From ff87190823ea699a7eee4dafda34358ee0caffa4 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 20 Aug 2019 09:42:17 -0700 Subject: [PATCH 050/154] Added CopyOnSelect as a Global Setting (#2152) * Added CopyOnSelect as a ControlSetting * Updated doc * Updated doc * CopyOnSelect feature changes (like, overall) * Made CopyOnSelect a CoreSetting CopyOnSelect value accessible through Terminal's IsCopyOnSelectActive * Refactor a bit. * CopyOnSelect Tests * PR nits --- doc/cascadia/SettingsSchema.md | 1 + .../TerminalApp/GlobalAppSettings.cpp | 21 +++++- src/cascadia/TerminalApp/GlobalAppSettings.h | 4 ++ src/cascadia/TerminalControl/TermControl.cpp | 67 ++++++++++++------- src/cascadia/TerminalCore/Terminal.cpp | 4 ++ src/cascadia/TerminalCore/Terminal.hpp | 4 ++ .../TerminalCore/TerminalSelection.cpp | 36 +++++++++- .../TerminalSettings/ICoreSettings.idl | 1 + .../TerminalSettings/TerminalSettings.cpp | 11 +++ .../TerminalSettings/terminalsettings.h | 3 + .../UnitTests_TerminalCore/MockTermSettings.h | 3 + .../UnitTests_TerminalCore/SelectionTest.cpp | 58 ++++++++++++++++ 12 files changed, 185 insertions(+), 28 deletions(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index 4ef60e9dc05..ecbaae39293 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -6,6 +6,7 @@ Properties listed below affect the entire window, regardless of the profile sett | Property | Necessity | Type | Default | Description | | -------- | --------- | ---- | ------- | ----------- | | `alwaysShowTabs` | _Required_ | Boolean | `true` | When set to `true`, tabs are always displayed. When set to `false` and `showTabsInTitlebar` is set to `false`, tabs only appear after typing Ctrl + T. | +| `copyOnSelect` | Optional | Boolean | `false` | When set to `true`, a selection is immediately copied to your clipboard upon creation. When set to `false`, the selection persists and awaits further action. | | `defaultProfile` | _Required_ | String | PowerShell guid | Sets the default profile. Opens by typing Ctrl + T or by clicking the '+' icon. The guid of the desired default profile is used as the value. | | `initialCols` | _Required_ | Integer | `120` | The number of columns displayed in the window upon first load. | | `initialRows` | _Required_ | Integer | `30` | The number of rows displayed in the window upon first load. | diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.cpp b/src/cascadia/TerminalApp/GlobalAppSettings.cpp index 4f6a9d8d396..eba5beedca7 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalApp/GlobalAppSettings.cpp @@ -24,6 +24,7 @@ static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTi static constexpr std::string_view RequestedThemeKey{ "requestedTheme" }; static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; static constexpr std::string_view WordDelimitersKey{ "wordDelimiters" }; +static constexpr std::string_view CopyOnSelectKey{ "copyOnSelect" }; static constexpr std::wstring_view LightThemeValue{ L"light" }; static constexpr std::wstring_view DarkThemeValue{ L"dark" }; @@ -39,7 +40,8 @@ GlobalAppSettings::GlobalAppSettings() : _showTitleInTitlebar{ true }, _showTabsInTitlebar{ true }, _requestedTheme{ ElementTheme::Default }, - _wordDelimiters{ DEFAULT_WORD_DELIMITERS } + _wordDelimiters{ DEFAULT_WORD_DELIMITERS }, + _copyOnSelect{ false } { } @@ -117,6 +119,16 @@ void GlobalAppSettings::SetWordDelimiters(const std::wstring wordDelimiters) noe _wordDelimiters = wordDelimiters; } +bool GlobalAppSettings::GetCopyOnSelect() const noexcept +{ + return _copyOnSelect; +} + +void GlobalAppSettings::SetCopyOnSelect(const bool copyOnSelect) noexcept +{ + _copyOnSelect = copyOnSelect; +} + #pragma region ExperimentalSettings bool GlobalAppSettings::GetShowTabsInTitlebar() const noexcept { @@ -141,6 +153,7 @@ void GlobalAppSettings::ApplyToSettings(TerminalSettings& settings) const noexce settings.InitialRows(_initialRows); settings.InitialCols(_initialCols); settings.WordDelimiters(_wordDelimiters); + settings.CopyOnSelect(_copyOnSelect); } // Method Description: @@ -160,6 +173,7 @@ Json::Value GlobalAppSettings::ToJson() const jsonObject[JsonKey(ShowTitleInTitlebarKey)] = _showTitleInTitlebar; jsonObject[JsonKey(ShowTabsInTitlebarKey)] = _showTabsInTitlebar; jsonObject[JsonKey(WordDelimitersKey)] = winrt::to_string(_wordDelimiters); + jsonObject[JsonKey(CopyOnSelectKey)] = _copyOnSelect; jsonObject[JsonKey(RequestedThemeKey)] = winrt::to_string(_SerializeTheme(_requestedTheme)); jsonObject[JsonKey(KeybindingsKey)] = AppKeyBindingsSerialization::ToJson(_keybindings); @@ -210,6 +224,11 @@ GlobalAppSettings GlobalAppSettings::FromJson(const Json::Value& json) result._wordDelimiters = GetWstringFromJson(wordDelimiters); } + if (auto copyOnSelect{ json[JsonKey(CopyOnSelectKey)] }) + { + result._copyOnSelect = copyOnSelect.asBool(); + } + if (auto requestedTheme{ json[JsonKey(RequestedThemeKey)] }) { result._requestedTheme = _ParseTheme(GetWstringFromJson(requestedTheme)); diff --git a/src/cascadia/TerminalApp/GlobalAppSettings.h b/src/cascadia/TerminalApp/GlobalAppSettings.h index 293ba743233..11dab24ccaa 100644 --- a/src/cascadia/TerminalApp/GlobalAppSettings.h +++ b/src/cascadia/TerminalApp/GlobalAppSettings.h @@ -50,6 +50,9 @@ class TerminalApp::GlobalAppSettings final std::wstring GetWordDelimiters() const noexcept; void SetWordDelimiters(const std::wstring wordDelimiters) noexcept; + bool GetCopyOnSelect() const noexcept; + void SetCopyOnSelect(const bool copyOnSelect) noexcept; + winrt::Windows::UI::Xaml::ElementTheme GetRequestedTheme() const noexcept; Json::Value ToJson() const; @@ -72,6 +75,7 @@ class TerminalApp::GlobalAppSettings final bool _showTabsInTitlebar; std::wstring _wordDelimiters; + bool _copyOnSelect; winrt::Windows::UI::Xaml::ElementTheme _requestedTheme; static winrt::Windows::UI::Xaml::ElementTheme _ParseTheme(const std::wstring& themeString) noexcept; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index cc9ac5c5f25..757f03b93ea 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -773,15 +773,14 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } else if (point.Properties().IsRightButtonPressed()) { - // copy selection, if one exists - if (_terminal->IsSelectionActive()) + // copyOnSelect causes right-click to always paste + if (_terminal->IsCopyOnSelectActive() || !_terminal->IsSelectionActive()) { - CopySelectionToClipboard(!shiftEnabled); + PasteTextFromClipboard(); } - // paste selection, otherwise else { - PasteTextFromClipboard(); + CopySelectionToClipboard(!shiftEnabled); } } } @@ -808,7 +807,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) { - if (_terminal->IsSelectionActive() && point.Properties().IsLeftButtonPressed()) + if (point.Properties().IsLeftButtonPressed()) { const auto cursorPosition = point.Position(); _SetEndSelectionPointAtCursor(cursorPosition); @@ -885,7 +884,19 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); - if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch) + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) + { + const auto modifiers = static_cast(args.KeyModifiers()); + // static_cast to a uint32_t because we can't use the WI_IsFlagSet + // macro directly with a VirtualKeyModifiers + const auto shiftEnabled = WI_IsFlagSet(modifiers, static_cast(VirtualKeyModifiers::Shift)); + + if (_terminal->IsCopyOnSelectActive()) + { + CopySelectionToClipboard(!shiftEnabled); + } + } + else if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Touch) { _touchAnchor = std::nullopt; } @@ -1387,35 +1398,41 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } // Method Description: - // - get text from buffer and send it to the Windows Clipboard (CascadiaWin32:main.cpp). Also removes rendering of selection. + // - Given a copy-able selection, get the selected text from the buffer and send it to the + // Windows Clipboard (CascadiaWin32:main.cpp). + // - CopyOnSelect does NOT clear the selection // Arguments: // - trimTrailingWhitespace: enable removing any whitespace from copied selection // and get text to appear on separate lines. bool TermControl::CopySelectionToClipboard(bool trimTrailingWhitespace) { - if (_terminal != nullptr && _terminal->IsSelectionActive()) + // no selection --> nothing to copy + if (_terminal == nullptr || !_terminal->IsSelectionActive()) { - // extract text from buffer - const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); + return false; + } + // extract text from buffer + const auto bufferData = _terminal->RetrieveSelectedTextFromBuffer(trimTrailingWhitespace); - // convert text: vector --> string - std::wstring textData; - for (const auto& text : bufferData.text) - { - textData += text; - } + // convert text: vector --> string + std::wstring textData; + for (const auto& text : bufferData.text) + { + textData += text; + } - // convert text to HTML format - const auto htmlData = TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), "Windows Terminal"); + // convert text to HTML format + const auto htmlData = TextBuffer::GenHTML(bufferData, _actualFont.GetUnscaledSize().Y, _actualFont.GetFaceName(), "Windows Terminal"); + if (!_terminal->IsCopyOnSelectActive()) + { _terminal->ClearSelection(); - - // send data up for clipboard - auto copyArgs = winrt::make_self(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData)); - _clipboardCopyHandlers(*this, *copyArgs); - return true; } - return false; + + // send data up for clipboard + auto copyArgs = winrt::make_self(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData)); + _clipboardCopyHandlers(*this, *copyArgs); + return true; } // Method Description: diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 1ff68683325..4e01f033af1 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -45,6 +45,8 @@ Terminal::Terminal() : _snapOnInput{ true }, _boxSelection{ false }, _selectionActive{ false }, + _allowSingleCharSelection{ false }, + _copyOnSelect{ false }, _selectionAnchor{ 0, 0 }, _endSelectionPosition{ 0, 0 } { @@ -135,6 +137,8 @@ void Terminal::UpdateSettings(winrt::Microsoft::Terminal::Settings::ICoreSetting _wordDelimiters = settings.WordDelimiters(); + _copyOnSelect = settings.CopyOnSelect(); + // TODO:MSFT:21327402 - if HistorySize has changed, resize the buffer so we // have a smaller scrollback. We should do this carefully - if the new buffer // size is smaller than where the mutable viewport currently is, we'll want diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 7dbe42363ef..876d21dacfc 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -146,6 +146,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma region TextSelection // These methods are defined in TerminalSelection.cpp + const bool IsCopyOnSelectActive() const noexcept; void DoubleClickSelection(const COORD position); void TripleClickSelection(const COORD position); void SetSelectionAnchor(const COORD position); @@ -183,6 +184,8 @@ class Microsoft::Terminal::Core::Terminal final : COORD _endSelectionPosition; bool _boxSelection; bool _selectionActive; + bool _allowSingleCharSelection; + bool _copyOnSelect; SHORT _selectionAnchor_YOffset; SHORT _endSelectionPosition_YOffset; std::wstring _wordDelimiters; @@ -234,5 +237,6 @@ class Microsoft::Terminal::Core::Terminal final : COORD _ExpandDoubleClickSelectionRight(const COORD position) const; const bool _isWordDelimiter(std::wstring_view cellChar) const; const COORD _ConvertToBufferCell(const COORD viewportPos) const; + const bool _isSingleCellSelection() const noexcept; #pragma endregion }; diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 866f3fb13a7..2131a762d24 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -14,7 +14,7 @@ std::vector Terminal::_GetSelectionRects() const { std::vector selectionArea; - if (!_selectionActive) + if (!IsSelectionActive()) { return selectionArea; } @@ -67,7 +67,7 @@ std::vector Terminal::_GetSelectionRects() const if (_multiClickSelectionMode == SelectionExpansionMode::Word) { const auto cellChar = _buffer->GetCellDataAt(selectionAnchorWithOffset)->Chars(); - if (_selectionAnchor == _endSelectionPosition && _isWordDelimiter(cellChar)) + if (_isSingleCellSelection() && _isWordDelimiter(cellChar)) { // only highlight the cell if you double click a delimiter } @@ -142,15 +142,39 @@ const SHORT Terminal::_ExpandWideGlyphSelectionRight(const SHORT xPos, const SHO return position.X; } +// Method Description: +// - Checks if selection is on a single cell +// Return Value: +// - bool representing if selection is only a single cell. Used for copyOnSelect +const bool Terminal::_isSingleCellSelection() const noexcept +{ + return (_selectionAnchor == _endSelectionPosition); +} + // Method Description: // - Checks if selection is active // Return Value: // - bool representing if selection is active. Used to decide copy/paste on right click const bool Terminal::IsSelectionActive() const noexcept { + // A single cell selection is not considered an active selection, + // if it's not allowed + if (!_allowSingleCharSelection && _isSingleCellSelection()) + { + return false; + } return _selectionActive; } +// Method Description: +// - Checks if the CopyOnSelect setting is active +// Return Value: +// - true if feature is active, false otherwise. +const bool Terminal::IsCopyOnSelectActive() const noexcept +{ + return _copyOnSelect; +} + // Method Description: // - Select the sequence between delimiters defined in Settings // Arguments: @@ -211,6 +235,8 @@ void Terminal::SetSelectionAnchor(const COORD position) _selectionAnchor_YOffset = gsl::narrow(_ViewStartIndex()); _selectionActive = true; + _allowSingleCharSelection = (_copyOnSelect) ? false : true; + SetEndSelectionPosition(position); _multiClickSelectionMode = SelectionExpansionMode::Cell; @@ -230,6 +256,11 @@ void Terminal::SetEndSelectionPosition(const COORD position) // copy value of ViewStartIndex to support scrolling // and update on new buffer output (used in _GetSelectionRects()) _endSelectionPosition_YOffset = gsl::narrow(_ViewStartIndex()); + + if (_copyOnSelect && !_isSingleCellSelection()) + { + _allowSingleCharSelection = true; + } } // Method Description: @@ -246,6 +277,7 @@ void Terminal::SetBoxSelection(const bool isEnabled) noexcept void Terminal::ClearSelection() { _selectionActive = false; + _allowSingleCharSelection = false; _selectionAnchor = { 0, 0 }; _endSelectionPosition = { 0, 0 }; _selectionAnchor_YOffset = 0; diff --git a/src/cascadia/TerminalSettings/ICoreSettings.idl b/src/cascadia/TerminalSettings/ICoreSettings.idl index 99e08d9c0aa..b715bd70868 100644 --- a/src/cascadia/TerminalSettings/ICoreSettings.idl +++ b/src/cascadia/TerminalSettings/ICoreSettings.idl @@ -28,6 +28,7 @@ namespace Microsoft.Terminal.Settings CursorStyle CursorShape; UInt32 CursorHeight; String WordDelimiters; + Boolean CopyOnSelect; }; } diff --git a/src/cascadia/TerminalSettings/TerminalSettings.cpp b/src/cascadia/TerminalSettings/TerminalSettings.cpp index f36ff913b97..70c2d4f7a5f 100644 --- a/src/cascadia/TerminalSettings/TerminalSettings.cpp +++ b/src/cascadia/TerminalSettings/TerminalSettings.cpp @@ -21,6 +21,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation _cursorShape{ CursorStyle::Vintage }, _cursorHeight{ DEFAULT_CURSOR_HEIGHT }, _wordDelimiters{ DEFAULT_WORD_DELIMITERS }, + _copyOnSelect{ false }, _useAcrylic{ false }, _closeOnExit{ true }, _tintOpacity{ 0.5 }, @@ -149,6 +150,16 @@ namespace winrt::Microsoft::Terminal::Settings::implementation _wordDelimiters = value; } + bool TerminalSettings::CopyOnSelect() + { + return _copyOnSelect; + } + + void TerminalSettings::CopyOnSelect(bool value) + { + _copyOnSelect = value; + } + bool TerminalSettings::UseAcrylic() { return _useAcrylic; diff --git a/src/cascadia/TerminalSettings/terminalsettings.h b/src/cascadia/TerminalSettings/terminalsettings.h index 8979e546c0a..c701631da8a 100644 --- a/src/cascadia/TerminalSettings/terminalsettings.h +++ b/src/cascadia/TerminalSettings/terminalsettings.h @@ -47,6 +47,8 @@ namespace winrt::Microsoft::Terminal::Settings::implementation void CursorHeight(uint32_t value); hstring WordDelimiters(); void WordDelimiters(hstring const& value); + bool CopyOnSelect(); + void CopyOnSelect(bool value); // ------------------------ End of Core Settings ----------------------- bool UseAcrylic(); @@ -116,6 +118,7 @@ namespace winrt::Microsoft::Terminal::Settings::implementation winrt::Windows::UI::Xaml::Media::Stretch _backgroundImageStretchMode; winrt::Windows::UI::Xaml::HorizontalAlignment _backgroundImageHorizontalAlignment; winrt::Windows::UI::Xaml::VerticalAlignment _backgroundImageVerticalAlignment; + bool _copyOnSelect; hstring _commandline; hstring _startingDir; hstring _startingTitle; diff --git a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h index 3024f4a34d7..eb5e4011a7c 100644 --- a/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h +++ b/src/cascadia/UnitTests_TerminalCore/MockTermSettings.h @@ -32,6 +32,7 @@ namespace TerminalCoreUnitTests CursorStyle CursorShape() const noexcept { return CursorStyle::Vintage; } uint32_t CursorHeight() { return 42UL; } winrt::hstring WordDelimiters() { return winrt::to_hstring(DEFAULT_WORD_DELIMITERS.c_str()); } + bool CopyOnSelect() { return _copyOnSelect; } // other implemented methods uint32_t GetColorTableEntry(int32_t) const { return 123; } @@ -47,6 +48,7 @@ namespace TerminalCoreUnitTests void CursorShape(CursorStyle const&) noexcept {} void CursorHeight(uint32_t) {} void WordDelimiters(winrt::hstring) {} + void CopyOnSelect(bool copyOnSelect) { _copyOnSelect = copyOnSelect; } // other unimplemented methods void SetColorTableEntry(int32_t /* index */, uint32_t /* value */) {} @@ -55,5 +57,6 @@ namespace TerminalCoreUnitTests int32_t _historySize; int32_t _initialRows; int32_t _initialCols; + bool _copyOnSelect{ false }; }; } diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index 01465aa3d3a..530f7b29d04 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -504,5 +504,63 @@ namespace TerminalCoreUnitTests selection = term.GetViewport().ConvertToOrigin(selectionRects.at(1)).ToInclusive(); VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 0, 11, 99, 11 })); } + + TEST_METHOD(CopyOnSelect) + { + Terminal term; + DummyRenderTarget emptyRT; + term.Create({ 100, 100 }, 0, emptyRT); + + // set copyOnSelect for terminal + auto settings = winrt::make(0, 100, 100); + settings.CopyOnSelect(true); + term.UpdateSettings(settings); + + // Simulate click at (x,y) = (5,10) + term.SetSelectionAnchor({ 5, 10 }); + + // Simulate move to (x,y) = (5,10) + // (So, no movement) + term.SetEndSelectionPosition({ 5, 10 }); + + // Case 1: single cell selection not allowed + { + // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(0)); + + // single cell selection should not be allowed + // thus, selection is NOT active + VERIFY_IS_FALSE(term.IsSelectionActive()); + } + + // Case 2: move off of single cell + term.SetEndSelectionPosition({ 6, 10 }); + { // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 5, 10, 6, 10 })); + VERIFY_IS_TRUE(term.IsSelectionActive()); + } + + // Case 3: move back onto single cell (now allowed) + term.SetEndSelectionPosition({ 5, 10 }); + { // Simulate renderer calling TriggerSelection and acquiring selection area + auto selectionRects = term.GetSelectionRects(); + + // Validate selection area + VERIFY_ARE_EQUAL(selectionRects.size(), static_cast(1)); + auto selection = term.GetViewport().ConvertToOrigin(selectionRects.at(0)).ToInclusive(); + VERIFY_ARE_EQUAL(selection, SMALL_RECT({ 5, 10, 5, 10 })); + + // single cell selection should now be allowed + VERIFY_IS_TRUE(term.IsSelectionActive()); + } + } }; } From 0c454f53e92ed3b1fe2bd73c8da75966912960c9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 20 Aug 2019 13:16:06 -0500 Subject: [PATCH 051/154] TURNS OUT CASE SENSITIVITY IS IMPORTANT (#2481) * TURNS OUT CASE SENSITIVITY IS IMPORTANT * Add a note that this is important --- .../LocalTests_TerminalApp/TerminalApp.LocalTests.manifest | 1 + src/cascadia/WindowsTerminal/WindowsTerminal.manifest | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest index ef9516047af..a447bc8fdc2 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.manifest @@ -9,6 +9,7 @@ + diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.manifest b/src/cascadia/WindowsTerminal/WindowsTerminal.manifest index 674a264e7c2..a447bc8fdc2 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.manifest +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.manifest @@ -9,7 +9,8 @@ - + + From 09d79cb422e14f9f5bcb3d5cc73ad0f18731ef06 Mon Sep 17 00:00:00 2001 From: Richard Szalay Date: Wed, 21 Aug 2019 08:38:45 +1000 Subject: [PATCH 052/154] Prevent splitting panes into 0 width/height #2401 (#2450) Fixes a crash that can occur when splitting pane that was so small that the target panes would have a width/height of 0, causing DxRenderer to fail when creating the device resources. This PR prevents both the call to `App::AddHorizontal/VerticalSplit` and the creation of the `TermControl` if the split would fail. Closes #2401 ## Details `App::_SplitPane` calls `focusedTab->CanAddHorizontalSplit/CanAddHorizontalSplit` before it initializes the `TermControl` to avoid having to deal with the cleanup. If a split cannot occur, it will simply return. **Question: Should we beep or something here?** It then follows the same naming/flow style as the split operation, so: `Tab::CanAddHorizontalSplit -> Pane::CanSplitHorizontal ->Pane::_CanSplit`. The public pane methods will handle leaf/child the same as the current Split methods. `_CanSplit` reuses existing logic like `_root.GetActualWidth/Height`, `Pane::_GetMinSize`, and the `Half` constant. ## Validation Steps Performed 1. Open a new tab 2. Attempt to split horizontally/vertically more than 6-8 times Success: Pane will will eventually stop splitting rather than crashing the process. --- src/cascadia/TerminalApp/App.cpp | 12 ++++- src/cascadia/TerminalApp/Pane.cpp | 84 +++++++++++++++++++++++++++++++ src/cascadia/TerminalApp/Pane.h | 4 ++ src/cascadia/TerminalApp/Tab.cpp | 18 +++++++ src/cascadia/TerminalApp/Tab.h | 2 + 5 files changed, 118 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index accaf8a7f4f..8dece58e463 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1494,11 +1494,19 @@ namespace winrt::TerminalApp::implementation const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings); - TermControl newControl{ controlSettings, controlConnection }; - const int focusedTabIndex = _GetFocusedTabIndex(); auto focusedTab = _tabs[focusedTabIndex]; + const auto canSplit = splitType == Pane::SplitState::Horizontal ? focusedTab->CanAddHorizontalSplit() : + focusedTab->CanAddVerticalSplit(); + + if (!canSplit) + { + return; + } + + TermControl newControl{ controlSettings, controlConnection }; + // Hookup our event handlers to the new terminal _RegisterTerminalEvents(newControl, focusedTab); diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 5262bf45148..10e3bd09d6a 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -777,6 +777,31 @@ void Pane::_ApplySplitDefinitions() } } +// Method Description: +// - Determines whether the pane can be split vertically +// Arguments: +// - splitType: what type of split we want to create. +// Return Value: +// - True if the pane can be split vertically. False otherwise. +bool Pane::CanSplitVertical() +{ + if (!_IsLeaf()) + { + if (_firstChild->_HasFocusedChild()) + { + return _firstChild->CanSplitVertical(); + } + else if (_secondChild->_HasFocusedChild()) + { + return _secondChild->CanSplitVertical(); + } + + return false; + } + + return _CanSplit(SplitState::Vertical); +} + // Method Description: // - Vertically split the focused pane in our tree of panes, and place the given // TermControl into the newly created pane. If we're the focused pane, then @@ -806,6 +831,31 @@ void Pane::SplitVertical(const GUID& profile, const TermControl& control) _Split(SplitState::Vertical, profile, control); } +// Method Description: +// - Determines whether the pane can be split horizontally +// Arguments: +// - splitType: what type of split we want to create. +// Return Value: +// - True if the pane can be split horizontally. False otherwise. +bool Pane::CanSplitHorizontal() +{ + if (!_IsLeaf()) + { + if (_firstChild->_HasFocusedChild()) + { + return _firstChild->CanSplitHorizontal(); + } + else if (_secondChild->_HasFocusedChild()) + { + return _secondChild->CanSplitHorizontal(); + } + + return false; + } + + return _CanSplit(SplitState::Horizontal); +} + // Method Description: // - Horizontally split the focused pane in our tree of panes, and place the given // TermControl into the newly created pane. If we're the focused pane, then @@ -834,6 +884,40 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control) _Split(SplitState::Horizontal, profile, control); } +// Method Description: +// - Determines whether the pane can be split. +// Arguments: +// - splitType: what type of split we want to create. +// Return Value: +// - True if the pane can be split. False otherwise. +bool Pane::_CanSplit(SplitState splitType) +{ + const bool changeWidth = _splitState == SplitState::Vertical; + + const Size actualSize{ gsl::narrow_cast(_root.ActualWidth()), + gsl::narrow_cast(_root.ActualHeight()) }; + + const Size minSize = _GetMinSize(); + + if (splitType == SplitState::Vertical) + { + const auto widthMinusSeparator = actualSize.Width - PaneSeparatorSize; + const auto newWidth = widthMinusSeparator * Half; + + return newWidth > minSize.Width; + } + + if (splitType == SplitState::Horizontal) + { + const auto heightMinusSeparator = actualSize.Height - PaneSeparatorSize; + const auto newHeight = heightMinusSeparator * Half; + + return newHeight > minSize.Height; + } + + return false; +} + // Method Description: // - Does the bulk of the work of creating a new split. Initializes our UI, // creates a new Pane to host the control, registers event handlers. diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 2fb4a2e499f..31979b6d090 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -49,7 +49,10 @@ class Pane : public std::enable_shared_from_this bool ResizePane(const winrt::TerminalApp::Direction& direction); bool NavigateFocus(const winrt::TerminalApp::Direction& direction); + bool CanSplitHorizontal(); void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + + bool CanSplitVertical(); void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void Close(); @@ -79,6 +82,7 @@ class Pane : public std::enable_shared_from_this bool _HasFocusedChild() const noexcept; void _SetupChildCloseHandlers(); + bool _CanSplit(SplitState splitType); void _Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void _CreateRowColDefinitions(const winrt::Windows::Foundation::Size& rootSize); void _CreateSplitContent(); diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index f8ac1199c36..4870d227f4b 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -203,6 +203,15 @@ void Tab::Scroll(const int delta) }); } +// Method Description: +// - Determines whether the focused pane has sufficient space to be split vertically. +// Return Value: +// - True if the focused pane can be split horizontally. False otherwise. +bool Tab::CanAddVerticalSplit() +{ + return _rootPane->CanSplitVertical(); +} + // Method Description: // - Vertically split the focused pane in our tree of panes, and place the // given TermControl into the newly created pane. @@ -216,6 +225,15 @@ void Tab::AddVerticalSplit(const GUID& profile, TermControl& control) _rootPane->SplitVertical(profile, control); } +// Method Description: +// - Determines whether the focused pane has sufficient space to be split horizontally. +// Return Value: +// - True if the focused pane can be split horizontally. False otherwise. +bool Tab::CanAddHorizontalSplit() +{ + return _rootPane->CanSplitHorizontal(); +} + // Method Description: // - Horizontally split the focused pane in our tree of panes, and place the // given TermControl into the newly created pane. diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index 4de1ca87a26..99c782201e3 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -19,7 +19,9 @@ class Tab void SetFocused(const bool focused); void Scroll(const int delta); + bool CanAddVerticalSplit(); void AddVerticalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + bool CanAddHorizontalSplit(); void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void UpdateFocus(); From f9752148d02b024e5aa96be88380d581d87c29aa Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 20 Aug 2019 15:39:28 -0700 Subject: [PATCH 053/154] Bugfix: Copy data should persist after Windows Terminal Closes (#2486) --- src/cascadia/TerminalApp/App.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 8dece58e463..af390a582fb 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1554,6 +1554,7 @@ namespace winrt::TerminalApp::implementation } Clipboard::SetContent(dataPack); + Clipboard::Flush(); }); } From 8096d7cf2f96a9898a664ec7fc4933dc06e8a212 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 20 Aug 2019 17:46:42 -0500 Subject: [PATCH 054/154] Don't overwrite the settings file (#2475) This is more trouble than it's worth. We had code before to re-serialize settings when they changed, to try and gracefully migrate settings from old schemas to new ones. This is good in theory, but with #754 coming soon, this is going to become a minefield. In the future we'll just always be providing a base schema that's reasonable, so this won't matter so much. Keys that users have that aren't understood will just be ignored, and that's _fine_. --- src/cascadia/TerminalApp/App.cpp | 12 ++++------- src/cascadia/TerminalApp/App.h | 2 +- src/cascadia/TerminalApp/CascadiaSettings.h | 2 +- .../CascadiaSettingsSerialization.cpp | 21 +------------------ 4 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index af390a582fb..6c88340b844 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -680,19 +680,15 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Attempt to load the settings. If we fail for any reason, returns an error. - // Arguments: - // - saveOnLoad: If true, after loading the settings, we should re-write - // them to the file, to make sure the schema is updated. See - // `CascadiaSettings::LoadAll` for details. // Return Value: // - S_OK if we successfully parsed the settings, otherwise an appropriate HRESULT. - [[nodiscard]] HRESULT App::_TryLoadSettings(const bool saveOnLoad) noexcept + [[nodiscard]] HRESULT App::_TryLoadSettings() noexcept { HRESULT hr = E_FAIL; try { - auto newSettings = CascadiaSettings::LoadAll(saveOnLoad); + auto newSettings = CascadiaSettings::LoadAll(); _settings = std::move(newSettings); const auto& warnings = _settings->GetWarnings(); hr = warnings.size() == 0 ? S_OK : S_FALSE; @@ -733,7 +729,7 @@ namespace winrt::TerminalApp::implementation // we should display the loading error. // * We can't display the error now, because we might not have a // UI yet. We'll display the error in _OnLoaded. - _settingsLoadedResult = _TryLoadSettings(true); + _settingsLoadedResult = _TryLoadSettings(); if (FAILED(_settingsLoadedResult)) { @@ -813,7 +809,7 @@ namespace winrt::TerminalApp::implementation // - don't change the settings (and don't actually apply the new settings) // - don't persist them. // - display a loading error - _settingsLoadedResult = _TryLoadSettings(false); + _settingsLoadedResult = _TryLoadSettings(); if (FAILED(_settingsLoadedResult)) { diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 82ad67954e3..47350c76056 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -85,7 +85,7 @@ namespace winrt::TerminalApp::implementation void _ShowLoadWarningsDialog(); void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); - [[nodiscard]] HRESULT _TryLoadSettings(const bool saveOnLoad) noexcept; + [[nodiscard]] HRESULT _TryLoadSettings() noexcept; void _LoadSettings(); void _OpenSettings(); diff --git a/src/cascadia/TerminalApp/CascadiaSettings.h b/src/cascadia/TerminalApp/CascadiaSettings.h index 6334705bdc4..703cdaa0990 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.h +++ b/src/cascadia/TerminalApp/CascadiaSettings.h @@ -40,7 +40,7 @@ class TerminalApp::CascadiaSettings final CascadiaSettings(); ~CascadiaSettings(); - static std::unique_ptr LoadAll(const bool saveOnLoad = true); + static std::unique_ptr LoadAll(); void SaveAll() const; winrt::Microsoft::Terminal::Settings::TerminalSettings MakeSettings(std::optional profileGuid) const; diff --git a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp index dfaf68aa05c..d8b35a0a675 100644 --- a/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettingsSerialization.cpp @@ -30,12 +30,9 @@ static constexpr std::string_view Utf8Bom{ u8"\uFEFF" }; // it will load the settings from our packaged localappdata. If we're // running as an unpackaged application, it will read it from the path // we've set under localappdata. -// Arguments: -// - saveOnLoad: If true, we'll write the settings back out after we load them, -// to make sure the schema is updated. // Return Value: // - a unique_ptr containing a new CascadiaSettings object. -std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoad) +std::unique_ptr CascadiaSettings::LoadAll() { std::unique_ptr resultPtr; std::optional fileData = _ReadSettings(); @@ -70,22 +67,6 @@ std::unique_ptr CascadiaSettings::LoadAll(const bool saveOnLoa // If this throws, the app will catch it and use the default settings (temporarily) resultPtr->_ValidateSettings(); - - const bool foundWarnings = resultPtr->_warnings.size() > 0; - - // Don't save on load if there were warnings - we tried to gracefully - // handle them. - if (saveOnLoad && !foundWarnings) - { - // Logically compare the json we've parsed from the file to what - // we'd serialize at runtime. If the values are different, then - // write the updated schema back out. - const Json::Value reserialized = resultPtr->ToJson(); - if (reserialized != root) - { - resultPtr->SaveAll(); - } - } } else { From 28b767d00b261bb63128777c8a9b2661fb5686d8 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Tue, 20 Aug 2019 16:14:26 -0700 Subject: [PATCH 055/154] dx: Render all gridlines (and, bonus: the box cursor) properly (#2491) Since we're rendering with antialiasing enabled, we need to make sure we're stroking actual pixels; to do that, we need to adjust all of our coordinates by the StrokeWidth / 2. We're always using a stroke width of 1, so that means 0.5. While I was here, I took the opportunity to fix the color of the grid lines. Fixes #543. --- src/renderer/dx/DxRenderer.cpp | 42 ++++++++++++++++++++++++---------- src/renderer/dx/DxRenderer.hpp | 1 + 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 9d372090724..33f8790470b 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -295,6 +295,17 @@ DxEngine::~DxEngine() RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), &_d2dBrushForeground)); + const D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties{ + D2D1_CAP_STYLE_SQUARE, // startCap + D2D1_CAP_STYLE_SQUARE, // endCap + D2D1_CAP_STYLE_SQUARE, // dashCap + D2D1_LINE_JOIN_MITER, // lineJoin + 0.f, // miterLimit + D2D1_DASH_STYLE_SOLID, // dashStyle + 0.f, // dashOffset + }; + RETURN_IF_FAILED(_d2dFactory->CreateStrokeStyle(&strokeStyleProperties, nullptr, 0, &_strokeStyle)); + // If in composition mode, apply scaling factor matrix if (_chainMode == SwapChainMode::ForComposition) { @@ -936,7 +947,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept const auto existingColor = _d2dBrushForeground->GetColor(); const auto restoreBrushOnExit = wil::scope_exit([&] { _d2dBrushForeground->SetColor(existingColor); }); - _d2dBrushForeground->SetColor(D2D1::ColorF(color)); + _d2dBrushForeground->SetColor(_ColorFFromColorRef(color)); const auto font = _GetFontSize(); D2D_POINT_2F target; @@ -948,14 +959,15 @@ void DxEngine::_InvalidOr(RECT rc) noexcept for (size_t i = 0; i < cchLine; i++) { - start = target; + // 0.5 pixel offset for crisp lines + start = { target.x + 0.5f, target.y + 0.5f }; if (lines & GridLines::Top) { end = start; end.x += font.X; - _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get()); + _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get(), 1.0f, _strokeStyle.Get()); } if (lines & GridLines::Left) @@ -963,7 +975,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept end = start; end.y += font.Y; - _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get()); + _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get(), 1.0f, _strokeStyle.Get()); } // NOTE: Watch out for inclusive/exclusive rectangles here. @@ -973,26 +985,25 @@ void DxEngine::_InvalidOr(RECT rc) noexcept // The bottom left corner inclusive is at 0,15 which is Y (0) + Font Height (16) - 1 = 15. // The top right corner inclusive is at 7,0 which is X (0) + Font Height (8) - 1 = 7. - start = target; - start.y += font.Y - 1; + // 0.5 pixel offset for crisp lines; -0.5 on the Y to fit _in_ the cell, not outside it. + start = { target.x + 0.5f, target.y + font.Y - 0.5f }; if (lines & GridLines::Bottom) { end = start; - end.x += font.X; + end.x += font.X - 1.f; - _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get()); + _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get(), 1.0f, _strokeStyle.Get()); } - start = target; - start.x += font.X - 1; + start = { target.x + font.X - 0.5f, target.y + 0.5f }; if (lines & GridLines::Right) { end = start; - end.y += font.Y; + end.y += font.Y - 1.f; - _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get()); + _d2dRenderTarget->DrawLine(start, end, _d2dBrushForeground.Get(), 1.0f, _strokeStyle.Get()); } // Move to the next character in this run. @@ -1125,6 +1136,13 @@ enum class CursorPaintType } case CursorPaintType::Outline: { + // DrawRectangle in straddles physical pixels in an attempt to draw a line + // between them. To avoid this, bump the rectangle around by half the stroke width. + rect.top += 0.5f; + rect.left += 0.5f; + rect.bottom -= 0.5f; + rect.right -= 0.5f; + _d2dRenderTarget->DrawRectangle(rect, brush.Get()); break; } diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index c6127b8ed7d..bc25588d3f1 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -151,6 +151,7 @@ namespace Microsoft::Console::Render ::Microsoft::WRL::ComPtr _dwriteFontFace; ::Microsoft::WRL::ComPtr _dwriteTextAnalyzer; ::Microsoft::WRL::ComPtr _customRenderer; + ::Microsoft::WRL::ComPtr _strokeStyle; // Device-Dependent Resources bool _haveDeviceResources; From 667c0286c1eaee8935f441d24890196db32eb3a7 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 20 Aug 2019 16:32:44 -0700 Subject: [PATCH 056/154] Accessibility: Refactor Providers (#2414) Refactors the accessibility providers (ScreenInfoUiaProvider and UiaTextRange) into a better separated model between ConHost and Windows Terminal. ScreenInfoUiaProviderBase and UiaTextRangeBase are introduced. ConHost and Windows Terminal implement their own versions of ScreenInfoUiaProvider and UiaTextRange that inherit from their respective base classes. WindowsTerminal's ScreenInfoUiaProvider --> TermControlUiaProvider --- .../TermControlAutomationPeer.cpp | 4 +- .../TermControlAutomationPeer.h | 4 +- .../TermControlUiaProvider.cpp | 118 ++++ .../TermControlUiaProvider.hpp | 63 ++ .../TerminalControl/TerminalControl.vcxproj | 4 + .../TerminalControl.vcxproj.filters | 6 +- src/cascadia/TerminalControl/UiaTextRange.cpp | 197 ++++++ src/cascadia/TerminalControl/UiaTextRange.hpp | 83 +++ .../TerminalControl/XamlUiaTextRange.cpp | 2 +- .../TerminalControl/XamlUiaTextRange.h | 2 +- src/cascadia/TerminalCore/Terminal.hpp | 14 - .../TerminalCore/terminalrenderdata.cpp | 16 - .../WindowsTerminal/WindowUiaProvider.cpp | 26 +- src/host/renderData.cpp | 57 +- src/host/renderData.hpp | 13 - src/host/tracing.cpp | 4 +- src/interactivity/win32/lib/win32.LIB.vcxproj | 4 + .../win32/lib/win32.LIB.vcxproj.filters | 12 + .../win32/screenInfoUiaProvider.cpp | 132 ++++ .../win32/screenInfoUiaProvider.hpp | 68 ++ src/interactivity/win32/uiaTextRange.cpp | 281 ++++++++ src/interactivity/win32/uiaTextRange.hpp | 90 +++ .../UiaTextRangeTests.cpp | 4 +- src/interactivity/win32/windowUiaProvider.cpp | 6 +- src/interactivity/win32/windowUiaProvider.hpp | 2 +- src/types/IUiaData.h | 13 - ...ider.cpp => ScreenInfoUiaProviderBase.cpp} | 222 ++----- ...Provider.h => ScreenInfoUiaProviderBase.h} | 54 +- ...{UiaTextRange.cpp => UiaTextRangeBase.cpp} | 603 ++++++------------ ...{UiaTextRange.hpp => UiaTextRangeBase.hpp} | 82 +-- src/types/WindowUiaProviderBase.cpp | 1 - src/types/WindowUiaProviderBase.hpp | 1 - src/types/lib/types.vcxproj | 10 +- src/types/lib/types.vcxproj.filters | 11 +- 34 files changed, 1418 insertions(+), 791 deletions(-) create mode 100644 src/cascadia/TerminalControl/TermControlUiaProvider.cpp create mode 100644 src/cascadia/TerminalControl/TermControlUiaProvider.hpp create mode 100644 src/cascadia/TerminalControl/UiaTextRange.cpp create mode 100644 src/cascadia/TerminalControl/UiaTextRange.hpp create mode 100644 src/interactivity/win32/screenInfoUiaProvider.cpp create mode 100644 src/interactivity/win32/screenInfoUiaProvider.hpp create mode 100644 src/interactivity/win32/uiaTextRange.cpp create mode 100644 src/interactivity/win32/uiaTextRange.hpp rename src/types/{ScreenInfoUiaProvider.cpp => ScreenInfoUiaProviderBase.cpp} (66%) rename src/types/{ScreenInfoUiaProvider.h => ScreenInfoUiaProviderBase.h} (72%) rename src/types/{UiaTextRange.cpp => UiaTextRangeBase.cpp} (75%) rename src/types/{UiaTextRange.hpp => UiaTextRangeBase.hpp} (85%) diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index 565770facca..dcd677752b9 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner) : TermControlAutomationPeerT(owner), // pass owner to FrameworkElementAutomationPeer - _uiaProvider{ owner.GetUiaData(), nullptr, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; + _uiaProvider{ owner.GetUiaData(), std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; winrt::hstring TermControlAutomationPeer::GetClassNameCore() const { @@ -135,7 +135,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation winrt::com_array TermControlAutomationPeer::WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges) { // transfer ownership of UiaTextRanges to this new vector - auto providers = SafeArrayToOwningVector<::Microsoft::Console::Types::UiaTextRange>(textRanges); + auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges); int count = providers.size(); std::vector vec; diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.h b/src/cascadia/TerminalControl/TermControlAutomationPeer.h index e5db0b405da..e25db477a4d 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.h +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.h @@ -28,8 +28,8 @@ Author(s): #include "TermControlAutomationPeer.g.h" #include #include "../../renderer/inc/IRenderData.hpp" -#include "../types/ScreenInfoUiaProvider.h" #include "../types/WindowUiaProviderBase.hpp" +#include "TermControlUiaProvider.hpp" namespace winrt::Microsoft::Terminal::TerminalControl::implementation { @@ -56,7 +56,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation RECT GetBoundingRectWrapped(); private: - ::Microsoft::Console::Types::ScreenInfoUiaProvider _uiaProvider; + ::Microsoft::Terminal::TermControlUiaProvider _uiaProvider; winrt::com_array WrapArrayOfTextRangeProviders(SAFEARRAY* textRanges); }; diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.cpp b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp new file mode 100644 index 00000000000..eaac4ad4f7d --- /dev/null +++ b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "TermControlUiaProvider.hpp" + +using namespace Microsoft::Terminal; +using namespace Microsoft::Console::Types; + +TermControlUiaProvider::TermControlUiaProvider(_In_ IUiaData* pData, + _In_ std::function GetBoundingRect) : + _getBoundingRect(GetBoundingRect), + ScreenInfoUiaProviderBase(THROW_HR_IF_NULL(E_INVALIDARG, pData)) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); +} + +IFACEMETHODIMP TermControlUiaProvider::Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + /*ApiMsgNavigate apiMsg; + apiMsg.Direction = direction; + Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg);*/ + *ppProvider = nullptr; + + if (direction == NavigateDirection_Parent) + { + try + { + // TODO GitHub #2102: UIA Tree Navigation + //_pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); + } + catch (...) + { + *ppProvider = nullptr; + return wil::ResultFromCaughtException(); + } + RETURN_IF_NULL_ALLOC(*ppProvider); + } + + // For the other directions the default of nullptr is correct + return S_OK; +} + +IFACEMETHODIMP TermControlUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); + + RECT rc = _getBoundingRect(); + + pRect->left = rc.left; + pRect->top = rc.top; + pRect->width = rc.right - rc.left; + pRect->height = rc.bottom - rc.top; + + return S_OK; +} + +IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); + try + { + // TODO GitHub #2102: UIA Tree Navigation - the special fragments that knows about all of its descendants is called a fragment root + //_pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); + *ppProvider = nullptr; + } + catch (...) + { + *ppProvider = nullptr; + return wil::ResultFromCaughtException(); + } + RETURN_IF_NULL_ALLOC(*ppProvider); + return S_OK; +} + +std::deque TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) +{ + std::deque result; + + auto ranges = UiaTextRange::GetSelectionRanges(_pData, pProvider); + while (!ranges.empty()) + { + result.emplace_back(ranges.back()); + ranges.pop_back(); + } + + return result; +} + +UiaTextRangeBase* TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider) +{ + return UiaTextRange::Create(_pData, pProvider); +} + +UiaTextRangeBase* TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) +{ + return UiaTextRange::Create(_pData, pProvider, cursor); +} + +UiaTextRangeBase* TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) +{ + return UiaTextRange::Create(_pData, pProvider, start, end, degenerate); +} + +UiaTextRangeBase* TermControlUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) +{ + return UiaTextRange::Create(_pData, pProvider, point); +} diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.hpp b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp new file mode 100644 index 00000000000..e536fb93960 --- /dev/null +++ b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp @@ -0,0 +1,63 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- TermControlUiaProvider.hpp + +Abstract: +- This module provides UI Automation access to the screen buffer to + support both automation tests and accessibility (screen reading) + applications. +- ConHost and Windows Terminal must use IRenderData to have access to the proper information +- Based on examples, sample code, and guidance from + https://msdn.microsoft.com/en-us/library/windows/desktop/ee671596(v=vs.85).aspx + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "..\types\ScreenInfoUiaProviderBase.h" +#include "..\types\UiaTextRangeBase.hpp" +#include "UiaTextRange.hpp" + +namespace Microsoft::Terminal +{ + class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase + { + public: + TermControlUiaProvider(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ std::function GetBoundingRect); + + // IRawElementProviderFragment methods + IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override; + IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override; + + protected: + std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) override; + + // degenerate range + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider) override; + + // degenerate range at cursor position + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) override; + + // specific endpoint range + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) override; + + // range from a UiaPoint + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) override; + + private: + std::function _getBoundingRect; + }; +} diff --git a/src/cascadia/TerminalControl/TerminalControl.vcxproj b/src/cascadia/TerminalControl/TerminalControl.vcxproj index c50e9c096ef..a67e2ef476c 100644 --- a/src/cascadia/TerminalControl/TerminalControl.vcxproj +++ b/src/cascadia/TerminalControl/TerminalControl.vcxproj @@ -17,12 +17,14 @@ + TermControl.idl TermControlAutomationPeer.idl + @@ -30,6 +32,7 @@ Create
+ TermControl.idl @@ -37,6 +40,7 @@ TermControlAutomationPeer.idl + diff --git a/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters b/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters index d3862cb0fea..888488b3f7a 100644 --- a/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters +++ b/src/cascadia/TerminalControl/TerminalControl.vcxproj.filters @@ -15,12 +15,16 @@ + + - + + + diff --git a/src/cascadia/TerminalControl/UiaTextRange.cpp b/src/cascadia/TerminalControl/UiaTextRange.cpp new file mode 100644 index 00000000000..5eb9c8ac121 --- /dev/null +++ b/src/cascadia/TerminalControl/UiaTextRange.cpp @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "UiaTextRange.hpp" +#include "TermControlUiaProvider.hpp" + +using namespace Microsoft::Terminal; +using namespace Microsoft::Console::Types; + +std::deque UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* pProvider) +{ + std::deque ranges; + + // get the selection rects + const auto rectangles = pData->GetSelectionRects(); + + // create a range for each row + for (const auto& rect : rectangles) + { + ScreenInfoRow currentRow = rect.Top(); + Endpoint start = _screenInfoRowToEndpoint(pData, currentRow) + rect.Left(); + Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive(); + UiaTextRange* range = UiaTextRange::Create(pData, + pProvider, + start, + end, + false); + if (range == nullptr) + { + // something went wrong, clean up and throw + while (!ranges.empty()) + { + UiaTextRangeBase* temp = ranges[0]; + ranges.pop_front(); + } + THROW_HR(E_INVALIDARG); + } + else + { + ranges.push_back(range); + } + } + return ranges; +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider) +{ + try + { + return new UiaTextRange(pData, pProvider); + } + catch (...) + { + return nullptr; + } +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) +{ + try + { + return new UiaTextRange(pData, pProvider, cursor); + } + catch (...) + { + return nullptr; + } +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) +{ + try + { + return new UiaTextRange(pData, + pProvider, + start, + end, + degenerate); + } + catch (...) + { + return nullptr; + } +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) +{ + try + { + return new UiaTextRange(pData, pProvider, point); + } + catch (...) + { + return nullptr; + } +} + +// degenerate range constructor. +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) : + UiaTextRangeBase(pData, pProvider) +{ +} + +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) : + UiaTextRangeBase(pData, pProvider, cursor) +{ +} + +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) : + UiaTextRangeBase(pData, pProvider, start, end, degenerate) +{ +} + +// returns a degenerate text range of the start of the row closest to the y value of point +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) : + UiaTextRangeBase(pData, pProvider) +{ + Initialize(point); +} + +IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +{ + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + try + { + *ppRetVal = new UiaTextRange(*this); + } + catch (...) + { + *ppRetVal = nullptr; + return wil::ResultFromCaughtException(); + } + if (*ppRetVal == nullptr) + { + return E_OUTOFMEMORY; + } + +#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS) + OutputDebugString(L"Clone\n"); + std::wstringstream ss; + ss << _id << L" cloned to " << (static_cast(*ppRetVal))->_id; + std::wstring str = ss.str(); + OutputDebugString(str.c_str()); + OutputDebugString(L"\n"); +#endif + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + // tracing + /*ApiMsgClone apiMsg; + apiMsg.CloneId = static_cast(*ppRetVal)->GetId(); + Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/ + + return S_OK; +} + +IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +{ + // TODO GitHub #605: Search functionality + return E_NOTIMPL; +} + +void UiaTextRange::_ChangeViewport(const SMALL_RECT /*NewWindow*/) +{ + // TODO GitHub #2361: Update viewport when calling UiaTextRangeBase::ScrollIntoView() +} + +void UiaTextRange::_TranslatePointToScreen(LPPOINT /*clientPoint*/) const +{ + // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF CLIENTTOSCREEN() +} + +void UiaTextRange::_TranslatePointFromScreen(LPPOINT /*screenPoint*/) const +{ + // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT() +} diff --git a/src/cascadia/TerminalControl/UiaTextRange.hpp b/src/cascadia/TerminalControl/UiaTextRange.hpp new file mode 100644 index 00000000000..7e561e3f971 --- /dev/null +++ b/src/cascadia/TerminalControl/UiaTextRange.hpp @@ -0,0 +1,83 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- UiaTextRange.hpp + +Abstract: +- This module provides UI Automation access to the text of the console + window to support both automation tests and accessibility (screen + reading) applications. + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "..\types\UiaTextRangeBase.hpp" + +namespace Microsoft::Terminal +{ + class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase + { + public: + static std::deque GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* pProvider); + + // degenerate range + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider); + + // degenerate range at cursor position + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor); + + // specific endpoint range + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate); + + // range from a UiaPoint + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point); + + IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + IFACEMETHODIMP FindText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + + protected: + void _ChangeViewport(const SMALL_RECT NewWindow) override; + void _TranslatePointToScreen(LPPOINT clientPoint) const override; + void _TranslatePointFromScreen(LPPOINT screenPoint) const override; + + private: + // degenerate range + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider); + + // degenerate range at cursor position + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor); + + // specific endpoint range + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate); + + // range from a UiaPoint + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point); + }; +} diff --git a/src/cascadia/TerminalControl/XamlUiaTextRange.cpp b/src/cascadia/TerminalControl/XamlUiaTextRange.cpp index 4640019cb83..4ca5c524590 100644 --- a/src/cascadia/TerminalControl/XamlUiaTextRange.cpp +++ b/src/cascadia/TerminalControl/XamlUiaTextRange.cpp @@ -3,7 +3,7 @@ #include "pch.h" #include "XamlUiaTextRange.h" -#include "../types/UiaTextRange.hpp" +#include "UiaTextRange.hpp" namespace UIA { diff --git a/src/cascadia/TerminalControl/XamlUiaTextRange.h b/src/cascadia/TerminalControl/XamlUiaTextRange.h index 6560f997bbb..57b4ac605c5 100644 --- a/src/cascadia/TerminalControl/XamlUiaTextRange.h +++ b/src/cascadia/TerminalControl/XamlUiaTextRange.h @@ -22,7 +22,7 @@ Author(s): #include "TermControlAutomationPeer.h" #include -#include "../types/UiaTextRange.hpp" +#include "UiaTextRange.hpp" namespace winrt::Microsoft::Terminal::TerminalControl::implementation { diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 876d21dacfc..ba2f2bdc4cf 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -119,20 +119,6 @@ class Microsoft::Terminal::Core::Terminal final : const bool IsSelectionActive() const noexcept; void ClearSelection() override; void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; - - // TODO GitHub #605: Search functionality - // For now, just adding it here to make UiaTextRange easier to create (Accessibility) - // We should actually abstract this out better once Windows Terminal has Search - HRESULT SearchForText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, - unsigned int _start, - unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, - std::function Clone) override; - const std::wstring GetConsoleTitle() const noexcept override; #pragma endregion diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index 36c75b560de..d34c557b84f 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -123,22 +123,6 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd) SetEndSelectionPosition(coordEnd); } -// TODO GitHub #605: Search functionality -// For now, just adding it here to make UiaTextRange easier to create (Accessibility) -// We should actually abstract this out better once Windows Terminal has Search -HRESULT Terminal::SearchForText(_In_ BSTR /*text*/, - _In_ BOOL /*searchBackward*/, - _In_ BOOL /*ignoreCase*/, - _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/, - unsigned int /*_start*/, - unsigned int /*_end*/, - std::function /*_coordToEndpoint*/, - std::function /*_endpointToCoord*/, - std::function /*Clone*/) -{ - return E_NOTIMPL; -} - const std::wstring Terminal::GetConsoleTitle() const noexcept { return _title; diff --git a/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp index d25f3b86b99..d8f51c51414 100644 --- a/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp +++ b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp @@ -3,7 +3,6 @@ #include "pch.h" #include "WindowUiaProvider.hpp" -#include "../types/ScreenInfoUiaProvider.h" #include "../host/renderData.hpp" @@ -19,12 +18,13 @@ WindowUiaProvider::~WindowUiaProvider() WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWindow* baseWindow) { WindowUiaProvider* pWindowProvider = nullptr; - Microsoft::Console::Types::ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; + //Microsoft::Terminal::TermControlUiaProvider* pScreenInfoProvider = nullptr; try { pWindowProvider = new WindowUiaProvider(baseWindow); - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model /*Globals& g = ServiceLocator::LocateGlobals(); CONSOLE_INFORMATION& gci = g.getConsoleInformation(); Microsoft::Console::Render::IRenderData* renderData = &gci.renderData; @@ -45,11 +45,6 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind pWindowProvider->Release(); } - if (nullptr != pScreenInfoProvider) - { - pScreenInfoProvider->Release(); - } - LOG_CAUGHT_EXCEPTION(); return nullptr; @@ -60,7 +55,8 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind { try { - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model //return _pScreenInfoProvider->Signal(UIA_AutomationFocusChangedEventId); return E_NOTIMPL; } @@ -76,7 +72,8 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind if (id == UIA_Text_TextSelectionChangedEventId || id == UIA_Text_TextChangedEventId) { - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model /*if (_pScreenInfoProvider) { hr = _pScreenInfoProvider->Signal(id); @@ -116,7 +113,8 @@ IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _CO *ppProvider = nullptr; HRESULT hr = S_OK; - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2102 or #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model /*if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) { *ppProvider = _pScreenInfoProvider; @@ -145,7 +143,8 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, { RETURN_IF_FAILED(_EnsureValidHwnd()); - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model /**ppProvider = _pScreenInfoProvider; (*ppProvider)->AddRef();*/ @@ -155,7 +154,8 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { RETURN_IF_FAILED(_EnsureValidHwnd()); - // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider + // This may be needed for the signaling model //return _pScreenInfoProvider->QueryInterface(IID_PPV_ARGS(ppProvider)); return S_OK; } diff --git a/src/host/renderData.cpp b/src/host/renderData.cpp index 685bfadfd98..92030f03fd2 100644 --- a/src/host/renderData.cpp +++ b/src/host/renderData.cpp @@ -9,12 +9,10 @@ #include "handle.h" #include "..\interactivity\inc\ServiceLocator.hpp" -#include "search.h" -#include "..\types\UiaTextRange.hpp" - #pragma hdrstop using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Interactivity::Win32; using Microsoft::Console::Interactivity::ServiceLocator; #pragma region IBaseData @@ -371,57 +369,4 @@ void RenderData::SelectNewRegion(const COORD coordStart, const COORD coordEnd) { Selection::Instance().SelectNewRegion(coordStart, coordEnd); } - -// TODO GitHub #605: Search functionality -// For now, just adding it here to make UiaTextRange easier to create (Accessibility) -// We should actually abstract this out better once Windows Terminal has Search -HRESULT RenderData::SearchForText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, - unsigned int _start, - unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, - std::function Clone) -{ - typedef unsigned int Endpoint; - - const std::wstring wstr{ text, SysStringLen(text) }; - const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive; - - auto searchDirection = Search::Direction::Forward; - Endpoint searchAnchor = _start; - if (searchBackward) - { - searchDirection = Search::Direction::Backward; - searchAnchor = _end; - } - - CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - THROW_HR_IF(E_POINTER, !gci.HasActiveOutputBuffer()); - const auto& screenInfo = gci.GetActiveOutputBuffer().GetActiveBuffer(); - - Search searcher{ screenInfo, wstr, searchDirection, sensitivity, _endpointToCoord(this, searchAnchor) }; - - HRESULT hr = S_OK; - if (searcher.FindNext()) - { - const auto foundLocation = searcher.GetFoundLocation(); - const Endpoint start = _coordToEndpoint(this, foundLocation.first); - const Endpoint end = _coordToEndpoint(this, foundLocation.second); - // make sure what was found is within the bounds of the current range - if ((searchDirection == Search::Direction::Forward && end < _end) || - (searchDirection == Search::Direction::Backward && start > _start)) - { - hr = Clone(ppRetVal); - if (SUCCEEDED(hr)) - { - UiaTextRange& range = static_cast(**ppRetVal); - range.SetRangeValues(start, end, false); - } - } - } - return hr; -} #pragma endregion diff --git a/src/host/renderData.hpp b/src/host/renderData.hpp index 0a2054b9239..717e7b8418d 100644 --- a/src/host/renderData.hpp +++ b/src/host/renderData.hpp @@ -59,18 +59,5 @@ class RenderData final : const bool IsSelectionActive() const override; void ClearSelection() override; void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override; - - // TODO GitHub #605: Search functionality - // For now, just adding it here to make UiaTextRange easier to create (Accessibility) - // We should actually abstract this out better once Windows Terminal has Search - HRESULT SearchForText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, - unsigned int _start, - unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, - std::function Clone); #pragma endregion }; diff --git a/src/host/tracing.cpp b/src/host/tracing.cpp index a3ecf287e12..6c48a599fdd 100644 --- a/src/host/tracing.cpp +++ b/src/host/tracing.cpp @@ -3,8 +3,8 @@ #include "precomp.h" #include "tracing.hpp" -#include "../types/UiaTextRange.hpp" -#include "../types/ScreenInfoUiaProvider.h" +#include "../types/UiaTextRangeBase.hpp" +#include "../types/ScreenInfoUiaProviderBase.h" #include "../types/WindowUiaProviderBase.hpp" diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj b/src/interactivity/win32/lib/win32.LIB.vcxproj index 8833c49c5f9..d9afadfee87 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj @@ -19,7 +19,9 @@ Create + + @@ -41,7 +43,9 @@ + + diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters index 015db990ff1..4a4ad011590 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters @@ -72,6 +72,12 @@ Source Files + + Source Files + + + Source Files + @@ -131,6 +137,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/src/interactivity/win32/screenInfoUiaProvider.cpp b/src/interactivity/win32/screenInfoUiaProvider.cpp new file mode 100644 index 00000000000..9b525e33ce9 --- /dev/null +++ b/src/interactivity/win32/screenInfoUiaProvider.cpp @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "screenInfoUiaProvider.hpp" +#include "..\types\WindowUiaProviderBase.hpp" + +using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Render; +using namespace Microsoft::Console::Interactivity; +using namespace Microsoft::Console::Interactivity::Win32; + +ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, + _In_ WindowUiaProviderBase* const pUiaParent) : + _pUiaParent(THROW_HR_IF_NULL(E_INVALIDARG, pUiaParent)), + ScreenInfoUiaProviderBase(THROW_HR_IF_NULL(E_INVALIDARG, pData)) +{ +} + +IFACEMETHODIMP ScreenInfoUiaProvider::Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +{ + RETURN_HR_IF(E_INVALIDARG, ppProvider == nullptr); + *ppProvider = nullptr; + + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + /*ApiMsgNavigate apiMsg; + apiMsg.Direction = direction; + Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg);*/ + + if (direction == NavigateDirection_Parent) + { + try + { + _pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); + } + catch (...) + { + *ppProvider = nullptr; + return wil::ResultFromCaughtException(); + } + RETURN_IF_NULL_ALLOC(*ppProvider); + } + + // For the other directions the default of nullptr is correct + return S_OK; +} + +IFACEMETHODIMP ScreenInfoUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); + + RECT rc = _pUiaParent->GetWindowRect(); + + pRect->left = rc.left; + pRect->top = rc.top; + pRect->width = rc.right - rc.left; + pRect->height = rc.bottom - rc.top; + + return S_OK; +} + +IFACEMETHODIMP ScreenInfoUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) +{ + RETURN_HR_IF(E_INVALIDARG, ppProvider == nullptr); + *ppProvider = nullptr; + + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); + try + { + _pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); + } + catch (...) + { + *ppProvider = nullptr; + return wil::ResultFromCaughtException(); + } + RETURN_IF_NULL_ALLOC(*ppProvider); + return S_OK; +} + +HWND ScreenInfoUiaProvider::GetWindowHandle() const +{ + return _pUiaParent->GetWindowHandle(); +} + +void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow) +{ + _pUiaParent->ChangeViewport(NewWindow); +} + +std::deque ScreenInfoUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) +{ + std::deque result; + + auto ranges = UiaTextRange::GetSelectionRanges(_pData, pProvider); + while (!ranges.empty()) + { + result.emplace_back(ranges.back()); + ranges.pop_back(); + } + + return result; +} + +UiaTextRangeBase* ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider) +{ + return UiaTextRange::Create(_pData, pProvider); +} + +UiaTextRangeBase* ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) +{ + return UiaTextRange::Create(_pData, pProvider, cursor); +} + +UiaTextRangeBase* ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) +{ + return UiaTextRange::Create(_pData, pProvider, start, end, degenerate); +} + +UiaTextRangeBase* ScreenInfoUiaProvider::CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) +{ + return UiaTextRange::Create(_pData, pProvider, point); +} diff --git a/src/interactivity/win32/screenInfoUiaProvider.hpp b/src/interactivity/win32/screenInfoUiaProvider.hpp new file mode 100644 index 00000000000..b7c29ce3cf3 --- /dev/null +++ b/src/interactivity/win32/screenInfoUiaProvider.hpp @@ -0,0 +1,68 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- screenInfoUiaProvider.hpp + +Abstract: +- This module provides UI Automation access to the screen buffer to + support both automation tests and accessibility (screen reading) + applications. +- This is the ConHost extension of ScreenInfoUiaProviderBase.hpp +- Based on examples, sample code, and guidance from + https://msdn.microsoft.com/en-us/library/windows/desktop/ee671596(v=vs.85).aspx + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "precomp.h" +#include "..\types\ScreenInfoUiaProviderBase.h" +#include "..\types\UiaTextRangeBase.hpp" +#include "uiaTextRange.hpp" + +namespace Microsoft::Console::Interactivity::Win32 +{ + class ScreenInfoUiaProvider final : public Microsoft::Console::Types::ScreenInfoUiaProviderBase + { + public: + ScreenInfoUiaProvider(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ Microsoft::Console::Types::WindowUiaProviderBase* const pUiaParent); + + // IRawElementProviderFragment methods + IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) override; + IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override; + IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override; + + HWND GetWindowHandle() const; + void ChangeViewport(const SMALL_RECT NewWindow); + + protected: + std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) override; + + // degenerate range + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider) override; + + // degenerate range at cursor position + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) override; + + // specific endpoint range + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) override; + + // range from a UiaPoint + Microsoft::Console::Types::UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) override; + + private: + // weak reference to uia parent + Microsoft::Console::Types::WindowUiaProviderBase* const _pUiaParent; + }; +} diff --git a/src/interactivity/win32/uiaTextRange.cpp b/src/interactivity/win32/uiaTextRange.cpp new file mode 100644 index 00000000000..7751da3042f --- /dev/null +++ b/src/interactivity/win32/uiaTextRange.cpp @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "uiaTextRange.hpp" +#include "screenInfoUiaProvider.hpp" +#include "..\host\search.h" +#include "..\interactivity\inc\ServiceLocator.hpp" + +using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Interactivity::Win32; +using Microsoft::Console::Interactivity::ServiceLocator; + +std::deque UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* pProvider) +{ + std::deque ranges; + + // get the selection rects + const auto rectangles = pData->GetSelectionRects(); + + // create a range for each row + for (const auto& rect : rectangles) + { + ScreenInfoRow currentRow = rect.Top(); + Endpoint start = _screenInfoRowToEndpoint(pData, currentRow) + rect.Left(); + Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive(); + UiaTextRange* range = UiaTextRange::Create(pData, + pProvider, + start, + end, + false); + if (range == nullptr) + { + // something when wrong, clean up and throw + while (!ranges.empty()) + { + UiaTextRangeBase* temp = ranges[0]; + ranges.pop_front(); + temp->Release(); + } + THROW_HR(E_INVALIDARG); + } + else + { + ranges.push_back(range); + } + } + return ranges; +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider) +{ + UiaTextRange* range = nullptr; + ; + try + { + range = new UiaTextRange(pData, pProvider); + } + catch (...) + { + range = nullptr; + } + + if (range) + { + pProvider->AddRef(); + } + return range; +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) +{ + UiaTextRange* range = nullptr; + try + { + range = new UiaTextRange(pData, pProvider, cursor); + } + catch (...) + { + range = nullptr; + } + + if (range) + { + pProvider->AddRef(); + } + return range; +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) +{ + UiaTextRange* range = nullptr; + try + { + range = new UiaTextRange(pData, + pProvider, + start, + end, + degenerate); + } + catch (...) + { + range = nullptr; + } + + if (range) + { + pProvider->AddRef(); + } + return range; +} + +UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) +{ + UiaTextRange* range = nullptr; + try + { + range = new UiaTextRange(pData, pProvider, point); + } + catch (...) + { + range = nullptr; + } + + if (range) + { + pProvider->AddRef(); + } + return range; +} + +// degenerate range constructor. +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) : + UiaTextRangeBase(pData, pProvider) +{ +} + +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) : + UiaTextRangeBase(pData, pProvider, cursor) +{ +} + +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) : + UiaTextRangeBase(pData, pProvider, start, end, degenerate) +{ +} + +// returns a degenerate text range of the start of the row closest to the y value of point +UiaTextRange::UiaTextRange(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) : + UiaTextRangeBase(pData, pProvider) +{ + Initialize(point); +} + +IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +{ + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + try + { + *ppRetVal = new UiaTextRange(*this); + } + catch (...) + { + *ppRetVal = nullptr; + return wil::ResultFromCaughtException(); + } + if (*ppRetVal == nullptr) + { + return E_OUTOFMEMORY; + } + +#if defined(_DEBUG) && defined(UiaTextRangeBase_DEBUG_MSGS) + OutputDebugString(L"Clone\n"); + std::wstringstream ss; + ss << _id << L" cloned to " << (static_cast(*ppRetVal))->_id; + std::wstring str = ss.str(); + OutputDebugString(str.c_str()); + OutputDebugString(L"\n"); +#endif + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + // tracing + /*ApiMsgClone apiMsg; + apiMsg.CloneId = static_cast(*ppRetVal)->GetId(); + Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/ + + return S_OK; +} + +IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +{ + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + //Tracing::s_TraceUia(this, ApiCall::FindText, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + try + { + const std::wstring wstr{ text, SysStringLen(text) }; + const auto sensitivity = ignoreCase ? Search::Sensitivity::CaseInsensitive : Search::Sensitivity::CaseSensitive; + + auto searchDirection = Search::Direction::Forward; + Endpoint searchAnchor = _start; + if (searchBackward) + { + searchDirection = Search::Direction::Backward; + searchAnchor = _end; + } + + CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + THROW_HR_IF(E_POINTER, !gci.HasActiveOutputBuffer()); + const auto& screenInfo = gci.GetActiveOutputBuffer().GetActiveBuffer(); + Search searcher{ screenInfo, wstr, searchDirection, sensitivity, _endpointToCoord(_pData, searchAnchor) }; + + HRESULT hr = S_OK; + if (searcher.FindNext()) + { + const auto foundLocation = searcher.GetFoundLocation(); + const Endpoint start = _coordToEndpoint(_pData, foundLocation.first); + const Endpoint end = _coordToEndpoint(_pData, foundLocation.second); + // make sure what was found is within the bounds of the current range + if ((searchDirection == Search::Direction::Forward && end < _end) || + (searchDirection == Search::Direction::Backward && start > _start)) + { + hr = Clone(ppRetVal); + if (SUCCEEDED(hr)) + { + UiaTextRange& range = static_cast(**ppRetVal); + range._start = start; + range._end = end; + range._degenerate = false; + } + } + } + return hr; + } + CATCH_RETURN(); +} + +void UiaTextRange::_ChangeViewport(const SMALL_RECT NewWindow) +{ + auto provider = static_cast(_pProvider.get()); + provider->ChangeViewport(NewWindow); +} + +void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const +{ + ClientToScreen(_getWindowHandle(), clientPoint); +} + +void UiaTextRange::_TranslatePointFromScreen(LPPOINT screenPoint) const +{ + ScreenToClient(_getWindowHandle(), screenPoint); +} + +HWND UiaTextRange::_getWindowHandle() const +{ + const auto provider = static_cast(_pProvider.get()); + return provider->GetWindowHandle(); +} diff --git a/src/interactivity/win32/uiaTextRange.hpp b/src/interactivity/win32/uiaTextRange.hpp new file mode 100644 index 00000000000..998ed313604 --- /dev/null +++ b/src/interactivity/win32/uiaTextRange.hpp @@ -0,0 +1,90 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- UiaTextRange.hpp + +Abstract: +- This module provides UI Automation access to the text of the console + window to support both automation tests and accessibility (screen + reading) applications. + +Author(s): +- Carlos Zamora (CaZamor) 2019 +--*/ + +#pragma once + +#include "precomp.h" +#include "..\types\UiaTextRangeBase.hpp" + +namespace Microsoft::Console::Interactivity::Win32 +{ + class UiaTextRange final : public Microsoft::Console::Types::UiaTextRangeBase + { + public: + static std::deque GetSelectionRanges(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* pProvider); + + // degenerate range + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider); + + // degenerate range at cursor position + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor); + + // specific endpoint range + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate); + + // range from a UiaPoint + static UiaTextRange* Create(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point); + + IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + IFACEMETHODIMP FindText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + + protected: + void _ChangeViewport(const SMALL_RECT NewWindow) override; + void _TranslatePointToScreen(LPPOINT clientPoint) const override; + void _TranslatePointFromScreen(LPPOINT screenPoint) const override; + + private: + // degenerate range + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider); + + // degenerate range at cursor position + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor); + + // specific endpoint range + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate); + + // range from a UiaPoint + UiaTextRange(_In_ Microsoft::Console::Types::IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point); + + HWND _getWindowHandle() const; + +#ifdef UNIT_TESTING + friend class ::UiaTextRangeTests; +#endif + }; +} diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index 85d521a2e95..54570294cac 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -6,14 +6,14 @@ #include "..\..\inc\consoletaeftemplates.hpp" #include "CommonState.hpp" -#include "..\types\UiaTextRange.hpp" +#include "uiaTextRange.hpp" #include "../../../buffer/out/textBuffer.hpp" using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; -using namespace Microsoft::Console::Types; +using namespace Microsoft::Console::Interactivity::Win32; // UiaTextRange takes an object that implements // IRawElementProviderSimple as a constructor argument. Making a real diff --git a/src/interactivity/win32/windowUiaProvider.cpp b/src/interactivity/win32/windowUiaProvider.cpp index ea0d81db471..de320b12760 100644 --- a/src/interactivity/win32/windowUiaProvider.cpp +++ b/src/interactivity/win32/windowUiaProvider.cpp @@ -3,7 +3,7 @@ #include "precomp.h" #include "windowUiaProvider.hpp" -#include "../types/ScreenInfoUiaProvider.h" +#include "screenInfoUiaProvider.hpp" #include "../types/IUiaData.h" #include "../host/renderData.hpp" @@ -29,7 +29,7 @@ WindowUiaProvider::~WindowUiaProvider() WindowUiaProvider* WindowUiaProvider::Create(IConsoleWindow* baseWindow) { WindowUiaProvider* pWindowProvider = nullptr; - Microsoft::Console::Types::ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; + ScreenInfoUiaProvider* pScreenInfoProvider = nullptr; try { pWindowProvider = new WindowUiaProvider(baseWindow); @@ -38,7 +38,7 @@ WindowUiaProvider* WindowUiaProvider::Create(IConsoleWindow* baseWindow) CONSOLE_INFORMATION& gci = g.getConsoleInformation(); IUiaData* uiaData = &gci.renderData; - pScreenInfoProvider = new Microsoft::Console::Types::ScreenInfoUiaProvider(uiaData, pWindowProvider); + pScreenInfoProvider = new ScreenInfoUiaProvider(uiaData, pWindowProvider); pWindowProvider->_pScreenInfoProvider = pScreenInfoProvider; // TODO GitHub #1914: Re-attach Tracing to UIA Tree diff --git a/src/interactivity/win32/windowUiaProvider.hpp b/src/interactivity/win32/windowUiaProvider.hpp index cb9f1b31bb3..777ad2f8bd7 100644 --- a/src/interactivity/win32/windowUiaProvider.hpp +++ b/src/interactivity/win32/windowUiaProvider.hpp @@ -57,6 +57,6 @@ namespace Microsoft::Console::Interactivity::Win32 WindowUiaProvider(Microsoft::Console::Types::IConsoleWindow* baseWindow); ~WindowUiaProvider(); - Microsoft::Console::Types::ScreenInfoUiaProvider* _pScreenInfoProvider; + ScreenInfoUiaProvider* _pScreenInfoProvider; }; } diff --git a/src/types/IUiaData.h b/src/types/IUiaData.h index 73a693de8c8..a087789172d 100644 --- a/src/types/IUiaData.h +++ b/src/types/IUiaData.h @@ -28,19 +28,6 @@ namespace Microsoft::Console::Types virtual const bool IsSelectionActive() const = 0; virtual void ClearSelection() = 0; virtual void SelectNewRegion(const COORD coordStart, const COORD coordEnd) = 0; - - // TODO GitHub #605: Search functionality - // For now, just adding it here to make UiaTextRange easier to create (Accessibility) - // We should actually abstract this out better once Windows Terminal has Search - virtual HRESULT SearchForText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal, - unsigned int _start, - unsigned int _end, - std::function _coordToEndpoint, - std::function _endpointToCoord, - std::function Clone) = 0; }; // See docs/virtual-dtors.md for an explanation of why this is weird. diff --git a/src/types/ScreenInfoUiaProvider.cpp b/src/types/ScreenInfoUiaProviderBase.cpp similarity index 66% rename from src/types/ScreenInfoUiaProvider.cpp rename to src/types/ScreenInfoUiaProviderBase.cpp index 4d22a3bd120..aff23dde5cc 100644 --- a/src/types/ScreenInfoUiaProvider.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -3,10 +3,8 @@ #include "precomp.h" -#include "ScreenInfoUiaProvider.h" - +#include "ScreenInfoUiaProviderBase.h" #include "WindowUiaProviderBase.hpp" -#include "UiaTextRange.hpp" using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing; @@ -31,22 +29,7 @@ SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int return psa; } -ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, - _In_ WindowUiaProviderBase* const pUiaParent, - _In_ std::function GetBoundingRect) : - _pUiaParent(pUiaParent), - _signalFiringMapping{}, - _cRefs(1), - _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)), - _getBoundingRect(GetBoundingRect) -{ - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); -} - -ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, - _In_ WindowUiaProviderBase* const pUiaParent) : - _pUiaParent(pUiaParent), +ScreenInfoUiaProviderBase::ScreenInfoUiaProviderBase(_In_ IUiaData* pData) : _signalFiringMapping{}, _cRefs(1), _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)) @@ -55,11 +38,11 @@ ScreenInfoUiaProvider::ScreenInfoUiaProvider(_In_ IUiaData* pData, //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); } -ScreenInfoUiaProvider::~ScreenInfoUiaProvider() +ScreenInfoUiaProviderBase::~ScreenInfoUiaProviderBase() { } -[[nodiscard]] HRESULT ScreenInfoUiaProvider::Signal(_In_ EVENTID id) +[[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID id) { HRESULT hr = S_OK; // check to see if we're already firing this particular event @@ -90,7 +73,7 @@ ScreenInfoUiaProvider::~ScreenInfoUiaProvider() #pragma region IUnknown IFACEMETHODIMP_(ULONG) -ScreenInfoUiaProvider::AddRef() +ScreenInfoUiaProviderBase::AddRef() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); @@ -98,7 +81,7 @@ ScreenInfoUiaProvider::AddRef() } IFACEMETHODIMP_(ULONG) -ScreenInfoUiaProvider::Release() +ScreenInfoUiaProviderBase::Release() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::Release, nullptr); @@ -110,8 +93,8 @@ ScreenInfoUiaProvider::Release() return val; } -IFACEMETHODIMP ScreenInfoUiaProvider::QueryInterface(_In_ REFIID riid, - _COM_Outptr_result_maybenull_ void** ppInterface) +IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, + _COM_Outptr_result_maybenull_ void** ppInterface) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); @@ -148,7 +131,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::QueryInterface(_In_ REFIID riid, // Implementation of IRawElementProviderSimple::get_ProviderOptions. // Gets UI Automation provider options. -IFACEMETHODIMP ScreenInfoUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* pOptions) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); @@ -158,9 +141,12 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_ProviderOptions(_Out_ ProviderOptions* // Implementation of IRawElementProviderSimple::get_PatternProvider. // Gets the object that supports ISelectionPattern. -IFACEMETHODIMP ScreenInfoUiaProvider::GetPatternProvider(_In_ PATTERNID patternId, - _COM_Outptr_result_maybenull_ IUnknown** ppInterface) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patternId, + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) { + RETURN_HR_IF(E_INVALIDARG, ppInterface == nullptr); + *ppInterface = nullptr; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetPatternProvider, nullptr); @@ -180,8 +166,8 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetPatternProvider(_In_ PATTERNID patternI // Implementation of IRawElementProviderSimple::get_PropertyValue. // Gets custom properties. -IFACEMETHODIMP ScreenInfoUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyId, - _Out_ VARIANT* pVariant) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyId, + _Out_ VARIANT* pVariant) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr); @@ -251,10 +237,11 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetPropertyValue(_In_ PROPERTYID propertyI return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProvider::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppProvider == nullptr); *ppProvider = nullptr; return S_OK; @@ -263,42 +250,13 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_HostRawElementProvider(_COM_Outptr_res #pragma region IRawElementProviderFragment -IFACEMETHODIMP ScreenInfoUiaProvider::Navigate(_In_ NavigateDirection direction, - _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) -{ - // TODO GitHub 2120: _pUiaParent should not be allowed to be null - RETURN_HR_IF(E_NOTIMPL, _pUiaParent == nullptr); - - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - /*ApiMsgNavigate apiMsg; - apiMsg.Direction = direction; - Tracing::s_TraceUia(this, ApiCall::Navigate, &apiMsg);*/ - *ppProvider = nullptr; - - if (direction == NavigateDirection_Parent) - { - try - { - _pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); - } - catch (...) - { - *ppProvider = nullptr; - return wil::ResultFromCaughtException(); - } - RETURN_IF_NULL_ALLOC(*ppProvider); - } - - // For the other directions the default of nullptr is correct - return S_OK; -} - -IFACEMETHODIMP ScreenInfoUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); // Root defers this to host, others must implement it... + RETURN_HR_IF(E_INVALIDARG, ppRuntimeId == nullptr); *ppRuntimeId = nullptr; // AppendRuntimeId is a magic Number that tells UIAutomation to Append its own Runtime ID(From the HWND) @@ -310,40 +268,17 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetRuntimeId(_Outptr_result_maybenull_ SAF return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProvider::get_BoundingRectangle(_Out_ UiaRect* pRect) -{ - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangle, nullptr); - - RECT rc; - // TODO GitHub 2120: _pUiaParent should not be allowed to be null - if (_pUiaParent == nullptr) - { - rc = _getBoundingRect(); - } - else - { - rc = _pUiaParent->GetWindowRect(); - } - - pRect->left = rc.left; - pRect->top = rc.top; - pRect->width = rc.right - rc.left; - pRect->height = rc.bottom - rc.top; - - return S_OK; -} - -IFACEMETHODIMP ScreenInfoUiaProvider::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRoots == nullptr); *ppRoots = nullptr; return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProvider::SetFocus() +IFACEMETHODIMP ScreenInfoUiaProviderBase::SetFocus() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::SetFocus, nullptr); @@ -351,30 +286,11 @@ IFACEMETHODIMP ScreenInfoUiaProvider::SetFocus() return Signal(UIA_AutomationFocusChangedEventId); } -IFACEMETHODIMP ScreenInfoUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) -{ - // TODO GitHub 2120: _pUiaParent should not be allowed to be null - RETURN_HR_IF(E_NOTIMPL, _pUiaParent == nullptr); - - //Tracing::s_TraceUia(this, ApiCall::GetFragmentRoot, nullptr); - try - { - _pUiaParent->QueryInterface(IID_PPV_ARGS(ppProvider)); - } - catch (...) - { - *ppProvider = nullptr; - return wil::ResultFromCaughtException(); - } - RETURN_IF_NULL_ALLOC(*ppProvider); - return S_OK; -} - #pragma endregion #pragma region ITextProvider -IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //ApiMsgGetSelection apiMsg; @@ -384,6 +300,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF _UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); *ppRetVal = nullptr; HRESULT hr = S_OK; @@ -412,12 +329,11 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF return hr; } - UiaTextRange* range; + UiaTextRangeBase* range; try { - range = UiaTextRange::Create(_pData, - pProvider, - cursor); + range = CreateTextRange(pProvider, + cursor); } catch (...) { @@ -444,12 +360,12 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF else { // get the selection ranges - std::deque ranges; + std::deque ranges; IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(QueryInterface(IID_PPV_ARGS(&pProvider))); try { - ranges = UiaTextRange::GetSelectionRanges(_pData, pProvider); + ranges = GetSelectionRanges(pProvider); } catch (...) { @@ -479,7 +395,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF *ppRetVal = nullptr; while (!ranges.empty()) { - UiaTextRange* pRange = ranges[0]; + UiaTextRangeBase* pRange = ranges[0]; ranges.pop_front(); pRange->Release(); } @@ -493,7 +409,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetSelection(_Outptr_result_maybenull_ SAF return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetVisibleRanges, nullptr); @@ -503,6 +419,9 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ _UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + const auto viewport = _getViewport(); const COORD screenBufferCoords = _getScreenBufferCoords(); const int totalLines = screenBufferCoords.Y; @@ -532,14 +451,13 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ return hr; } - UiaTextRange* range; + UiaTextRangeBase* range; try { - range = UiaTextRange::Create(_pData, - pProvider, - start, - end, - false); + range = CreateTextRange(pProvider, + start, + end, + false); } catch (...) { @@ -567,19 +485,22 @@ IFACEMETHODIMP ScreenInfoUiaProvider::GetVisibleRanges(_Outptr_result_maybenull_ return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromChild(_In_ IRawElementProviderSimple* /*childElement*/, - _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromChild(_In_ IRawElementProviderSimple* /*childElement*/, + _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RangeFromChild, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(_pData, pProvider); + *ppRetVal = CreateTextRange(pProvider); } catch (...) { @@ -591,21 +512,23 @@ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromChild(_In_ IRawElementProviderSim return hr; } -IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromPoint(_In_ UiaPoint point, - _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point, + _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RangeFromPoint, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(_pData, - pProvider, - point); + *ppRetVal = CreateTextRange(pProvider, + point); } catch (...) { @@ -617,18 +540,21 @@ IFACEMETHODIMP ScreenInfoUiaProvider::RangeFromPoint(_In_ UiaPoint point, return hr; } -IFACEMETHODIMP ScreenInfoUiaProvider::get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetDocumentRange, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); HRESULT hr = S_OK; try { - *ppRetVal = UiaTextRange::Create(_pData, pProvider); + *ppRetVal = CreateTextRange(pProvider); } catch (...) { @@ -645,7 +571,7 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_DocumentRange(_COM_Outptr_result_maybe return hr; } -IFACEMETHODIMP ScreenInfoUiaProvider::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr); @@ -656,49 +582,29 @@ IFACEMETHODIMP ScreenInfoUiaProvider::get_SupportedTextSelection(_Out_ Supported #pragma endregion -const COORD ScreenInfoUiaProvider::_getScreenBufferCoords() const +const COORD ScreenInfoUiaProviderBase::_getScreenBufferCoords() const { return _getTextBuffer().GetSize().Dimensions(); } -const TextBuffer& ScreenInfoUiaProvider::_getTextBuffer() const +const TextBuffer& ScreenInfoUiaProviderBase::_getTextBuffer() const { return _pData->GetTextBuffer(); } -const Viewport ScreenInfoUiaProvider::_getViewport() const +const Viewport ScreenInfoUiaProviderBase::_getViewport() const { return _pData->GetViewport(); } -void ScreenInfoUiaProvider::_LockConsole() noexcept +void ScreenInfoUiaProviderBase::_LockConsole() noexcept { // TODO GitHub #2141: Lock and Unlock in conhost should decouple Ctrl+C dispatch and use smarter handling _pData->LockConsole(); } -void ScreenInfoUiaProvider::_UnlockConsole() noexcept +void ScreenInfoUiaProviderBase::_UnlockConsole() noexcept { // TODO GitHub #2141: Lock and Unlock in conhost should decouple Ctrl+C dispatch and use smarter handling _pData->UnlockConsole(); } - -HWND ScreenInfoUiaProvider::GetWindowHandle() const -{ - // TODO GitHub 2120: _pUiaParent should not be allowed to be null - if (_pUiaParent == nullptr) - { - return nullptr; - } - return _pUiaParent->GetWindowHandle(); -} - -void ScreenInfoUiaProvider::ChangeViewport(const SMALL_RECT NewWindow) -{ - // TODO GitHub 2120: _pUiaParent should not be allowed to be null - if (_pUiaParent == nullptr) - { - return; - } - _pUiaParent->ChangeViewport(NewWindow); -} diff --git a/src/types/ScreenInfoUiaProvider.h b/src/types/ScreenInfoUiaProviderBase.h similarity index 72% rename from src/types/ScreenInfoUiaProvider.h rename to src/types/ScreenInfoUiaProviderBase.h index 93bae88c096..0b47454eefa 100644 --- a/src/types/ScreenInfoUiaProvider.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: -- screenInfoUiaProvider.hpp +- ScreenInfoUiaProviderBase.hpp Abstract: - This module provides UI Automation access to the screen buffer to @@ -23,6 +23,7 @@ Author(s): #include "precomp.h" #include "../buffer/out/textBuffer.hpp" +#include "UiaTextRangeBase.hpp" #include "IUiaData.h" namespace Microsoft::Console::Types @@ -30,20 +31,14 @@ namespace Microsoft::Console::Types class WindowUiaProviderBase; class Viewport; - class ScreenInfoUiaProvider : + class ScreenInfoUiaProviderBase : public IRawElementProviderSimple, public IRawElementProviderFragment, public ITextProvider { public: - ScreenInfoUiaProvider(_In_ IUiaData* pData, - _In_ WindowUiaProviderBase* const pUiaParent, - _In_ std::function GetBoundingRect); - - // TODO GitHub 2120: pUiaParent should not be allowed to be null - ScreenInfoUiaProvider(_In_ IUiaData* pData, - _In_ WindowUiaProviderBase* const pUiaParent); - virtual ~ScreenInfoUiaProvider(); + ScreenInfoUiaProviderBase(_In_ IUiaData* pData); + virtual ~ScreenInfoUiaProviderBase(); [[nodiscard]] HRESULT Signal(_In_ EVENTID id); @@ -64,13 +59,13 @@ namespace Microsoft::Console::Types IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider); // IRawElementProviderFragment methods - IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, - _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider); + virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId); - IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect); + virtual IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) = 0; IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots); IFACEMETHODIMP SetFocus(); - IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider); + virtual IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) = 0; // ITextProvider IFACEMETHODIMP GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); @@ -82,19 +77,33 @@ namespace Microsoft::Console::Types IFACEMETHODIMP get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal); - HWND GetWindowHandle() const; - void ChangeViewport(const SMALL_RECT NewWindow); + protected: + virtual std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) = 0; - private: - // Ref counter for COM object - ULONG _cRefs; + // degenerate range + virtual UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider) = 0; + + // degenerate range at cursor position + virtual UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) = 0; + + // specific endpoint range + virtual UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) = 0; - // weak reference to uia parent - WindowUiaProviderBase* const _pUiaParent; + // range from a UiaPoint + virtual UiaTextRangeBase* CreateTextRange(_In_ IRawElementProviderSimple* const pProvider, + const UiaPoint point) = 0; // weak reference to IRenderData IUiaData* _pData; + private: + // Ref counter for COM object + ULONG _cRefs; + // this is used to prevent the object from // signaling an event while it is already in the // process of signalling another event. @@ -113,9 +122,6 @@ namespace Microsoft::Console::Types const Viewport _getViewport() const; void _LockConsole() noexcept; void _UnlockConsole() noexcept; - - // these functions are reserved for Windows Terminal - std::function _getBoundingRect; }; namespace ScreenInfoUiaProviderTracing diff --git a/src/types/UiaTextRange.cpp b/src/types/UiaTextRangeBase.cpp similarity index 75% rename from src/types/UiaTextRange.cpp rename to src/types/UiaTextRangeBase.cpp index 6df92d307f4..845ce21042c 100644 --- a/src/types/UiaTextRange.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -2,52 +2,52 @@ // Licensed under the MIT license. #include "precomp.h" -#include "UiaTextRange.hpp" -#include "ScreenInfoUiaProvider.h" +#include "UiaTextRangeBase.hpp" +#include "ScreenInfoUiaProviderBase.h" using namespace Microsoft::Console::Types; -using namespace Microsoft::Console::Types::UiaTextRangeTracing; +using namespace Microsoft::Console::Types::UiaTextRangeBaseTracing; // toggle these for additional logging in a debug build //#define UIATEXTRANGE_DEBUG_MSGS 1 #undef UIATEXTRANGE_DEBUG_MSGS -IdType UiaTextRange::id = 1; +IdType UiaTextRangeBase::id = 1; -UiaTextRange::MoveState::MoveState(IUiaData* pData, - const UiaTextRange& range, - const MovementDirection direction) : - StartScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(pData, range.GetStart()) }, - StartColumn{ UiaTextRange::_endpointToColumn(pData, range.GetStart()) }, - EndScreenInfoRow{ UiaTextRange::_endpointToScreenInfoRow(pData, range.GetEnd()) }, - EndColumn{ UiaTextRange::_endpointToColumn(pData, range.GetEnd()) }, +UiaTextRangeBase::MoveState::MoveState(IUiaData* pData, + const UiaTextRangeBase& range, + const MovementDirection direction) : + StartScreenInfoRow{ UiaTextRangeBase::_endpointToScreenInfoRow(pData, range.GetStart()) }, + StartColumn{ UiaTextRangeBase::_endpointToColumn(pData, range.GetStart()) }, + EndScreenInfoRow{ UiaTextRangeBase::_endpointToScreenInfoRow(pData, range.GetEnd()) }, + EndColumn{ UiaTextRangeBase::_endpointToColumn(pData, range.GetEnd()) }, Direction{ direction } { if (direction == MovementDirection::Forward) { - LimitingRow = UiaTextRange::_getLastScreenInfoRowIndex(pData); - FirstColumnInRow = UiaTextRange::_getFirstColumnIndex(); - LastColumnInRow = UiaTextRange::_getLastColumnIndex(pData); + LimitingRow = UiaTextRangeBase::_getLastScreenInfoRowIndex(pData); + FirstColumnInRow = UiaTextRangeBase::_getFirstColumnIndex(); + LastColumnInRow = UiaTextRangeBase::_getLastColumnIndex(pData); Increment = MovementIncrement::Forward; } else { - LimitingRow = UiaTextRange::_getFirstScreenInfoRowIndex(); - FirstColumnInRow = UiaTextRange::_getLastColumnIndex(pData); - LastColumnInRow = UiaTextRange::_getFirstColumnIndex(); + LimitingRow = UiaTextRangeBase::_getFirstScreenInfoRowIndex(); + FirstColumnInRow = UiaTextRangeBase::_getLastColumnIndex(pData); + LastColumnInRow = UiaTextRangeBase::_getFirstColumnIndex(); Increment = MovementIncrement::Backward; } } -UiaTextRange::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, - const Column startColumn, - const ScreenInfoRow endScreenInfoRow, - const Column endColumn, - const ScreenInfoRow limitingRow, - const Column firstColumnInRow, - const Column lastColumnInRow, - const MovementIncrement increment, - const MovementDirection direction) : +UiaTextRangeBase::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, + const Column startColumn, + const ScreenInfoRow endScreenInfoRow, + const Column endColumn, + const ScreenInfoRow limitingRow, + const Column firstColumnInRow, + const Column lastColumnInRow, + const MovementIncrement increment, + const MovementDirection direction) : StartScreenInfoRow{ startScreenInfoRow }, StartColumn{ startColumn }, EndScreenInfoRow{ endScreenInfoRow }, @@ -65,7 +65,7 @@ UiaTextRange::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, // This is a debugging function that prints out the current // relationship between screen info rows, text buffer rows, and // endpoints. -void UiaTextRange::_outputRowConversions(IUiaData* pData) +void UiaTextRangeBase::_outputRowConversions(IUiaData* pData) { try { @@ -86,7 +86,7 @@ void UiaTextRange::_outputRowConversions(IUiaData* pData) } } -void UiaTextRange::_outputObjectState() +void UiaTextRangeBase::_outputObjectState() { std::wstringstream ss; ss << "Object State"; @@ -101,136 +101,8 @@ void UiaTextRange::_outputObjectState() } #endif // _DEBUG -std::deque UiaTextRange::GetSelectionRanges(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* pProvider) -{ - std::deque ranges; - - // get the selection rects - const auto rectangles = pData->GetSelectionRects(); - - // create a range for each row - for (const auto& rect : rectangles) - { - ScreenInfoRow currentRow = rect.Top(); - Endpoint start = _screenInfoRowToEndpoint(pData, currentRow) + rect.Left(); - Endpoint end = _screenInfoRowToEndpoint(pData, currentRow) + rect.RightInclusive(); - UiaTextRange* range = UiaTextRange::Create(pData, - pProvider, - start, - end, - false); - if (range == nullptr) - { - // something when wrong, clean up and throw - while (!ranges.empty()) - { - UiaTextRange* temp = ranges[0]; - ranges.pop_front(); - temp->Release(); - } - throw E_INVALIDARG; - } - else - { - ranges.push_back(range); - } - } - return ranges; -} - -UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider) -{ - UiaTextRange* range = nullptr; - ; - try - { - range = new UiaTextRange(pData, pProvider); - } - catch (...) - { - range = nullptr; - } - - if (range) - { - pProvider->AddRef(); - } - return range; -} - -UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Cursor& cursor) -{ - UiaTextRange* range = nullptr; - try - { - range = new UiaTextRange(pData, pProvider, cursor); - } - catch (...) - { - range = nullptr; - } - - if (range) - { - pProvider->AddRef(); - } - return range; -} - -UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Endpoint start, - const Endpoint end, - const bool degenerate) -{ - UiaTextRange* range = nullptr; - try - { - range = new UiaTextRange(pData, - pProvider, - start, - end, - degenerate); - } - catch (...) - { - range = nullptr; - } - - if (range) - { - pProvider->AddRef(); - } - return range; -} - -UiaTextRange* UiaTextRange::Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const UiaPoint point) -{ - UiaTextRange* range = nullptr; - try - { - range = new UiaTextRange(pData, pProvider, point); - } - catch (...) - { - range = nullptr; - } - - if (range) - { - pProvider->AddRef(); - } - return range; -} - // degenerate range constructor. -UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) : +UiaTextRangeBase::UiaTextRangeBase(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* const pProvider) : _cRefs{ 1 }, _pProvider{ THROW_HR_IF_NULL(E_INVALIDARG, pProvider) }, _start{ 0 }, @@ -248,10 +120,10 @@ UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* Tracing::s_TraceUia(nullptr, ApiCall::Constructor, &apiMsg);*/ } -UiaTextRange::UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Cursor& cursor) : - UiaTextRange(pData, pProvider) +UiaTextRangeBase::UiaTextRangeBase(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor) : + UiaTextRangeBase(pData, pProvider) { _degenerate = true; _start = _screenInfoRowToEndpoint(_pData, cursor.GetPosition().Y) + cursor.GetPosition().X; @@ -263,12 +135,12 @@ UiaTextRange::UiaTextRange(_In_ IUiaData* pData, #endif } -UiaTextRange::UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Endpoint start, - const Endpoint end, - const bool degenerate) : - UiaTextRange(pData, pProvider) +UiaTextRangeBase::UiaTextRangeBase(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate) : + UiaTextRangeBase(pData, pProvider) { THROW_HR_IF(E_INVALIDARG, !degenerate && start > end); @@ -282,11 +154,7 @@ UiaTextRange::UiaTextRange(_In_ IUiaData* pData, #endif } -// returns a degenerate text range of the start of the row closest to the y value of point -UiaTextRange::UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const UiaPoint point) : - UiaTextRange(pData, pProvider) +void UiaTextRangeBase::Initialize(_In_ const UiaPoint point) { POINT clientPoint; clientPoint.x = static_cast(point.x); @@ -306,15 +174,7 @@ UiaTextRange::UiaTextRange(_In_ IUiaData* pData, else { // change point coords to pixels relative to window - HWND hwnd = _getWindowHandle(); - if (hwnd == nullptr) - { - // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT() - } - else - { - ScreenToClient(hwnd, &clientPoint); - } + _TranslatePointFromScreen(&clientPoint); const COORD currentFontSize = _getScreenFontSize(); row = (clientPoint.y / currentFontSize.Y) + viewport.Top; @@ -322,14 +182,9 @@ UiaTextRange::UiaTextRange(_In_ IUiaData* pData, _start = _screenInfoRowToEndpoint(_pData, row); _end = _start; _degenerate = true; - -#if defined(_DEBUG) && defined(UIATEXTRANGE_DEBUG_MSGS) - OutputDebugString(L"Constructor\n"); - _outputObjectState(); -#endif } -UiaTextRange::UiaTextRange(const UiaTextRange& a) : +UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) : _cRefs{ 1 }, _pProvider{ a._pProvider }, _start{ a._start }, @@ -337,7 +192,6 @@ UiaTextRange::UiaTextRange(const UiaTextRange& a) : _degenerate{ a._degenerate }, _pData{ a._pData } { - (static_cast(_pProvider))->AddRef(); _id = id; ++id; @@ -347,22 +201,17 @@ UiaTextRange::UiaTextRange(const UiaTextRange& a) : #endif } -UiaTextRange::~UiaTextRange() -{ - (static_cast(_pProvider))->Release(); -} - -const IdType UiaTextRange::GetId() const +const IdType UiaTextRangeBase::GetId() const { return _id; } -const Endpoint UiaTextRange::GetStart() const +const Endpoint UiaTextRangeBase::GetStart() const { return _start; } -const Endpoint UiaTextRange::GetEnd() const +const Endpoint UiaTextRangeBase::GetEnd() const { return _end; } @@ -373,12 +222,12 @@ const Endpoint UiaTextRange::GetEnd() const // - // Return Value: // - true if range is degenerate, false otherwise. -const bool UiaTextRange::IsDegenerate() const +const bool UiaTextRangeBase::IsDegenerate() const { return _degenerate; } -void UiaTextRange::SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) +void UiaTextRangeBase::SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) { _start = start; _end = end; @@ -388,7 +237,7 @@ void UiaTextRange::SetRangeValues(const Endpoint start, const Endpoint end, cons #pragma region IUnknown IFACEMETHODIMP_(ULONG) -UiaTextRange::AddRef() +UiaTextRangeBase::AddRef() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::AddRef, nullptr); @@ -396,7 +245,7 @@ UiaTextRange::AddRef() } IFACEMETHODIMP_(ULONG) -UiaTextRange::Release() +UiaTextRangeBase::Release() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::Release, nullptr); @@ -409,8 +258,11 @@ UiaTextRange::Release() return val; } -IFACEMETHODIMP UiaTextRange::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) +IFACEMETHODIMP UiaTextRangeBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { + RETURN_HR_IF(E_INVALIDARG, ppInterface == nullptr); + *ppInterface = nullptr; + // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); @@ -436,48 +288,16 @@ IFACEMETHODIMP UiaTextRange::QueryInterface(_In_ REFIID riid, _COM_Outptr_result #pragma region ITextRangeProvider -IFACEMETHODIMP UiaTextRange::Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) -{ - try - { - *ppRetVal = new UiaTextRange(*this); - } - catch (...) - { - *ppRetVal = nullptr; - return wil::ResultFromCaughtException(); - } - if (*ppRetVal == nullptr) - { - return E_OUTOFMEMORY; - } - -#if defined(_DEBUG) && defined(UIATEXTRANGE_DEBUG_MSGS) - OutputDebugString(L"Clone\n"); - std::wstringstream ss; - ss << _id << L" cloned to " << (static_cast(*ppRetVal))->_id; - std::wstring str = ss.str(); - OutputDebugString(str.c_str()); - OutputDebugString(L"\n"); -#endif - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - // tracing - /*ApiMsgClone apiMsg; - apiMsg.CloneId = static_cast(*ppRetVal)->GetId(); - Tracing::s_TraceUia(this, ApiCall::Clone, &apiMsg);*/ - - return S_OK; -} - -IFACEMETHODIMP UiaTextRange::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = FALSE; - UiaTextRange* other = static_cast(pRange); + UiaTextRangeBase* other = static_cast(pRange); if (other) { *pRetVal = !!(_start == other->GetStart() && @@ -494,13 +314,16 @@ IFACEMETHODIMP UiaTextRange::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ return S_OK; } -IFACEMETHODIMP UiaTextRange::CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, - _In_ ITextRangeProvider* pTargetRange, - _In_ TextPatternRangeEndpoint targetEndpoint, - _Out_ int* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, + _In_ ITextRangeProvider* pTargetRange, + _In_ TextPatternRangeEndpoint targetEndpoint, + _Out_ int* pRetVal) { + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); + *pRetVal = 0; + // get the text range that we're comparing to - UiaTextRange* range = static_cast(pTargetRange); + UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) { return E_INVALIDARG; @@ -543,7 +366,7 @@ IFACEMETHODIMP UiaTextRange::CompareEndpoints(_In_ TextPatternRangeEndpoint endp return S_OK; } -IFACEMETHODIMP UiaTextRange::ExpandToEnclosingUnit(_In_ TextUnit unit) +IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { @@ -589,48 +412,21 @@ IFACEMETHODIMP UiaTextRange::ExpandToEnclosingUnit(_In_ TextUnit unit) } // we don't support this currently -IFACEMETHODIMP UiaTextRange::FindAttribute(_In_ TEXTATTRIBUTEID /*textAttributeId*/, - _In_ VARIANT /*val*/, - _In_ BOOL /*searchBackward*/, - _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/) +IFACEMETHODIMP UiaTextRangeBase::FindAttribute(_In_ TEXTATTRIBUTEID /*textAttributeId*/, + _In_ VARIANT /*val*/, + _In_ BOOL /*searchBackward*/, + _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::FindAttribute, nullptr); return E_NOTIMPL; } -IFACEMETHODIMP UiaTextRange::FindText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, + _Out_ VARIANT* pRetVal) { - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - //Tracing::s_TraceUia(this, ApiCall::FindText, nullptr); - - *ppRetVal = nullptr; - try - { - // TODO GitHub #605: Search functionality - // For now, just adding it here to make UiaTextRange easier to create (Accessibility) - // We should actually abstract this out better once Windows Terminal has Search - - std::function Clone = std::bind(&UiaTextRange::Clone, this, std::placeholders::_1); - return _pData->SearchForText(text, - searchBackward, - ignoreCase, - ppRetVal, - _start, - _end, - _coordToEndpoint, - _endpointToCoord, - Clone); - } - CATCH_RETURN(); -} + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); -IFACEMETHODIMP UiaTextRange::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, - _Out_ VARIANT* pRetVal) -{ // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetAttributeValue, nullptr); if (textAttributeId == UIA_IsReadOnlyAttributeId) @@ -646,13 +442,14 @@ IFACEMETHODIMP UiaTextRange::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttribut return S_OK; } -IFACEMETHODIMP UiaTextRange::GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); *ppRetVal = nullptr; try @@ -707,19 +504,25 @@ IFACEMETHODIMP UiaTextRange::GetBoundingRectangles(_Outptr_result_maybenull_ SAF return S_OK; } -IFACEMETHODIMP UiaTextRange::GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal) { + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + *ppRetVal = nullptr; + //Tracing::s_TraceUia(this, ApiCall::GetBoundingRectangles, nullptr); return _pProvider->QueryInterface(IID_PPV_ARGS(ppRetVal)); } -IFACEMETHODIMP UiaTextRange::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); + *pRetVal = nullptr; + std::wstring wstr = L""; if (maxLength < -1) @@ -811,15 +614,16 @@ IFACEMETHODIMP UiaTextRange::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) return S_OK; } -IFACEMETHODIMP UiaTextRange::Move(_In_ TextUnit unit, - _In_ int count, - _Out_ int* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit, + _In_ int count, + _Out_ int* pRetVal) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; if (count == 0) { @@ -881,16 +685,17 @@ IFACEMETHODIMP UiaTextRange::Move(_In_ TextUnit unit, return S_OK; } -IFACEMETHODIMP UiaTextRange::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint, - _In_ TextUnit unit, - _In_ int count, - _Out_ int* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint, + _In_ TextUnit unit, + _In_ int count, + _Out_ int* pRetVal) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); + RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; if (count == 0) { @@ -948,16 +753,16 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByUnit(_In_ TextPatternRangeEndpoint en return S_OK; } -IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint endpoint, - _In_ ITextRangeProvider* pTargetRange, - _In_ TextPatternRangeEndpoint targetEndpoint) +IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoint endpoint, + _In_ ITextRangeProvider* pTargetRange, + _In_ TextPatternRangeEndpoint targetEndpoint) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { _pData->UnlockConsole(); }); - UiaTextRange* range = static_cast(pTargetRange); + UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) { return E_INVALIDARG; @@ -1058,7 +863,7 @@ IFACEMETHODIMP UiaTextRange::MoveEndpointByRange(_In_ TextPatternRangeEndpoint e return S_OK; } -IFACEMETHODIMP UiaTextRange::Select() +IFACEMETHODIMP UiaTextRangeBase::Select() { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { @@ -1090,7 +895,7 @@ IFACEMETHODIMP UiaTextRange::Select() } // we don't support this -IFACEMETHODIMP UiaTextRange::AddToSelection() +IFACEMETHODIMP UiaTextRangeBase::AddToSelection() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::AddToSelection, nullptr); @@ -1098,14 +903,14 @@ IFACEMETHODIMP UiaTextRange::AddToSelection() } // we don't support this -IFACEMETHODIMP UiaTextRange::RemoveFromSelection() +IFACEMETHODIMP UiaTextRangeBase::RemoveFromSelection() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RemoveFromSelection, nullptr); return E_NOTIMPL; } -IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) +IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) { _pData->LockConsole(); auto Unlock = wil::scope_exit([&] { @@ -1179,8 +984,7 @@ IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) try { - auto provider = static_cast(_pProvider); - provider->ChangeViewport(newViewport); + _ChangeViewport(newViewport); } CATCH_RETURN(); @@ -1193,11 +997,13 @@ IFACEMETHODIMP UiaTextRange::ScrollIntoView(_In_ BOOL alignToTop) return S_OK; } -IFACEMETHODIMP UiaTextRange::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetChildren, nullptr); + RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + // we don't have any children *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 0); if (*ppRetVal == nullptr) @@ -1209,12 +1015,12 @@ IFACEMETHODIMP UiaTextRange::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** p #pragma endregion -const COORD UiaTextRange::_getScreenBufferCoords(IUiaData* pData) +const COORD UiaTextRangeBase::_getScreenBufferCoords(IUiaData* pData) { return pData->GetTextBuffer().GetSize().Dimensions(); } -COORD UiaTextRange::_getScreenFontSize() const +COORD UiaTextRangeBase::_getScreenFontSize() const { COORD coordRet = _pData->GetFontInfo().GetSize(); @@ -1231,7 +1037,7 @@ COORD UiaTextRange::_getScreenFontSize() const // - // Return Value: // - The number of rows -const unsigned int UiaTextRange::_getTotalRows(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) { return pData->GetTextBuffer().TotalRowCount(); } @@ -1242,7 +1048,7 @@ const unsigned int UiaTextRange::_getTotalRows(IUiaData* pData) // - // Return Value: // - The row width -const unsigned int UiaTextRange::_getRowWidth(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getRowWidth(IUiaData* pData) { // make sure that we can't leak a 0 return std::max(static_cast(_getScreenBufferCoords(pData).X), 1u); @@ -1254,7 +1060,7 @@ const unsigned int UiaTextRange::_getRowWidth(IUiaData* pData) // - endpoint - the endpoint to translate // Return Value: // - the column value -const Column UiaTextRange::_endpointToColumn(IUiaData* pData, const Endpoint endpoint) +const Column UiaTextRangeBase::_endpointToColumn(IUiaData* pData, const Endpoint endpoint) { return endpoint % _getRowWidth(pData); } @@ -1265,8 +1071,8 @@ const Column UiaTextRange::_endpointToColumn(IUiaData* pData, const Endpoint end // - endpoint - the endpoint to convert // Return Value: // - the text buffer row value -const TextBufferRow UiaTextRange::_endpointToTextBufferRow(IUiaData* pData, - const Endpoint endpoint) +const TextBufferRow UiaTextRangeBase::_endpointToTextBufferRow(IUiaData* pData, + const Endpoint endpoint) { return endpoint / _getRowWidth(pData); } @@ -1278,7 +1084,7 @@ const TextBufferRow UiaTextRange::_endpointToTextBufferRow(IUiaData* pData, // - // Return Value: // - The number of rows in the range. -const unsigned int UiaTextRange::_rowCountInRange(IUiaData* pData) const +const unsigned int UiaTextRangeBase::_rowCountInRange(IUiaData* pData) const { if (_degenerate) { @@ -1302,8 +1108,8 @@ const unsigned int UiaTextRange::_rowCountInRange(IUiaData* pData) const // - row - the TextBufferRow to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row) +const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pData, + const TextBufferRow row) { const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row - firstRowIndex); @@ -1316,7 +1122,7 @@ const ScreenInfoRow UiaTextRange::_textBufferRowToScreenInfoRow(IUiaData* pData, // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) +const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) { const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); @@ -1329,8 +1135,8 @@ const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(IUiaData* pData, con // - viewport - the viewport to use for the conversion // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) +const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, + const SMALL_RECT viewport) { return row - viewport.Top; } @@ -1343,7 +1149,7 @@ const ViewportRow UiaTextRange::_screenInfoRowToViewportRow(const ScreenInfoRow // - the non-normalized row index // Return Value: // - the normalized row index -const Row UiaTextRange::_normalizeRow(IUiaData* pData, const Row row) +const Row UiaTextRangeBase::_normalizeRow(IUiaData* pData, const Row row) { const unsigned int totalRows = _getTotalRows(pData); return ((row + totalRows) % totalRows); @@ -1355,7 +1161,7 @@ const Row UiaTextRange::_normalizeRow(IUiaData* pData, const Row row) // - viewport - The viewport to measure // Return Value: // - The viewport height -const unsigned int UiaTextRange::_getViewportHeight(const SMALL_RECT viewport) +const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewport) { FAIL_FAST_IF(!(viewport.Bottom >= viewport.Top)); // + 1 because COORD is inclusive on both sides so subtracting top @@ -1369,7 +1175,7 @@ const unsigned int UiaTextRange::_getViewportHeight(const SMALL_RECT viewport) // - viewport - The viewport to measure // Return Value: // - The viewport width -const unsigned int UiaTextRange::_getViewportWidth(const SMALL_RECT viewport) +const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport) { FAIL_FAST_IF(!(viewport.Right >= viewport.Left)); @@ -1385,8 +1191,8 @@ const unsigned int UiaTextRange::_getViewportWidth(const SMALL_RECT viewport) // - row - the screen info row to check // Return Value: // - true if the row is within the bounds of the viewport -const bool UiaTextRange::_isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row) +const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, + const ScreenInfoRow row) { return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); } @@ -1398,8 +1204,8 @@ const bool UiaTextRange::_isScreenInfoRowInViewport(IUiaData* pData, // - viewport - the viewport to use for the bounds // Return Value: // - true if the row is within the bounds of the viewport -const bool UiaTextRange::_isScreenInfoRowInViewport(const ScreenInfoRow row, - const SMALL_RECT viewport) +const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, + const SMALL_RECT viewport) { ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && @@ -1412,8 +1218,8 @@ const bool UiaTextRange::_isScreenInfoRowInViewport(const ScreenInfoRow row, // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent TextBufferRow. -const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row) +const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(IUiaData* pData, + const ScreenInfoRow row) { const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row + firstRowIndex); @@ -1425,7 +1231,7 @@ const TextBufferRow UiaTextRange::_screenInfoRowToTextBufferRow(IUiaData* pData, // - row - the TextBufferRow to convert // Return Value: // - the equivalent Endpoint, starting at the beginning of the TextBufferRow. -const Endpoint UiaTextRange::_textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row) +const Endpoint UiaTextRangeBase::_textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row) { return _getRowWidth(pData) * row; } @@ -1436,8 +1242,8 @@ const Endpoint UiaTextRange::_textBufferRowToEndpoint(IUiaData* pData, const Tex // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent Endpoint. -const Endpoint UiaTextRange::_screenInfoRowToEndpoint(IUiaData* pData, - const ScreenInfoRow row) +const Endpoint UiaTextRangeBase::_screenInfoRowToEndpoint(IUiaData* pData, + const ScreenInfoRow row) { return _textBufferRowToEndpoint(pData, _screenInfoRowToTextBufferRow(pData, row)); } @@ -1448,8 +1254,8 @@ const Endpoint UiaTextRange::_screenInfoRowToEndpoint(IUiaData* pData, // - endpoint - the endpoint to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(IUiaData* pData, - const Endpoint endpoint) +const ScreenInfoRow UiaTextRangeBase::_endpointToScreenInfoRow(IUiaData* pData, + const Endpoint endpoint) { return _textBufferRowToScreenInfoRow(pData, _endpointToTextBufferRow(pData, endpoint)); } @@ -1463,9 +1269,9 @@ const ScreenInfoRow UiaTextRange::_endpointToScreenInfoRow(IUiaData* pData, // - // Notes: // - alters coords. may throw an exception. -void UiaTextRange::_addScreenInfoRowBoundaries(IUiaData* pData, - const ScreenInfoRow screenInfoRow, - _Inout_ std::vector& coords) const +void UiaTextRangeBase::_addScreenInfoRowBoundaries(IUiaData* pData, + const ScreenInfoRow screenInfoRow, + _Inout_ std::vector& coords) const { const COORD currentFontSize = _getScreenFontSize(); @@ -1501,17 +1307,8 @@ void UiaTextRange::_addScreenInfoRowBoundaries(IUiaData* pData, // convert the coords to be relative to the screen instead of // the client window - HWND hwnd = _getWindowHandle(); - - if (hwnd == nullptr) - { - // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF CLIENTTOSCREEN() - } - else - { - ClientToScreen(hwnd, &topLeft); - ClientToScreen(hwnd, &bottomRight); - } + _TranslatePointToScreen(&topLeft); + _TranslatePointToScreen(&bottomRight); const LONG width = bottomRight.x - topLeft.x; const LONG height = bottomRight.y - topLeft.y; @@ -1529,7 +1326,7 @@ void UiaTextRange::_addScreenInfoRowBoundaries(IUiaData* pData, // - // Return Value: // - the index of the first row (0-indexed) of the screen info -const unsigned int UiaTextRange::_getFirstScreenInfoRowIndex() +const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() { return 0; } @@ -1540,7 +1337,7 @@ const unsigned int UiaTextRange::_getFirstScreenInfoRowIndex() // - // Return Value: // - the index of the last row (0-indexed) of the screen info -const unsigned int UiaTextRange::_getLastScreenInfoRowIndex(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(IUiaData* pData) { return _getTotalRows(pData) - 1; } @@ -1551,7 +1348,7 @@ const unsigned int UiaTextRange::_getLastScreenInfoRowIndex(IUiaData* pData) // - // Return Value: // - the index of the first column (0-indexed) of the screen info rows -const Column UiaTextRange::_getFirstColumnIndex() +const Column UiaTextRangeBase::_getFirstColumnIndex() { return 0; } @@ -1562,7 +1359,7 @@ const Column UiaTextRange::_getFirstColumnIndex() // - // Return Value: // - the index of the last column (0-indexed) of the screen info rows -const Column UiaTextRange::_getLastColumnIndex(IUiaData* pData) +const Column UiaTextRangeBase::_getLastColumnIndex(IUiaData* pData) { return _getRowWidth(pData) - 1; } @@ -1578,11 +1375,11 @@ const Column UiaTextRange::_getLastColumnIndex(IUiaData* pData) // -1 if A < B // 1 if A > B // 0 if A == B -const int UiaTextRange::_compareScreenCoords(IUiaData* pData, - const ScreenInfoRow rowA, - const Column colA, - const ScreenInfoRow rowB, - const Column colB) +const int UiaTextRangeBase::_compareScreenCoords(IUiaData* pData, + const ScreenInfoRow rowA, + const Column colA, + const ScreenInfoRow rowB, + const Column colB) { FAIL_FAST_IF(!(rowA >= _getFirstScreenInfoRowIndex())); FAIL_FAST_IF(!(rowA <= _getLastScreenInfoRowIndex(pData))); @@ -1627,10 +1424,10 @@ const int UiaTextRange::_compareScreenCoords(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByCharacter(IUiaData* pData, - const int moveCount, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::pair UiaTextRangeBase::_moveByCharacter(IUiaData* pData, + const int moveCount, + const MoveState moveState, + _Out_ int* const pAmountMoved) { if (moveState.Direction == MovementDirection::Forward) { @@ -1642,10 +1439,10 @@ std::pair UiaTextRange::_moveByCharacter(IUiaData* pData, } } -std::pair UiaTextRange::_moveByCharacterForward(IUiaData* pData, - const int moveCount, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData* pData, + const int moveCount, + const MoveState moveState, + _Out_ int* const pAmountMoved) { *pAmountMoved = 0; int count = moveCount; @@ -1688,11 +1485,12 @@ std::pair UiaTextRange::_moveByCharacterForward(IUiaData* pD return std::make_pair(std::move(start), std::move(end)); } -std::pair UiaTextRange::_moveByCharacterBackward(IUiaData* pData, - const int moveCount, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaData* pData, + const int moveCount, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; @@ -1745,11 +1543,12 @@ std::pair UiaTextRange::_moveByCharacterBackward(IUiaData* p // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByLine(IUiaData* pData, - const int moveCount, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::pair UiaTextRangeBase::_moveByLine(IUiaData* pData, + const int moveCount, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; @@ -1792,13 +1591,14 @@ std::pair UiaTextRange::_moveByLine(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRange::_moveByDocument(IUiaData* pData, - const int /*moveCount*/, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::pair UiaTextRangeBase::_moveByDocument(IUiaData* pData, + const int /*moveCount*/, + const MoveState moveState, + _Out_ int* const pAmountMoved) { // We can't move by anything larger than a line, so move by document will apply and will // just report that it can't do that. + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; // We then have to return the same endpoints as what we initially had so nothing happens. @@ -1819,12 +1619,13 @@ std::pair UiaTextRange::_moveByDocument(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitCharacter(IUiaData* pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::tuple UiaTextRangeBase::_moveEndpointByUnitCharacter(IUiaData* pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); if (moveState.Direction == MovementDirection::Forward) { return _moveEndpointByUnitCharacterForward(pData, moveCount, endpoint, moveState, pAmountMoved); @@ -1836,12 +1637,13 @@ std::tuple UiaTextRange::_moveEndpointByUnitCharacter( } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterForward(IUiaData* pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ int* const pAmountMoved) +UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; ScreenInfoRow currentScreenInfoRow; @@ -1925,12 +1727,13 @@ UiaTextRange::_moveEndpointByUnitCharacterForward(IUiaData* pData, } std::tuple -UiaTextRange::_moveEndpointByUnitCharacterBackward(IUiaData* pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ int* const pAmountMoved) +UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; ScreenInfoRow currentScreenInfoRow; @@ -2025,12 +1828,13 @@ UiaTextRange::_moveEndpointByUnitCharacterBackward(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitLine(IUiaData* pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(IUiaData* pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; ScreenInfoRow currentScreenInfoRow; @@ -2184,12 +1988,13 @@ std::tuple UiaTextRange::_moveEndpointByUnitLine(IUiaD // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRange::_moveEndpointByUnitDocument(IUiaData* pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ int* const pAmountMoved) +std::tuple UiaTextRangeBase::_moveEndpointByUnitDocument(IUiaData* pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ int* const pAmountMoved) { + THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; Endpoint start; @@ -2251,18 +2056,18 @@ std::tuple UiaTextRange::_moveEndpointByUnitDocument(I return std::make_tuple(start, end, degenerate); } -COORD UiaTextRange::_endpointToCoord(IUiaData* pData, const Endpoint endpoint) +COORD UiaTextRangeBase::_endpointToCoord(IUiaData* pData, const Endpoint endpoint) { return { gsl::narrow(_endpointToColumn(pData, endpoint)), gsl::narrow(_endpointToScreenInfoRow(pData, endpoint)) }; } -Endpoint UiaTextRange::_coordToEndpoint(IUiaData* pData, - const COORD coord) +Endpoint UiaTextRangeBase::_coordToEndpoint(IUiaData* pData, + const COORD coord) { return _screenInfoRowToEndpoint(pData, coord.Y) + coord.X; } -RECT UiaTextRange::_getTerminalRect() const +RECT UiaTextRangeBase::_getTerminalRect() const { UiaRect result; @@ -2277,9 +2082,3 @@ RECT UiaTextRange::_getTerminalRect() const gsl::narrow(result.top + result.height) }; } - -HWND UiaTextRange::_getWindowHandle() const -{ - const auto provider = static_cast(_pProvider); - return provider->GetWindowHandle(); -} diff --git a/src/types/UiaTextRange.hpp b/src/types/UiaTextRangeBase.hpp similarity index 85% rename from src/types/UiaTextRange.hpp rename to src/types/UiaTextRangeBase.hpp index e8ff01cde43..0a82ab2dc5b 100644 --- a/src/types/UiaTextRange.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -3,7 +3,7 @@ Copyright (c) Microsoft Corporation Licensed under the MIT license. Module Name: -- UiaTextRange.hpp +- UiaTextRangeBase.hpp Abstract: - This module provides UI Automation access to the text of the console @@ -31,7 +31,7 @@ Author(s): class UiaTextRangeTests; #endif -// The UiaTextRange deals with several data structures that have +// The UiaTextRangeBase deals with several data structures that have // similar semantics. In order to keep the information from these data // structures separated, each structure has its own naming for a // row. @@ -73,7 +73,7 @@ constexpr IdType InvalidId = 0; namespace Microsoft::Console::Types { - class UiaTextRange final : public ITextRangeProvider + class UiaTextRangeBase : public ITextRangeProvider { private: static IdType id; @@ -117,7 +117,7 @@ namespace Microsoft::Console::Types MovementDirection Direction; MoveState(IUiaData* pData, - const UiaTextRange& range, + const UiaTextRangeBase& range, const MovementDirection direction); private: @@ -137,30 +137,7 @@ namespace Microsoft::Console::Types }; public: - static std::deque GetSelectionRanges(_In_ IUiaData* pData, _In_ IRawElementProviderSimple* pProvider); - - // degenerate range - static UiaTextRange* Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider); - - // degenerate range at cursor position - static UiaTextRange* Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Cursor& cursor); - - // specific endpoint range - static UiaTextRange* Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Endpoint start, - const Endpoint end, - const bool degenerate); - - // range from a UiaPoint - static UiaTextRange* Create(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const UiaPoint point); - - ~UiaTextRange(); + virtual ~UiaTextRangeBase() = default; const IdType GetId() const; const Endpoint GetStart() const; @@ -180,7 +157,7 @@ namespace Microsoft::Console::Types _COM_Outptr_result_maybenull_ void** ppInterface); // ITextRangeProvider methods - IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); + virtual IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal); IFACEMETHODIMP CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, _In_ ITextRangeProvider* pTargetRange, @@ -191,10 +168,10 @@ namespace Microsoft::Console::Types _In_ VARIANT val, _In_ BOOL searchBackward, _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); - IFACEMETHODIMP FindText(_In_ BSTR text, - _In_ BOOL searchBackward, - _In_ BOOL ignoreCase, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); + virtual IFACEMETHODIMP FindText(_In_ BSTR text, + _In_ BOOL searchBackward, + _In_ BOOL ignoreCase, + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; IFACEMETHODIMP GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, _Out_ VARIANT* pRetVal); IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); @@ -224,34 +201,31 @@ namespace Microsoft::Console::Types #endif IUiaData* const _pData; - IRawElementProviderSimple* const _pProvider; + wil::com_ptr const _pProvider; - RECT _getTerminalRect() const; - HWND _getWindowHandle() const; + virtual void _ChangeViewport(const SMALL_RECT NewWindow) = 0; + virtual void _TranslatePointToScreen(LPPOINT clientPoint) const = 0; + virtual void _TranslatePointFromScreen(LPPOINT screenPoint) const = 0; - private: // degenerate range - UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider); + UiaTextRangeBase(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider); // degenerate range at cursor position - UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Cursor& cursor); + UiaTextRangeBase(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Cursor& cursor); // specific endpoint range - UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const Endpoint start, - const Endpoint end, - const bool degenerate); + UiaTextRangeBase(_In_ IUiaData* pData, + _In_ IRawElementProviderSimple* const pProvider, + const Endpoint start, + const Endpoint end, + const bool degenerate); - // range from a UiaPoint - UiaTextRange(_In_ IUiaData* pData, - _In_ IRawElementProviderSimple* const pProvider, - const UiaPoint point); + void Initialize(_In_ const UiaPoint point); - UiaTextRange(const UiaTextRange& a); + UiaTextRangeBase(const UiaTextRangeBase& a); // used to debug objects passed back and forth // between the provider and the client @@ -283,6 +257,8 @@ namespace Microsoft::Console::Types // then both endpoints will contain the same value. bool _degenerate; + RECT _getTerminalRect() const; + static const COORD _getScreenBufferCoords(IUiaData* pData); COORD _getScreenFontSize() const; @@ -409,7 +385,7 @@ namespace Microsoft::Console::Types #endif }; - namespace UiaTextRangeTracing + namespace UiaTextRangeBaseTracing { enum class ApiCall { diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index 5b175b14e1b..8faa65ff1f9 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -5,7 +5,6 @@ #include "IUiaWindow.h" #include "WindowUiaProviderBase.hpp" -#include "ScreenInfoUiaProvider.h" using namespace Microsoft::Console::Types; diff --git a/src/types/WindowUiaProviderBase.hpp b/src/types/WindowUiaProviderBase.hpp index ac2d3838ff6..d78739a511b 100644 --- a/src/types/WindowUiaProviderBase.hpp +++ b/src/types/WindowUiaProviderBase.hpp @@ -25,7 +25,6 @@ Author(s): namespace Microsoft::Console::Types { class IUiaWindow; - class ScreenInfoUiaProvider; class WindowUiaProviderBase : public IRawElementProviderSimple, diff --git a/src/types/lib/types.vcxproj b/src/types/lib/types.vcxproj index a0ca927f641..b5c48480eff 100644 --- a/src/types/lib/types.vcxproj +++ b/src/types/lib/types.vcxproj @@ -11,8 +11,8 @@ - - + + @@ -36,8 +36,8 @@ - - + + @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/src/types/lib/types.vcxproj.filters b/src/types/lib/types.vcxproj.filters index 804d8dd5007..60c1dfbde5c 100644 --- a/src/types/lib/types.vcxproj.filters +++ b/src/types/lib/types.vcxproj.filters @@ -63,10 +63,10 @@ Source Files - + Source Files - + Source Files @@ -110,7 +110,7 @@ Header Files - + Header Files @@ -143,10 +143,7 @@ Header Files - - Header Files - - + Header Files From e92efa5bc08929601a6b11f153ca9a3fc10c9e66 Mon Sep 17 00:00:00 2001 From: MikeTheGreat Date: Tue, 20 Aug 2019 16:35:16 -0700 Subject: [PATCH 057/154] doc: svg currently doesn't work; using .jpg instead (#2443) Since the JPG won't stretch nicely we're also going to put it in the top-right corner without any scaling --- doc/user-docs/UsingJsonSettings.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/user-docs/UsingJsonSettings.md b/doc/user-docs/UsingJsonSettings.md index 6d0f78f7fa7..c00497f420d 100644 --- a/doc/user-docs/UsingJsonSettings.md +++ b/doc/user-docs/UsingJsonSettings.md @@ -99,7 +99,7 @@ The schema name can then be referenced in one or more profiles. ### Add a custom background to the WSL Debian terminal profile -1. Download the Debian SVG logo https://www.debian.org/logos/openlogo.svg +1. Download the Debian JPG logo https://www.debian.org/logos/openlogo-100.jpg 2. Put the image in the `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_\RoamingState\` directory (same directory as your `profiles.json` file). @@ -108,9 +108,10 @@ The schema name can then be referenced in one or more profiles. 3. Open your WT json properties file. 4. Under the Debian Linux profile, add the following fields: ```json - "backgroundImage": "ms-appdata:///Roaming/openlogo.jpg", - "backgroundImageOpacity": 0.3, - "backgroundImageStretchMode": "fill", + "backgroundImage": "ms-appdata:///Roaming/openlogo-100.jpg", + "backgroundImageOpacity": 1, + "backgroundImageStretchMode" : "none", + "backgroundImageAlignment" : "topRight", ``` 5. Make sure that `useAcrylic` is `false`. 6. Save the file. From be52880620fefa8701f6ee254c7faee239a3889e Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 20 Aug 2019 17:50:34 -0700 Subject: [PATCH 058/154] Accessibility: Add BoundingRects to UiaTextRanges (#2423) --- src/cascadia/TerminalControl/TermControl.cpp | 10 ++++++ src/cascadia/TerminalControl/TermControl.h | 2 ++ .../TermControlAutomationPeer.cpp | 2 +- .../TermControlUiaProvider.cpp | 18 +++++++++-- .../TermControlUiaProvider.hpp | 11 ++++++- src/cascadia/TerminalControl/UiaTextRange.cpp | 31 +++++++++++++++++-- src/cascadia/TerminalControl/UiaTextRange.hpp | 1 + src/types/UiaTextRangeBase.cpp | 2 +- src/types/UiaTextRangeBase.hpp | 2 +- 9 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 757f03b93ea..66a068b748f 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -352,6 +352,16 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation return _terminal.get(); } + const FontInfo TermControl::GetActualFont() const + { + return _actualFont; + } + + const Windows::UI::Xaml::Thickness TermControl::GetPadding() const + { + return _swapChainPanel.Margin(); + } + void TermControl::SwapChainChanged() { if (!_initializedTerminal) diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 969584407cd..14a9e4045bf 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -73,6 +73,8 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation Windows::UI::Xaml::Automation::Peers::AutomationPeer OnCreateAutomationPeer(); ::Microsoft::Console::Types::IUiaData* GetUiaData() const; + const FontInfo GetActualFont() const; + const Windows::UI::Xaml::Thickness GetPadding() const; static Windows::Foundation::Point GetProposedDimensions(Microsoft::Terminal::Settings::IControlSettings const& settings, const uint32_t dpi); diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index dcd677752b9..585eb32cfa4 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -29,7 +29,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { TermControlAutomationPeer::TermControlAutomationPeer(winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& owner) : TermControlAutomationPeerT(owner), // pass owner to FrameworkElementAutomationPeer - _uiaProvider{ owner.GetUiaData(), std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; + _uiaProvider{ owner, std::bind(&TermControlAutomationPeer::GetBoundingRectWrapped, this) } {}; winrt::hstring TermControlAutomationPeer::GetClassNameCore() const { diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.cpp b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp index eaac4ad4f7d..19298e86214 100644 --- a/src/cascadia/TerminalControl/TermControlUiaProvider.cpp +++ b/src/cascadia/TerminalControl/TermControlUiaProvider.cpp @@ -3,14 +3,16 @@ #include "pch.h" #include "TermControlUiaProvider.hpp" +#include "TermControl.h" using namespace Microsoft::Terminal; using namespace Microsoft::Console::Types; -TermControlUiaProvider::TermControlUiaProvider(_In_ IUiaData* pData, +TermControlUiaProvider::TermControlUiaProvider(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& termControl, _In_ std::function GetBoundingRect) : _getBoundingRect(GetBoundingRect), - ScreenInfoUiaProviderBase(THROW_HR_IF_NULL(E_INVALIDARG, pData)) + _termControl(termControl), + ScreenInfoUiaProviderBase(THROW_HR_IF_NULL(E_INVALIDARG, termControl.GetUiaData())) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); @@ -78,7 +80,17 @@ IFACEMETHODIMP TermControlUiaProvider::get_FragmentRoot(_COM_Outptr_result_maybe return S_OK; } -std::deque TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) +const COORD TermControlUiaProvider::GetFontSize() const +{ + return _termControl.GetActualFont().GetSize(); +} + +const winrt::Windows::UI::Xaml::Thickness TermControlUiaProvider::GetPadding() const +{ + return _termControl.GetPadding(); +} + +std::deque TermControlUiaProvider::GetSelectionRanges(_In_ IRawElementProviderSimple* const pProvider) { std::deque result; diff --git a/src/cascadia/TerminalControl/TermControlUiaProvider.hpp b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp index e536fb93960..b71211502ba 100644 --- a/src/cascadia/TerminalControl/TermControlUiaProvider.hpp +++ b/src/cascadia/TerminalControl/TermControlUiaProvider.hpp @@ -23,12 +23,17 @@ Author(s): #include "..\types\UiaTextRangeBase.hpp" #include "UiaTextRange.hpp" +namespace winrt::Microsoft::Terminal::TerminalControl::implementation +{ + struct TermControl; +} + namespace Microsoft::Terminal { class TermControlUiaProvider : public Microsoft::Console::Types::ScreenInfoUiaProviderBase { public: - TermControlUiaProvider(_In_ Microsoft::Console::Types::IUiaData* pData, + TermControlUiaProvider(_In_ winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& termControl, _In_ std::function GetBoundingRect); // IRawElementProviderFragment methods @@ -37,6 +42,9 @@ namespace Microsoft::Terminal IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override; IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override; + const COORD GetFontSize() const; + const winrt::Windows::UI::Xaml::Thickness GetPadding() const; + protected: std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) override; @@ -59,5 +67,6 @@ namespace Microsoft::Terminal private: std::function _getBoundingRect; + winrt::Microsoft::Terminal::TerminalControl::implementation::TermControl const& _termControl; }; } diff --git a/src/cascadia/TerminalControl/UiaTextRange.cpp b/src/cascadia/TerminalControl/UiaTextRange.cpp index 5eb9c8ac121..93133347a7e 100644 --- a/src/cascadia/TerminalControl/UiaTextRange.cpp +++ b/src/cascadia/TerminalControl/UiaTextRange.cpp @@ -186,12 +186,39 @@ void UiaTextRange::_ChangeViewport(const SMALL_RECT /*NewWindow*/) // TODO GitHub #2361: Update viewport when calling UiaTextRangeBase::ScrollIntoView() } -void UiaTextRange::_TranslatePointToScreen(LPPOINT /*clientPoint*/) const +// Method Description: +// - Transform coordinates relative to the client to relative to the screen +// Arguments: +// - clientPoint: coordinates relative to the client where +// (0,0) is the top-left of the app window +// Return Value: +// - +void UiaTextRange::_TranslatePointToScreen(LPPOINT clientPoint) const { - // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF CLIENTTOSCREEN() + auto provider = static_cast(_pProvider.get()); + + // update based on TermControl location (important for Panes) + UiaRect boundingRect; + THROW_IF_FAILED(provider->get_BoundingRectangle(&boundingRect)); + clientPoint->x += gsl::narrow(boundingRect.left); + clientPoint->y += gsl::narrow(boundingRect.top); + + // update based on TermControl padding + auto padding = provider->GetPadding(); + clientPoint->x += gsl::narrow(padding.Left); + clientPoint->y += gsl::narrow(padding.Top); } void UiaTextRange::_TranslatePointFromScreen(LPPOINT /*screenPoint*/) const { // TODO GitHub #2103: NON-HWND IMPLEMENTATION OF SCREENTOCLIENT() } + +const COORD UiaTextRange::_getScreenFontSize() const +{ + // Do NOT get the font info from IRenderData. It is a dummy font info. + // Instead, the font info is saved in the TermControl. So we have to + // ask our parent to get it for us. + auto provider = static_cast(_pProvider.get()); + return provider->GetFontSize(); +} diff --git a/src/cascadia/TerminalControl/UiaTextRange.hpp b/src/cascadia/TerminalControl/UiaTextRange.hpp index 7e561e3f971..020373b4302 100644 --- a/src/cascadia/TerminalControl/UiaTextRange.hpp +++ b/src/cascadia/TerminalControl/UiaTextRange.hpp @@ -57,6 +57,7 @@ namespace Microsoft::Terminal void _ChangeViewport(const SMALL_RECT NewWindow) override; void _TranslatePointToScreen(LPPOINT clientPoint) const override; void _TranslatePointFromScreen(LPPOINT screenPoint) const override; + const COORD _getScreenFontSize() const override; private: // degenerate range diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 845ce21042c..47e7d502f32 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -1020,7 +1020,7 @@ const COORD UiaTextRangeBase::_getScreenBufferCoords(IUiaData* pData) return pData->GetTextBuffer().GetSize().Dimensions(); } -COORD UiaTextRangeBase::_getScreenFontSize() const +const COORD UiaTextRangeBase::_getScreenFontSize() const { COORD coordRet = _pData->GetFontInfo().GetSize(); diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 0a82ab2dc5b..1d1f957af3f 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -260,7 +260,7 @@ namespace Microsoft::Console::Types RECT _getTerminalRect() const; static const COORD _getScreenBufferCoords(IUiaData* pData); - COORD _getScreenFontSize() const; + virtual const COORD _getScreenFontSize() const; static const unsigned int _getTotalRows(IUiaData* pData); static const unsigned int _getRowWidth(IUiaData* pData); From d1a3e6d2b8d5fd01b46f0021976888073a3ce024 Mon Sep 17 00:00:00 2001 From: Nathan Metzger Date: Tue, 20 Aug 2019 22:16:16 -0400 Subject: [PATCH 059/154] doc: startingDirectory formatting note (#2415) --- doc/user-docs/UsingJsonSettings.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/user-docs/UsingJsonSettings.md b/doc/user-docs/UsingJsonSettings.md index c00497f420d..58e26fa9cd1 100644 --- a/doc/user-docs/UsingJsonSettings.md +++ b/doc/user-docs/UsingJsonSettings.md @@ -71,6 +71,8 @@ Example settings include .... ``` +> 👉 **Note**: To use backslashes in any path field, you'll need to escape them following JSON escaping rules (they should look like `\\`). As an alternative, you can use forward slashes. + The profile GUID is used to reference the default profile in the global settings. The values for background image stretch mode are documented [here](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.media.stretch) From 6d50fb4d3176e2903b60884a1a67db1a187a416d Mon Sep 17 00:00:00 2001 From: Paul-00910 <51178702+Paul-00910@users.noreply.github.com> Date: Wed, 21 Aug 2019 18:14:55 +0200 Subject: [PATCH 060/154] doc: More clear path instructions (#2497) --- doc/user-docs/UsingJsonSettings.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user-docs/UsingJsonSettings.md b/doc/user-docs/UsingJsonSettings.md index 58e26fa9cd1..7a281cddb5a 100644 --- a/doc/user-docs/UsingJsonSettings.md +++ b/doc/user-docs/UsingJsonSettings.md @@ -67,11 +67,11 @@ Example settings include "fontSize" : 9, "guid" : "{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}", "name" : "Debian", - "startingDirectory" : "%USERPROFILE%/wslhome" + "startingDirectory" : "%USERPROFILE%\\wslhome" .... ``` -> 👉 **Note**: To use backslashes in any path field, you'll need to escape them following JSON escaping rules (they should look like `\\`). As an alternative, you can use forward slashes. +> 👉 **Note**: To use backslashes in any path field, you'll need to escape them following JSON escaping rules (like shown above). As an alternative, you can use forward slashes ("%USERPROFILE%/wslhome"). The profile GUID is used to reference the default profile in the global settings. From 84d19f53484d183b5d28a7308277ac676d748d7c Mon Sep 17 00:00:00 2001 From: brightbluejay Date: Wed, 21 Aug 2019 18:38:27 +0100 Subject: [PATCH 061/154] doc: Update bot.md (#2500) minor spelling correction --- doc/bot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/bot.md b/doc/bot.md index 0f1864e70d9..fad0cca928f 100644 --- a/doc/bot.md +++ b/doc/bot.md @@ -8,7 +8,7 @@ We'll be using tags, primarily, to help us understand what needs attention, what ### Quick-Guidance to Core Contributors 1. Look at `Needs-Attention` as top priority 1. Look at `Needs-Triage` during triage meetings to get a handle on what's new and sort it out -1. Look at `Needs-Tag-Fix` when you have a few minutes to fix up things tagged impoperly +1. Look at `Needs-Tag-Fix` when you have a few minutes to fix up things tagged improperly 1. Manually add `Needs-Author-Feedback` when there's something we need the author to follow up on and want attention if they return it or an auto-close for inactivity if it goes stale. ### Tagging/Process Details From 5694606aea95f1f60c7e265356303eb4d126fd2a Mon Sep 17 00:00:00 2001 From: brightbluejay Date: Wed, 21 Aug 2019 18:38:51 +0100 Subject: [PATCH 062/154] doc: Update submitting_code.md (#2499) Minor spelling corrections --- doc/submitting_code.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/submitting_code.md b/doc/submitting_code.md index 24442bceb97..2e81141b027 100644 --- a/doc/submitting_code.md +++ b/doc/submitting_code.md @@ -3,7 +3,7 @@ In Openconsole, `dev/main` is the master branch for the repo. -Any branch that begins with `dev/` is recognized by our CI system and will automatically run x86 and amd64 builds and run our unit and feature tests. For feature branchs the pattern we use is `dev//`. ex. `dev/austdi/SomeCoolUnicodeFeature`. The important parts are the dev prefix and your alias. +Any branch that begins with `dev/` is recognized by our CI system and will automatically run x86 and amd64 builds and run our unit and feature tests. For feature branches the pattern we use is `dev//`. ex. `dev/austdi/SomeCoolUnicodeFeature`. The important parts are the dev prefix and your alias. `inbox` is a special branch that coordinates Openconsole code to the main OS repo. @@ -15,12 +15,12 @@ Because we build outside of the OS repo, we need a way to get code back into it ## What to do when cherry-picking to inbox fails -Sometimes VSTS doesn't want to allow a cherry pick to the inbox branch. It might have a valid reason or it might just be finicky. You'll need to complete the merge manually on a local machine. The steps are: +Sometimes VSTS doesn't want to allow a cherry pick to the inbox branch. It might have a valid reason, or it might just be finicky. You'll need to complete the merge manually on a local machine. The steps are: 1. make sure you have pulled the latest commits for the `dev/main` and `inbox` branches 2. make a new branch from inbox 3. cherry-pick the commits from the PR to the newly created branch (this is easier if you squashed your commits when you merged into `dev/main` -4. fix any merge conficts and commit +4. fix any merge conflicts and commit 5. push the new branch to the remote 6. create a new PR of that branch in `inbox` 7. complete PR and continue on to completing the auto-created PR in the OS repo From 9ff90ba17469788a20b498b6482496b973bc7dee Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Wed, 21 Aug 2019 14:55:28 -0700 Subject: [PATCH 063/154] az: Introduce a "credential version" to force old credentials to be deleted (#2492) When we change the client ID, we're going to need to force people to log in again. We can do that either by: 1. Trying to log in and refresh the user's token and failing (displaying a cryptic message like "you aren't on the internet, please get on the internet"), **OR** by... 2. Getting out ahead of it, detecting when we would have failed for client ID (and other) reasons, and _not trying at all._ This is option 2. --- .../TerminalConnection/AzureConnection.cpp | 38 ++++++++++++++++--- .../AzureConnectionStrings.h | 1 + 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index ea5ec4274ac..b4f920688be 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -23,6 +23,8 @@ using namespace web::websockets::client; using namespace concurrency::streams; using namespace winrt::Windows::Security::Credentials; +static constexpr int CurrentCredentialVersion = 1; + namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { // This file only builds for non-ARM64 so we don't need to check that here @@ -387,6 +389,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - E_FAIL if the user closes the tab HRESULT AzureConnection::_AccessHelper() { + bool oldVersionEncountered = false; auto vault = PasswordVault(); winrt::Windows::Foundation::Collections::IVectorView credList; // FindAllByResource throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block @@ -400,13 +403,37 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _state = State::DeviceFlow; return S_FALSE; } - _maxStored = credList.Size(); - // Display the user's saved connection settings - for (int i = 0; i < _maxStored; i++) + _maxStored = 0; + for (const auto& entry : credList) { - auto entry = credList.GetAt(i); auto nameJson = json::value::parse(entry.UserName().c_str()); - _outputHandlers(_StrFormatHelper(ithTenant, i, nameJson.at(L"displayName").as_string().c_str(), nameJson.at(L"tenantID").as_string().c_str())); + std::optional credentialVersion; + if (nameJson.has_integer_field(U("ver"))) + { + credentialVersion = nameJson.at(U("ver")).as_integer(); + } + + if (!credentialVersion.has_value() || credentialVersion.value() != CurrentCredentialVersion) + { + // ignore credentials that aren't from the latest credential revision + vault.Remove(entry); + oldVersionEncountered = true; + continue; + } + + _outputHandlers(_StrFormatHelper(ithTenant, _maxStored, nameJson.at(L"displayName").as_string().c_str(), nameJson.at(L"tenantID").as_string().c_str())); + _maxStored++; + } + + if (!_maxStored) + { + if (oldVersionEncountered) + { + _outputHandlers(winrt::to_hstring(oldCredentialsFlushedMessage)); + } + // No valid up-to-date credentials were found, so start the device flow + _state = State::DeviceFlow; + return S_FALSE; } _outputHandlers(winrt::to_hstring(enterTenant)); @@ -846,6 +873,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { auto vault = PasswordVault(); json::value userName; + userName[U("ver")] = CurrentCredentialVersion; userName[U("displayName")] = json::value::string(_displayName); userName[U("tenantID")] = json::value::string(_tenantID); json::value passWord; diff --git a/src/cascadia/TerminalConnection/AzureConnectionStrings.h b/src/cascadia/TerminalConnection/AzureConnectionStrings.h index 7686df840ed..75eaf022d93 100644 --- a/src/cascadia/TerminalConnection/AzureConnectionStrings.h +++ b/src/cascadia/TerminalConnection/AzureConnectionStrings.h @@ -26,5 +26,6 @@ const auto tokensRemoved = L"Tokens removed!\r\n"; const auto exitStr = L"Exit.\r\n"; const auto authString = L"Authenticated.\r\n"; const auto internetOrServerIssue = L"Could not connect to Azure. You may not have internet or the server might be down.\r\n"; +const auto oldCredentialsFlushedMessage = L"Authentication parameters changed. You'll need to log in again.\r\n"; const auto ithTenant = L"Tenant %d: %s (%s)\r\n"; From 1006e9878038acfc7609765fae83d3b231fc8506 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Thu, 22 Aug 2019 12:05:18 -0700 Subject: [PATCH 064/154] az: Don't fail when a tenant doesn't have a knowable name (#2508) On occasion, in certain delegated access scenarios, we'll fail to read the name of one or more of the user's Azure tenants. We would summarily explode (because we're being strict about our incoming JSON, and we didn't know that this was possible.) Now we'll substitute in an alternate name and present the ID. Fixes #2249. * Update src/cascadia/TerminalConnection/AzureConnection.cpp --- .../TerminalConnection/AzureConnection.cpp | 27 +++++++++++++++---- .../AzureConnectionStrings.h | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index b4f920688be..029e2986acb 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -274,6 +274,20 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } } + // Method description: + // - This method returns a tenant's ID and display name (if one is available). + // If there is no display name, a placeholder is returned in its stead. + // Arguments: + // - tenant - the unparsed tenant + // Return value: + // - a tuple containing the ID and display name of the tenant. + static std::tuple _crackTenant(const json::value& tenant) + { + auto tenantId{ tenant.at(L"tenantId").as_string() }; + auto displayName{ tenant.has_string_field(L"displayName") ? tenant.at(L"displayName").as_string() : unknownTenantName }; + return { tenantId, displayName }; + } + // Method description: // - this method bridges the thread to the Azure connection instance // Arguments: @@ -535,8 +549,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation } else if (_tenantList.size() == 1) { - _tenantID = tenantListAsArray.at(0).at(L"tenantId").as_string(); - _displayName = tenantListAsArray.at(0).at(L"displayName").as_string(); + const auto& chosenTenant = tenantListAsArray.at(0); + std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant); // We have to refresh now that we have the tenantID const auto refreshResponse = _RefreshTokens(); @@ -564,7 +578,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation _maxSize = tenantListAsArray.size(); for (int i = 0; i < _maxSize; i++) { - _outputHandlers(_StrFormatHelper(ithTenant, i, tenantListAsArray.at(i).at(L"displayName").as_string().c_str(), tenantListAsArray.at(i).at(L"tenantId").as_string().c_str())); + const auto& tenant = tenantListAsArray.at(i); + const auto [tenantId, tenantDisplayName] = _crackTenant(tenant); + _outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str())); } _outputHandlers(winrt::to_hstring(enterTenant)); // Use a lock to wait for the user to input a valid number @@ -577,8 +593,9 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation { return E_FAIL; } - _tenantID = tenantListAsArray.at(_tenantNumber).at(L"tenantId").as_string(); - _displayName = tenantListAsArray.at(_tenantNumber).at(L"displayName").as_string(); + + const auto& chosenTenant = tenantListAsArray.at(_tenantNumber); + std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant); // We have to refresh now that we have the tenantID const auto refreshResponse = _RefreshTokens(); diff --git a/src/cascadia/TerminalConnection/AzureConnectionStrings.h b/src/cascadia/TerminalConnection/AzureConnectionStrings.h index 75eaf022d93..ec75d954bb7 100644 --- a/src/cascadia/TerminalConnection/AzureConnectionStrings.h +++ b/src/cascadia/TerminalConnection/AzureConnectionStrings.h @@ -28,4 +28,5 @@ const auto authString = L"Authenticated.\r\n"; const auto internetOrServerIssue = L"Could not connect to Azure. You may not have internet or the server might be down.\r\n"; const auto oldCredentialsFlushedMessage = L"Authentication parameters changed. You'll need to log in again.\r\n"; +const auto unknownTenantName = L""; const auto ithTenant = L"Tenant %d: %s (%s)\r\n"; From e7c78c8d283da65a8bc374ac50f52ad4234e0854 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Thu, 22 Aug 2019 15:38:30 -0700 Subject: [PATCH 065/154] Update package version to 0.4 --- src/cascadia/CascadiaPackage/CascadiaPackage.wapproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index c5be3f34839..6c3839f370f 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -5,7 +5,7 @@ 0 - 3 + 4 10.0.18362.0 From 949839fdd84e4de6421afb55c36dd493ebad7724 Mon Sep 17 00:00:00 2001 From: brightbluejay Date: Fri, 23 Aug 2019 18:55:28 +0100 Subject: [PATCH 066/154] doc: Update Keybindings-Arguments.md (#2498) Minor grammar and spelling changes --- doc/cascadia/Keybindings-Arguments.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/cascadia/Keybindings-Arguments.md b/doc/cascadia/Keybindings-Arguments.md index 1dcd08ba1ad..1d187caef9a 100644 --- a/doc/cascadia/Keybindings-Arguments.md +++ b/doc/cascadia/Keybindings-Arguments.md @@ -83,9 +83,9 @@ singular `newTabProfile` action, and that action requires a `profileIndex` in the `args` object. Also, pay attention to the last set of keybindings, the `splitProfile` ones. -This is a function that requires two arguments, both a `orientation` and a +This is a function that requires two arguments, both an `orientation` and a `profileIndex`. Before this change we would have needed to create 20 separate -actions (10 profile indicies * 2 directions) to handle these cases. Now it can +actions (10 profile indices * 2 directions) to handle these cases. Now it can be done with a single action that can be much more flexible in its implementation. @@ -102,7 +102,7 @@ build `IActionEventArgs` using the `IActionArgs` to set all the parameter values All current keybinding events will be changed from their current types to `TypedEventHandler`s. These `TypedEventHandler`s second param will always be an -instance of `IActionEventArgs`. So for example: +instance of `IActionEventArgs`. So, for example: ```csharp @@ -202,7 +202,7 @@ the event handlers with the `IActionArgs` we've stored in the map with the Then, in `App`, we'll handle each of these events. We set up lambdas as event handlers for each event in `App::_HookupKeyBindings`. In each of those -functions, We'll inspect the `IActionArgs` parameter, and use args from its +functions, we'll inspect the `IActionArgs` parameter, and use args from its implementation to call callbacks in the `App` class. We will update `App` to have methods defined with the actual keybinding function signatures. @@ -235,7 +235,7 @@ The code will look like: ### Handling Keybinding Events -Commmon to all implementations of `IActionArgs` is the `Handled` property. This +Common to all implementations of `IActionArgs` is the `Handled` property. This will let the app indicate if it was able to actually process a keybinding event or not. While in the large majority of cases, the events will all be marked handled, there are some scenarios where the Terminal will need to know if the @@ -291,7 +291,7 @@ We'll need to make sure that invalid keybindings are ignored. Currently, we already gracefully ignore keybindings that have invalid `keys` or invalid `commands`. We'll need to add additional validation on invalid sets of `args`. When we're parsing the args from a Json blob, we'll make sure to only ever look -for keys we're expecting, and ignore everything else. +for keys we're expecting and ignore everything else. If a keybinding requires certain args, but those args are not provided, we'll need to make sure those args each have reasonable default values to use. If for @@ -353,7 +353,7 @@ N/A them quickly. - [1] We probably won't be able to use the `ActionAndArgs` class directly, since that class is specific to the actions we define. We'll need another - way for extenstions to be able to uniquely identify their own actions. + way for extensions to be able to uniquely identify their own actions. ## Resources From 02d8df843166844f14c6dfce10b8726089f8665c Mon Sep 17 00:00:00 2001 From: Marcel Freiberg Date: Fri, 23 Aug 2019 19:56:26 +0200 Subject: [PATCH 067/154] Don't treat the Windows keys as input (#2514) Fixes #2506. --- src/cascadia/TerminalControl/TermControl.cpp | 5 ++++- src/cascadia/TerminalCore/Terminal.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 66a068b748f..5628d037330 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -622,7 +622,10 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation if (_closing || e.OriginalKey() == VirtualKey::Control || e.OriginalKey() == VirtualKey::Shift || - e.OriginalKey() == VirtualKey::Menu) + e.OriginalKey() == VirtualKey::Menu || + e.OriginalKey() == VirtualKey::LeftWindows || + e.OriginalKey() == VirtualKey::RightWindows) + { e.Handled(true); return; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 4e01f033af1..f89544ca638 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -196,7 +196,7 @@ void Terminal::Write(std::wstring_view stringView) // - Send this particular key event to the terminal. The terminal will translate // the key and the modifiers pressed into the appropriate VT sequence for that // key chord. If we do translate the key, we'll return true. In that case, the -// event should NOT br processed any further. If we return false, the event +// event should NOT be processed any further. If we return false, the event // was NOT translated, and we should instead use the event to try and get the // real character out of the event. // Arguments: From ebcf8126dc0c212d18638f8650ea757fa150ba17 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Mon, 26 Aug 2019 10:21:10 -0700 Subject: [PATCH 068/154] connection: start up the output thread _only after_ all the pipes are up (#2528) Fixes #2527 --- .../TerminalConnection/ConhostConnection.cpp | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/cascadia/TerminalConnection/ConhostConnection.cpp b/src/cascadia/TerminalConnection/ConhostConnection.cpp index a5bea4f608f..3da9d43f913 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.cpp +++ b/src/cascadia/TerminalConnection/ConhostConnection.cpp @@ -81,17 +81,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation extraEnvVars.emplace(L"WT_SESSION", pwszGuid); } - // Create our own output handling thread - // Each connection needs to make sure to drain the output from its backing host. - _hOutputThread.reset(CreateThread(nullptr, - 0, - StaticOutputThreadProc, - this, - 0, - nullptr)); - - THROW_LAST_ERROR_IF_NULL(_hOutputThread); - STARTUPINFO si = { 0 }; si.cb = sizeof(STARTUPINFOW); @@ -119,6 +108,18 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation si, extraEnvVars)); + // Create our own output handling thread + // This must be done after the pipes are populated. + // Each connection needs to make sure to drain the output from its backing host. + _hOutputThread.reset(CreateThread(nullptr, + 0, + StaticOutputThreadProc, + this, + 0, + nullptr)); + + THROW_LAST_ERROR_IF_NULL(_hOutputThread); + _connected = true; } From cffa0331166446e08c8eb7af298c5409a545115d Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 26 Aug 2019 12:21:30 -0500 Subject: [PATCH 069/154] When we reload a profile, always use the same GUID for it (#2542) This ensures that settings reload works for profiles w/o GUIDs --- src/cascadia/TerminalApp/Profile.cpp | 5 ++++- src/cascadia/TerminalApp/Profile.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/Profile.cpp b/src/cascadia/TerminalApp/Profile.cpp index 5cd57f8a855..5863c3aae38 100644 --- a/src/cascadia/TerminalApp/Profile.cpp +++ b/src/cascadia/TerminalApp/Profile.cpp @@ -357,7 +357,10 @@ Profile Profile::FromJson(const Json::Value& json) } else { - result._guid = Utils::CreateGuid(); + // Always use the name to generate the temporary GUID. That way, across + // reloads, we'll generate the same static GUID. + const std::wstring_view name = result._name; + result._guid = Utils::CreateV5Uuid(RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID, gsl::as_bytes(gsl::make_span(name))); TraceLoggingWrite( g_hTerminalAppProvider, diff --git a/src/cascadia/TerminalApp/Profile.h b/src/cascadia/TerminalApp/Profile.h index 7b1627ab08a..990bbc728c0 100644 --- a/src/cascadia/TerminalApp/Profile.h +++ b/src/cascadia/TerminalApp/Profile.h @@ -16,6 +16,10 @@ Author(s): #pragma once #include "ColorScheme.h" +// GUID used for generating GUIDs at runtime, for profiles that did not have a +// GUID specified manually. +constexpr GUID RUNTIME_GENERATED_PROFILE_NAMESPACE_GUID = { 0xf65ddb7e, 0x706b, 0x4499, { 0x8a, 0x50, 0x40, 0x31, 0x3c, 0xaf, 0x51, 0x0a } }; + namespace TerminalApp { class Profile; From 974e95ebf7704b08da51c1f728ab2bc06108881f Mon Sep 17 00:00:00 2001 From: James Holderness Date: Wed, 28 Aug 2019 02:45:38 +0100 Subject: [PATCH 070/154] Make the RIS command clear the display and scrollback correctly (#2367) When the scrollback buffer is empty, the RIS escape sequence (Reset to Initial State) will fail to clear the screen, or reset any of the state. And when there is something in the scrollback, it doesn't get cleared completely, and the screen may get filled with the wrong background color (it should use the default color, but it actually uses the previously active background color). This commit attempts to fix those issues. The initial failure is caused by the `SCREEN_INFORMATION::WriteRect` method throwing an exception when passed an empty viewport. And the reason it's passed an empty viewport is because that's what the `Viewport::Subtract` method returns when the result of the subtraction is nothing. The PR fixes the problem by making the `Viewport::Subtract` method actually return nothing in that situation. This is a change in the defined behavior that also required the associated viewport tests to be updated. However, it does seem a sensible change, since the `Subtract` method never returns empty viewports under any other circumstances. And the only place the method seems to be used is in the `ScrollRegion` implementation, where the previous behavior is guaranteed to throw an exception. The other issues are fixed simply by changing the order in which things are reset in the `AdaptDispatch::HardReset` method. The call to `SoftReset` needed to be made first, so that the SGR attributes would be reset before the screen was cleared, thus making sure that the default background color would be used. And the screen needed to be cleared before the scrollback was erased, otherwise the last view of the screen would be retained in the scrollback buffer. These changes also required existing adapter tests to be updated, but not because of a change in the expected behaviour. It's just that certain tests relied on the `SoftReset` happening later in the order, so weren't expecting it to be called if say the scrollback erase had failed. It doesn't seem like the tests were deliberately trying to verify that the SoftReset _hadn't_ been called. In addition to the updates to existing tests, this PR also add a new screen buffer test which verifies the display and scrollback are correctly cleared under the conditions that were previously failing. Fixes #2307. --- src/host/ut_host/ScreenBufferTests.cpp | 65 +++++++++++++++++++ src/host/ut_host/ViewportTests.cpp | 12 +--- src/terminal/adapter/adaptDispatch.cpp | 9 +-- .../adapter/ut_adapter/adapterTest.cpp | 48 ++++++++------ src/types/viewport.cpp | 56 +++++++--------- 5 files changed, 123 insertions(+), 67 deletions(-) diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index aa56b1b4806..94d77faaff9 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -168,6 +168,8 @@ class ScreenBufferTests TEST_METHOD(ReverseLineFeedInMargins); TEST_METHOD(SetOriginMode); + + TEST_METHOD(HardResetBuffer); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -3516,3 +3518,66 @@ void ScreenBufferTests::SetOriginMode() // Reset DECOM so we don't affect future tests stateMachine.ProcessString(L"\x1B[?6l"); } + +void ScreenBufferTests::HardResetBuffer() +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto& stateMachine = si.GetStateMachine(); + const auto& viewport = si.GetViewport(); + const auto& cursor = si.GetTextBuffer().GetCursor(); + WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + auto isBufferClear = [&]() { + auto offset = 0; + auto width = si.GetBufferSize().Width(); + for (auto iter = si.GetCellDataAt({}); iter; ++iter, ++offset) + { + if (iter->Chars() != L" " || iter->TextAttr() != TextAttribute{}) + { + Log::Comment(NoThrowString().Format( + L"Buffer not clear at (X:%d, Y:%d)", + offset % width, + offset / width)); + return false; + } + } + return true; + }; + + const auto resetToInitialState = L"\033c"; + + Log::Comment(L"Start with a clear buffer, viewport and cursor at 0,0"); + si.SetAttributes(TextAttribute()); + si.ClearTextData(); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, { 0, 0 }, true)); + VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, 0 }, true)); + VERIFY_IS_TRUE(isBufferClear()); + + Log::Comment(L"Write a single line of text to the buffer"); + stateMachine.ProcessString(L"Hello World!\n"); + VERIFY_IS_FALSE(isBufferClear()); + VERIFY_ARE_EQUAL(COORD({ 0, 1 }), cursor.GetPosition()); + + Log::Comment(L"After a reset, buffer should be clear, with cursor at 0,0"); + stateMachine.ProcessString(resetToInitialState); + VERIFY_IS_TRUE(isBufferClear()); + VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition()); + + Log::Comment(L"Set the background color to red"); + stateMachine.ProcessString(L"\x1b[41m"); + Log::Comment(L"Write multiple pages of text to the buffer"); + for (auto i = 0; i < viewport.Height() * 2; i++) + { + stateMachine.ProcessString(L"Hello World!\n"); + } + VERIFY_IS_FALSE(isBufferClear()); + VERIFY_IS_GREATER_THAN(viewport.Top(), viewport.Height()); + VERIFY_IS_GREATER_THAN(cursor.GetPosition().Y, viewport.Height()); + + Log::Comment(L"After a reset, buffer should be clear, with viewport and cursor at 0,0"); + stateMachine.ProcessString(resetToInitialState); + VERIFY_IS_TRUE(isBufferClear()); + VERIFY_ARE_EQUAL(COORD({ 0, 0 }), viewport.Origin()); + VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition()); +} diff --git a/src/host/ut_host/ViewportTests.cpp b/src/host/ut_host/ViewportTests.cpp index d82028d9a53..c5bdc3aa9e6 100644 --- a/src/host/ut_host/ViewportTests.cpp +++ b/src/host/ut_host/ViewportTests.cpp @@ -1044,18 +1044,8 @@ class ViewportTests const auto original = Viewport::FromInclusive(srOriginal); const auto remove = original; - std::vector expected; - expected.emplace_back(Viewport::FromDimensions(original.Origin(), { 0, 0 })); - const auto actual = Viewport::Subtract(original, remove); - VERIFY_ARE_EQUAL(expected.size(), actual.size(), L"Same number of viewports in expected and actual"); - Log::Comment(L"Now validate that each viewport has the expected area."); - for (size_t i = 0; i < expected.size(); i++) - { - const auto& exp = expected.at(i); - const auto& act = actual.at(i); - VERIFY_ARE_EQUAL(exp, act); - } + VERIFY_ARE_EQUAL(0u, actual.size(), L"There should be no viewports returned"); } }; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 9a045213a3c..4b853910dd0 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -1601,17 +1601,18 @@ bool AdaptDispatch::SoftReset() // True if handled successfully. False otherwise. bool AdaptDispatch::HardReset() { + // Sets the SGR state to normal - this must be done before EraseInDisplay + // to ensure that it clears with the default background color. + bool fSuccess = SoftReset(); + // Clears the screen - Needs to be done in two operations. - bool fSuccess = _EraseScrollback(); if (fSuccess) { fSuccess = EraseInDisplay(DispatchTypes::EraseType::All); } - - // Sets the SGR state to normal. if (fSuccess) { - fSuccess = SoftReset(); + fSuccess = _EraseScrollback(); } // Cursor to 1,1 - the Soft Reset guarantees this is absolute diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index 5c584a82998..bc65cae83ee 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -3426,30 +3426,33 @@ class AdapterTest // The cursor will be moved to the same relative location in the new viewport with origin @ 0, 0 const COORD coordRelativeCursor = { _testGetSet->_coordCursorPos.X - _testGetSet->_srViewport.Left, _testGetSet->_coordCursorPos.Y - _testGetSet->_srViewport.Top }; - - // Cursor to 1,1 - _testGetSet->_coordExpectedCursorPos = { 0, 0 }; - _testGetSet->_fSetConsoleCursorPositionResult = true; - _testGetSet->_fPrivateSetLegacyAttributesResult = true; - _testGetSet->_fPrivateSetDefaultAttributesResult = true; - _testGetSet->_fPrivateBoldTextResult = true; - _testGetSet->_fExpectedForeground = true; - _testGetSet->_fExpectedBackground = true; - _testGetSet->_fExpectedMeta = true; - _testGetSet->_fExpectedIsBold = false; - _testGetSet->_expectedShowCursor = true; - _testGetSet->_privateShowCursorResult = true; const COORD coordExpectedCursorPos = { 0, 0 }; - // We're expecting _SetDefaultColorHelper to call - // PrivateSetLegacyAttributes with 0 as the wAttr param. - _testGetSet->_wExpectedAttribute = 0; + auto prepExpectedParameters = [&]() { + // Cursor to 1,1 + _testGetSet->_coordExpectedCursorPos = { 0, 0 }; + _testGetSet->_fSetConsoleCursorPositionResult = true; + _testGetSet->_fPrivateSetLegacyAttributesResult = true; + _testGetSet->_fPrivateSetDefaultAttributesResult = true; + _testGetSet->_fPrivateBoldTextResult = true; + _testGetSet->_fExpectedForeground = true; + _testGetSet->_fExpectedBackground = true; + _testGetSet->_fExpectedMeta = true; + _testGetSet->_fExpectedIsBold = false; + _testGetSet->_expectedShowCursor = true; + _testGetSet->_privateShowCursorResult = true; + + // We're expecting _SetDefaultColorHelper to call + // PrivateSetLegacyAttributes with 0 as the wAttr param. + _testGetSet->_wExpectedAttribute = 0; - // Prepare the results of SoftReset api calls - _testGetSet->_fPrivateSetCursorKeysModeResult = true; - _testGetSet->_fPrivateSetKeypadModeResult = true; - _testGetSet->_fGetConsoleScreenBufferInfoExResult = true; - _testGetSet->_fPrivateSetScrollingRegionResult = true; + // Prepare the results of SoftReset api calls + _testGetSet->_fPrivateSetCursorKeysModeResult = true; + _testGetSet->_fPrivateSetKeypadModeResult = true; + _testGetSet->_fGetConsoleScreenBufferInfoExResult = true; + _testGetSet->_fPrivateSetScrollingRegionResult = true; + }; + prepExpectedParameters(); VERIFY_IS_TRUE(_pDispatch->HardReset()); VERIFY_ARE_EQUAL(_testGetSet->_coordCursorPos, coordExpectedCursorPos); @@ -3457,18 +3460,21 @@ class AdapterTest Log::Comment(L"Test 2: Gracefully fail when getting console information fails."); _testGetSet->PrepData(); + prepExpectedParameters(); _testGetSet->_fGetConsoleScreenBufferInfoExResult = false; VERIFY_IS_FALSE(_pDispatch->HardReset()); Log::Comment(L"Test 3: Gracefully fail when filling the rectangle fails."); _testGetSet->PrepData(); + prepExpectedParameters(); _testGetSet->_fFillConsoleOutputCharacterWResult = false; VERIFY_IS_FALSE(_pDispatch->HardReset()); Log::Comment(L"Test 4: Gracefully fail when setting the window fails."); _testGetSet->PrepData(); + prepExpectedParameters(); _testGetSet->_fSetConsoleWindowInfoResult = false; VERIFY_IS_FALSE(_pDispatch->HardReset()); diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index 8484b6536a3..cb088c0519a 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -909,7 +909,8 @@ Viewport Viewport::ToOrigin() const noexcept // Just put the original rectangle into the results and return early. result.viewports.at(result.used++) = original; } - else + // If the original rectangle matches the intersection, there is nothing to return. + else if (original != intersection) { // Generate our potential four viewports that represent the region of the original that falls outside of the remove area. // We will bias toward generating wide rectangles over tall rectangles (if possible) so that optimizations that apply @@ -922,8 +923,8 @@ Viewport Viewport::ToOrigin() const noexcept // | | | | // | | | | // | | | | - // | | ======> | intersect | ======> early return of 0x0 Viewport - // | | | | at Original's origin + // | | ======> | intersect | ======> early return of nothing + // | | | | // | | | | // | | | | // |---------removeMe---------| |--------------------------| @@ -992,39 +993,32 @@ Viewport Viewport::ToOrigin() const noexcept // | removeMe | // |---------------| - if (original == intersection) + // We generate these rectangles by the original and intersection points, but some of them might be empty when the intersection + // lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind. + // We will filter those out below when adding them to the result. + const auto top = Viewport({ original.Left(), original.Top(), original.RightInclusive(), intersection.Top() - 1 }); + const auto bottom = Viewport({ original.Left(), intersection.BottomExclusive(), original.RightInclusive(), original.BottomInclusive() }); + const auto left = Viewport({ original.Left(), intersection.Top(), intersection.Left() - 1, intersection.BottomInclusive() }); + const auto right = Viewport({ intersection.RightExclusive(), intersection.Top(), original.RightInclusive(), intersection.BottomInclusive() }); + + if (top.IsValid()) { - result.viewports.at(result.used++) = Viewport::FromDimensions(original.Origin(), { 0, 0 }); + result.viewports.at(result.used++) = top; } - else - { - // We generate these rectangles by the original and intersection points, but some of them might be empty when the intersection - // lines up with the edge of the original. That's OK. That just means that the subtraction didn't leave anything behind. - // We will filter those out below when adding them to the result. - const auto top = Viewport({ original.Left(), original.Top(), original.RightInclusive(), intersection.Top() - 1 }); - const auto bottom = Viewport({ original.Left(), intersection.BottomExclusive(), original.RightInclusive(), original.BottomInclusive() }); - const auto left = Viewport({ original.Left(), intersection.Top(), intersection.Left() - 1, intersection.BottomInclusive() }); - const auto right = Viewport({ intersection.RightExclusive(), intersection.Top(), original.RightInclusive(), intersection.BottomInclusive() }); - - if (top.IsValid()) - { - result.viewports.at(result.used++) = top; - } - if (bottom.IsValid()) - { - result.viewports.at(result.used++) = bottom; - } + if (bottom.IsValid()) + { + result.viewports.at(result.used++) = bottom; + } - if (left.IsValid()) - { - result.viewports.at(result.used++) = left; - } + if (left.IsValid()) + { + result.viewports.at(result.used++) = left; + } - if (right.IsValid()) - { - result.viewports.at(result.used++) = right; - } + if (right.IsValid()) + { + result.viewports.at(result.used++) = right; } } From f4294b17d7f6eaf60ea72d75e1004a10633571ea Mon Sep 17 00:00:00 2001 From: Richard Szalay Date: Thu, 29 Aug 2019 00:40:16 +1000 Subject: [PATCH 071/154] Clean up Pane (#2494) * Merge pane splitting methods Having separate Horizontal/Vertical versions made it hard to manage, and App.cpp already made use of Pane::SplitState so it made sense to have that be the descriminator * Rename Tab::(Can)AddSplit to (Can)SplitPane to align with Pane methods Split was used as a noun in Tab but a verb in Pane, which felt odd * Remove unused local variable in Pane::_CanSplit * Remove redundant 'else' branches in Pane Improves readibility for all 'low hanging fruit' cases where the 'if' was returning. --- src/cascadia/TerminalApp/App.cpp | 6 +- src/cascadia/TerminalApp/Pane.cpp | 184 ++++++++++-------------------- src/cascadia/TerminalApp/Pane.h | 7 +- src/cascadia/TerminalApp/Tab.cpp | 37 ++---- src/cascadia/TerminalApp/Tab.h | 7 +- 5 files changed, 79 insertions(+), 162 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 6c88340b844..439a6367907 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1493,8 +1493,7 @@ namespace winrt::TerminalApp::implementation const int focusedTabIndex = _GetFocusedTabIndex(); auto focusedTab = _tabs[focusedTabIndex]; - const auto canSplit = splitType == Pane::SplitState::Horizontal ? focusedTab->CanAddHorizontalSplit() : - focusedTab->CanAddVerticalSplit(); + const auto canSplit = focusedTab->CanSplitPane(splitType); if (!canSplit) { @@ -1506,8 +1505,7 @@ namespace winrt::TerminalApp::implementation // Hookup our event handlers to the new terminal _RegisterTerminalEvents(newControl, focusedTab); - return splitType == Pane::SplitState::Horizontal ? focusedTab->AddHorizontalSplit(realGuid, newControl) : - focusedTab->AddVerticalSplit(realGuid, newControl); + focusedTab->SplitPane(splitType, realGuid, newControl); } // Method Description: diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 10e3bd09d6a..3096dddb362 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -164,26 +164,26 @@ bool Pane::ResizePane(const Direction& direction) { return _Resize(direction); } - else + + // If neither of our children were the focused leaf, then recurse into + // our children and see if they can handle the resize. + // For each child, if it has a focused descendant, try having that child + // handle the resize. + // If the child wasn't able to handle the resize, it's possible that + // there were no descendants with a separator the correct direction. If + // our separator _is_ the correct direction, then we should be the pane + // to resize. Otherwise, just return false, as we couldn't handle it + // either. + if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild()) { - // If neither of our children were the focused leaf, then recurse into - // our children and see if they can handle the resize. - // For each child, if it has a focused descendant, try having that child - // handle the resize. - // If the child wasn't able to handle the resize, it's possible that - // there were no descendants with a separator the correct direction. If - // our separator _is_ the correct direction, then we should be the pane - // to resize. Otherwise, just return false, as we couldn't handle it - // either. - if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild()) - { - return _firstChild->ResizePane(direction) || _Resize(direction); - } - else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild()) - { - return _secondChild->ResizePane(direction) || _Resize(direction); - } + return _firstChild->ResizePane(direction) || _Resize(direction); } + + if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild()) + { + return _secondChild->ResizePane(direction) || _Resize(direction); + } + return false; } @@ -253,26 +253,26 @@ bool Pane::NavigateFocus(const Direction& direction) { return _NavigateFocus(direction); } - else + + // If neither of our children were the focused leaf, then recurse into + // our children and see if they can handle the focus move. + // For each child, if it has a focused descendant, try having that child + // handle the focus move. + // If the child wasn't able to handle the focus move, it's possible that + // there were no descendants with a separator the correct direction. If + // our separator _is_ the correct direction, then we should be the pane + // to move focus into our other child. Otherwise, just return false, as + // we couldn't handle it either. + if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild()) { - // If neither of our children were the focused leaf, then recurse into - // our children and see if they can handle the focus move. - // For each child, if it has a focused descendant, try having that child - // handle the focus move. - // If the child wasn't able to handle the focus move, it's possible that - // there were no descendants with a separator the correct direction. If - // our separator _is_ the correct direction, then we should be the pane - // to move focus into our other child. Otherwise, just return false, as - // we couldn't handle it either. - if ((!_firstChild->_IsLeaf()) && _firstChild->_HasFocusedChild()) - { - return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction); - } - else if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild()) - { - return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction); - } + return _firstChild->NavigateFocus(direction) || _NavigateFocus(direction); } + + if ((!_secondChild->_IsLeaf()) && _secondChild->_HasFocusedChild()) + { + return _secondChild->NavigateFocus(direction) || _NavigateFocus(direction); + } + return false; } @@ -348,15 +348,13 @@ std::shared_ptr Pane::GetFocusedPane() { return _lastFocused ? shared_from_this() : nullptr; } - else + + auto firstFocused = _firstChild->GetFocusedPane(); + if (firstFocused != nullptr) { - auto firstFocused = _firstChild->GetFocusedPane(); - if (firstFocused != nullptr) - { - return firstFocused; - } - return _secondChild->GetFocusedPane(); + return firstFocused; } + return _secondChild->GetFocusedPane(); } // Method Description: @@ -778,110 +776,58 @@ void Pane::_ApplySplitDefinitions() } // Method Description: -// - Determines whether the pane can be split vertically +// - Determines whether the pane can be split // Arguments: // - splitType: what type of split we want to create. // Return Value: -// - True if the pane can be split vertically. False otherwise. -bool Pane::CanSplitVertical() +// - True if the pane can be split. False otherwise. +bool Pane::CanSplit(SplitState splitType) { - if (!_IsLeaf()) + if (_IsLeaf()) { - if (_firstChild->_HasFocusedChild()) - { - return _firstChild->CanSplitVertical(); - } - else if (_secondChild->_HasFocusedChild()) - { - return _secondChild->CanSplitVertical(); - } - - return false; + return _CanSplit(splitType); } - return _CanSplit(SplitState::Vertical); -} - -// Method Description: -// - Vertically split the focused pane in our tree of panes, and place the given -// TermControl into the newly created pane. If we're the focused pane, then -// we'll create two new children, and place them side-by-side in our Grid. -// Arguments: -// - profile: The profile GUID to associate with the newly created pane. -// - control: A TermControl to use in the new pane. -// Return Value: -// - -void Pane::SplitVertical(const GUID& profile, const TermControl& control) -{ - // If we're not the leaf, recurse into our children to split them. - if (!_IsLeaf()) + if (_firstChild->_HasFocusedChild()) { - if (_firstChild->_HasFocusedChild()) - { - _firstChild->SplitVertical(profile, control); - } - else if (_secondChild->_HasFocusedChild()) - { - _secondChild->SplitVertical(profile, control); - } - - return; + return _firstChild->CanSplit(splitType); } - _Split(SplitState::Vertical, profile, control); -} - -// Method Description: -// - Determines whether the pane can be split horizontally -// Arguments: -// - splitType: what type of split we want to create. -// Return Value: -// - True if the pane can be split horizontally. False otherwise. -bool Pane::CanSplitHorizontal() -{ - if (!_IsLeaf()) + if (_secondChild->_HasFocusedChild()) { - if (_firstChild->_HasFocusedChild()) - { - return _firstChild->CanSplitHorizontal(); - } - else if (_secondChild->_HasFocusedChild()) - { - return _secondChild->CanSplitHorizontal(); - } - - return false; + return _secondChild->CanSplit(splitType); } - return _CanSplit(SplitState::Horizontal); + return false; } // Method Description: -// - Horizontally split the focused pane in our tree of panes, and place the given +// - Split the focused pane in our tree of panes, and place the given // TermControl into the newly created pane. If we're the focused pane, then // we'll create two new children, and place them side-by-side in our Grid. // Arguments: +// - splitType: what type of split we want to create. // - profile: The profile GUID to associate with the newly created pane. // - control: A TermControl to use in the new pane. // Return Value: // - -void Pane::SplitHorizontal(const GUID& profile, const TermControl& control) +void Pane::Split(SplitState splitType, const GUID& profile, const TermControl& control) { if (!_IsLeaf()) { if (_firstChild->_HasFocusedChild()) { - _firstChild->SplitHorizontal(profile, control); + _firstChild->Split(splitType, profile, control); } else if (_secondChild->_HasFocusedChild()) { - _secondChild->SplitHorizontal(profile, control); + _secondChild->Split(splitType, profile, control); } return; } - _Split(SplitState::Horizontal, profile, control); + _Split(splitType, profile, control); } // Method Description: @@ -892,8 +838,6 @@ void Pane::SplitHorizontal(const GUID& profile, const TermControl& control) // - True if the pane can be split. False otherwise. bool Pane::_CanSplit(SplitState splitType) { - const bool changeWidth = _splitState == SplitState::Vertical; - const Size actualSize{ gsl::narrow_cast(_root.ActualWidth()), gsl::narrow_cast(_root.ActualHeight()) }; @@ -1006,14 +950,12 @@ Size Pane::_GetMinSize() const { return _control.MinimumSize(); } - else - { - const auto firstSize = _firstChild->_GetMinSize(); - const auto secondSize = _secondChild->_GetMinSize(); - const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0); - const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0); - return { newWidth, newHeight }; - } + + const auto firstSize = _firstChild->_GetMinSize(); + const auto secondSize = _secondChild->_GetMinSize(); + const auto newWidth = firstSize.Width + secondSize.Width + (_splitState == SplitState::Vertical ? PaneSeparatorSize : 0); + const auto newHeight = firstSize.Height + secondSize.Height + (_splitState == SplitState::Horizontal ? PaneSeparatorSize : 0); + return { newWidth, newHeight }; } DEFINE_EVENT(Pane, Closed, _closedHandlers, ConnectionClosedEventArgs); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 31979b6d090..d526153720e 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -49,11 +49,8 @@ class Pane : public std::enable_shared_from_this bool ResizePane(const winrt::TerminalApp::Direction& direction); bool NavigateFocus(const winrt::TerminalApp::Direction& direction); - bool CanSplitHorizontal(); - void SplitHorizontal(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); - - bool CanSplitVertical(); - void SplitVertical(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + bool CanSplit(SplitState splitType); + void Split(SplitState splitType, const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void Close(); diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index 4870d227f4b..c4e40032695 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -204,47 +204,28 @@ void Tab::Scroll(const int delta) } // Method Description: -// - Determines whether the focused pane has sufficient space to be split vertically. -// Return Value: -// - True if the focused pane can be split horizontally. False otherwise. -bool Tab::CanAddVerticalSplit() -{ - return _rootPane->CanSplitVertical(); -} - -// Method Description: -// - Vertically split the focused pane in our tree of panes, and place the -// given TermControl into the newly created pane. +// - Determines whether the focused pane has sufficient space to be split. // Arguments: -// - profile: The profile GUID to associate with the newly created pane. -// - control: A TermControl to use in the new pane. -// Return Value: -// - -void Tab::AddVerticalSplit(const GUID& profile, TermControl& control) -{ - _rootPane->SplitVertical(profile, control); -} - -// Method Description: -// - Determines whether the focused pane has sufficient space to be split horizontally. +// - splitType: The type of split we want to create. // Return Value: -// - True if the focused pane can be split horizontally. False otherwise. -bool Tab::CanAddHorizontalSplit() +// - True if the focused pane can be split. False otherwise. +bool Tab::CanSplitPane(Pane::SplitState splitType) { - return _rootPane->CanSplitHorizontal(); + return _rootPane->CanSplit(splitType); } // Method Description: -// - Horizontally split the focused pane in our tree of panes, and place the +// - Split the focused pane in our tree of panes, and place the // given TermControl into the newly created pane. // Arguments: +// - splitType: The type of split we want to create. // - profile: The profile GUID to associate with the newly created pane. // - control: A TermControl to use in the new pane. // Return Value: // - -void Tab::AddHorizontalSplit(const GUID& profile, TermControl& control) +void Tab::SplitPane(Pane::SplitState splitType, const GUID& profile, TermControl& control) { - _rootPane->SplitHorizontal(profile, control); + _rootPane->Split(splitType, profile, control); } // Method Description: diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/Tab.h index 99c782201e3..8af4423e6b8 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/Tab.h @@ -19,10 +19,9 @@ class Tab void SetFocused(const bool focused); void Scroll(const int delta); - bool CanAddVerticalSplit(); - void AddVerticalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); - bool CanAddHorizontalSplit(); - void AddHorizontalSplit(const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + + bool CanSplitPane(Pane::SplitState splitType); + void SplitPane(Pane::SplitState splitType, const GUID& profile, winrt::Microsoft::Terminal::TerminalControl::TermControl& control); void UpdateFocus(); void UpdateIcon(const winrt::hstring iconPath); From 5e38bcd7541bcf702c4ff8a749a0d4a883e42292 Mon Sep 17 00:00:00 2001 From: Martin Lopes <54248166+martin389@users.noreply.github.com> Date: Thu, 29 Aug 2019 01:43:29 +0100 Subject: [PATCH 072/154] Fixed typo in user-docs (#2592) Fixed typo "the the". --- doc/user-docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user-docs/index.md b/doc/user-docs/index.md index 5b7b27850d8..9229aa780e9 100644 --- a/doc/user-docs/index.md +++ b/doc/user-docs/index.md @@ -72,7 +72,7 @@ From the `down` button in the top bar select Settings (default shortcut `Ctrl+,` Your default json editor will open up the Terminal settings file. The file can be found at `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_/RoamingState` -An introduction to the the various settings can be found [here](UsingJsonSettings.md). +An introduction to the various settings can be found [here](UsingJsonSettings.md). The list of valid settings can be found in the [Profiles.json Documentation](../cascadia/SettingsSchema.md) doc. From 0d12a25b2d45c8316c3d61fe296018ad1c388f1c Mon Sep 17 00:00:00 2001 From: drebelsky Date: Thu, 29 Aug 2019 11:41:10 -0500 Subject: [PATCH 073/154] Fix typo (#2538) changed "an file" to "a file" --- src/tools/ColorTool/ColorTool/Resources.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/ColorTool/ColorTool/Resources.resx b/src/tools/ColorTool/ColorTool/Resources.resx index ee2a4a96736..091bbf02250 100644 --- a/src/tools/ColorTool/ColorTool/Resources.resx +++ b/src/tools/ColorTool/ColorTool/Resources.resx @@ -162,7 +162,7 @@ Options: -s, --schemes : Displays all available schemes -l, --location : Displays the full path to the schemes directory -v, --version : Display the version number - -o, --output <filename> : output the current color table to an file (in .ini format) + -o, --output <filename> : output the current color table to a file (in .ini format) Available importers: {0} @@ -170,4 +170,4 @@ Available importers: Wrote selected scheme to the defaults. - \ No newline at end of file + From f93adb95400a4a7314b929bc4f336f49d4187973 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 09:45:02 -0700 Subject: [PATCH 074/154] Added more bot rules (#2502) * Added more bot rules * Update bot.md --- doc/bot.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/bot.md b/doc/bot.md index fad0cca928f..b96c7e233e4 100644 --- a/doc/bot.md +++ b/doc/bot.md @@ -33,6 +33,17 @@ We'll be using tags, primarily, to help us understand what needs attention, what ## Rules +### Triage Shorthand +- All rules in this category apply to triaging issues. They're shorthand comments that the triage team can use in order to complete the triage process faster. +- Only individuals with `Write` or `Admin` privileges on the repository can use these responses. + +#### Duplicate Issues +- When a comment on the thread says `/dup #`... +1. Reply with a comment explaining that the issue is a duplicate and recommend that the opener and interested parties follow the issue on the listed ID number. +1. Close the issue +1. Remove all `Needs-*` tags +1. Add `Resolution-Duplicate` + ### Issue Management #### Mark as Triage Needed @@ -70,6 +81,9 @@ We'll be using tags, primarily, to help us understand what needs attention, what - And remove the `Needs-Feedback-Hub` tag - And add the `Needs-Author-Feedback` tag +#### Remove Help Wanted from In PR issues +- If an issue gets the `In-PR` tag when a new PR is created, we will remove the `Help-Wanted` tag to avoid someone trying to work on an issue where another person has already submitted a proposed fix. + ### PR Management #### Codeflow Link *(Disabled)* From 5de63096ac7c957088c23d3c71d74eedcae59f71 Mon Sep 17 00:00:00 2001 From: brightbluejay Date: Thu, 29 Aug 2019 17:46:32 +0100 Subject: [PATCH 075/154] Update building.md (#2501) minor spelling corrections --- doc/building.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/building.md b/doc/building.md index efeb878dba5..b7cebc72eca 100644 --- a/doc/building.md +++ b/doc/building.md @@ -1,7 +1,7 @@ # How to build Openconsole -Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and powershell in /tools. +Openconsole can be built with Visual Studio or from the command line. There are build scripts for both cmd and PowerShell in /tools. When using Visual Studio, be sure to set up the path for code formatting. This can be done in Visual Studio by going to Tools > Options > Text Editor > C++ > Formatting and checking "Use custom clang-format.exe file" and choosing the clang-format.exe in the repository at /dep/llvm/clang-format.exe by clicking "browse" right under the check box. @@ -33,4 +33,4 @@ Openconsole has three configuration types: - Release - AuditMode -AuditMode is an experimental mode that enables some additional static analyis from CppCoreCheck. +AuditMode is an experimental mode that enables some additional static analysis from CppCoreCheck. From cb02ca7534d343f6f556259cf2c47e77f6fd15fb Mon Sep 17 00:00:00 2001 From: Kayla Cinnamon <48369326+cinnamon-msft@users.noreply.github.com> Date: Thu, 29 Aug 2019 09:47:01 -0700 Subject: [PATCH 076/154] Changed default padding to 8,8,8,8 and default font size to 11 (#2378) * changed default padding to 5,5,5,5 and default font size to 11 * updated documentation * changed padding to 8 --- doc/cascadia/SettingsSchema.md | 4 ++-- src/inc/DefaultSettings.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index ecbaae39293..2d5d32bf8c1 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -27,11 +27,11 @@ Properties listed below are specific to each unique profile. | `cursorColor` | _Required_ | String | `#FFFFFF` | Sets the cursor color for the profile. Uses hex color format: `"#rrggbb"`. | | `cursorShape` | _Required_ | String | `bar` | Sets the cursor shape for the profile. Possible values: `"vintage"` ( ▃ ), `"bar"` ( ┃ ), `"underscore"` ( ▁ ), `"filledBox"` ( █ ), `"emptyBox"` ( ▯ ) | | `fontFace` | _Required_ | String | `Consolas` | Name of the font face used in the profile. We will try to fallback to Consolas if this can't be found or is invalid. | -| `fontSize` | _Required_ | Integer | `10` | Sets the font size. | +| `fontSize` | _Required_ | Integer | `12` | Sets the font size. | | `guid` | _Required_ | String | | Unique identifier of the profile. Written in registry format: `"{00000000-0000-0000-0000-000000000000}"`. | | `historySize` | _Required_ | Integer | `9001` | The number of lines above the ones displayed in the window you can scroll back to. | | `name` | _Required_ | String | `PowerShell Core` | Name of the profile. Displays in the dropdown menu.
Additionally, this value will be used as the "title" to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. This "title" behavior can be overriden by using `tabTitle`. | -| `padding` | _Required_ | String | `0, 0, 0, 0` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. | +| `padding` | _Required_ | String | `8, 8, 8, 8` | Sets the padding around the text within the window. Can have three different formats: `"#"` sets the same padding for all sides, `"#, #"` sets the same padding for left-right and top-bottom, and `"#, #, #, #"` sets the padding individually for left, top, right, and bottom. | | `snapOnInput` | _Required_ | Boolean | `true` | When set to `true`, the window will scroll to the command input line when typing. When set to `false`, the window will not scroll when you start typing. | | `startingDirectory` | _Required_ | String | `%USERPROFILE%` | The directory the shell starts in when it is loaded. | | `useAcrylic` | _Required_ | Boolean | `false` | When set to `true`, the window will have an acrylic background. When set to `false`, the window will have a plain, untextured background. | diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 99ae30aed78..678f4115b31 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -27,12 +27,12 @@ constexpr COLORREF POWERSHELL_BLUE = RGB(1, 36, 86); constexpr short DEFAULT_HISTORY_SIZE = 9001; const std::wstring DEFAULT_FONT_FACE{ L"Consolas" }; -constexpr int DEFAULT_FONT_SIZE = 10; +constexpr int DEFAULT_FONT_SIZE = 12; constexpr int DEFAULT_ROWS = 30; constexpr int DEFAULT_COLS = 120; -const std::wstring DEFAULT_PADDING{ L"0, 0, 0, 0" }; +const std::wstring DEFAULT_PADDING{ L"8, 8, 8, 8" }; const std::wstring DEFAULT_STARTING_DIRECTORY{ L"%USERPROFILE%" }; constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE; From 1989eb9d0027477acb76174b936016d2d60580e6 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 10:27:29 -0700 Subject: [PATCH 077/154] Make warnings errors for static analysis. --- src/StaticAnalysis.ruleset | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/StaticAnalysis.ruleset b/src/StaticAnalysis.ruleset index e9d2a69ed8d..c1f52c15f69 100644 --- a/src/StaticAnalysis.ruleset +++ b/src/StaticAnalysis.ruleset @@ -1,11 +1,6 @@  - - - - - - + From 65dec36cb1eebbb700eb220b7c1a36ceedea03d8 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 11:05:32 -0700 Subject: [PATCH 078/154] C26446, Use .at instead of array indices --- src/buffer/out/AttrRow.cpp | 6 ++--- src/buffer/out/TextColor.cpp | 8 +++--- src/buffer/out/textBuffer.cpp | 24 ++++++++--------- src/renderer/dx/CustomTextLayout.cpp | 36 ++++++++++++------------- src/types/IInputEvent.cpp | 2 +- src/types/ScreenInfoUiaProviderBase.cpp | 4 +-- src/types/UiaTextRangeBase.cpp | 2 +- src/types/convert.cpp | 2 +- src/types/utils.cpp | 12 ++++----- 9 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/buffer/out/AttrRow.cpp b/src/buffer/out/AttrRow.cpp index e9efd8f6037..37d77a02f06 100644 --- a/src/buffer/out/AttrRow.cpp +++ b/src/buffer/out/AttrRow.cpp @@ -46,7 +46,7 @@ void ATTR_ROW::Resize(const size_t newWidth) { // Get the attribute that covers the final column of old width. const auto runPos = FindAttrIndex(_cchRowWidth - 1, nullptr); - auto& run = _list[runPos]; + auto& run = _list.at(runPos); // Extend its length by the additional columns we're adding. run.SetLength(run.GetLength() + newWidth - _cchRowWidth); @@ -60,7 +60,7 @@ void ATTR_ROW::Resize(const size_t newWidth) // Get the attribute that covers the final column of the new width size_t CountOfAttr = 0; const auto runPos = FindAttrIndex(newWidth - 1, &CountOfAttr); - auto& run = _list[runPos]; + auto& run = _list.at(runPos); // CountOfAttr was given to us as "how many columns left from this point forward are covered by the returned run" // So if the original run was B5 covering a 5 size OldWidth and we have a NewWidth of 3 @@ -108,7 +108,7 @@ TextAttribute ATTR_ROW::GetAttrByColumn(const size_t column, { THROW_HR_IF(E_INVALIDARG, column >= _cchRowWidth); const auto runPos = FindAttrIndex(column, pApplies); - return _list[runPos].GetAttributes(); + return _list.at(runPos).GetAttributes(); } // Routine Description: diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index 76778a8230b..af90f9ff857 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -81,9 +81,9 @@ COLORREF TextColor::GetColor(std::basic_string_view colorTable, // If we find a match, return instead the bright version of this color for (size_t i = 0; i < 8; i++) { - if (colorTable[i] == defaultColor) + if (colorTable.at(i) == defaultColor) { - return colorTable[i + 8]; + return colorTable.at(i + 8); } } } @@ -103,11 +103,11 @@ COLORREF TextColor::GetColor(std::basic_string_view colorTable, { FAIL_FAST_IF(colorTable.size() < 16); FAIL_FAST_IF((size_t)(_index + 8) > (size_t)(colorTable.size())); - return colorTable[_index + 8]; + return colorTable.at(_index + 8); } else { - return colorTable[_index]; + return colorTable.at(_index); } } } diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 831c672a586..09cae28d62b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -78,7 +78,7 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const // Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows. const size_t offsetIndex = (_firstRow + index) % totalRows; - return _storage[offsetIndex]; + return _storage.at(offsetIndex); } // Routine Description: @@ -812,7 +812,7 @@ void TextBuffer::Reset() // rotate rows until the top row is at index 0 try { - const ROW& newTopRow = _storage[TopRowIndex]; + const ROW& newTopRow = _storage.at(TopRowIndex); while (&newTopRow != &_storage.front()) { _storage.push_back(std::move(_storage.front())); @@ -923,7 +923,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row) } THROW_HR_IF(E_FAIL, Row.GetId() == _firstRow); - return _storage[prevRowIndex]; + return _storage.at(prevRowIndex); } // Method Description: @@ -1118,25 +1118,25 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo htmlBuilder << "
"; } - for (UINT col = 0; col < rows.text[row].length(); col++) + for (UINT col = 0; col < rows.text.at(row).length(); col++) { // do not include \r nor \n as they don't have attributes // and are not HTML friendly. For line break use '
' instead. bool isLastCharInRow = - col == rows.text[row].length() - 1 || - rows.text[row][col + 1] == '\r' || - rows.text[row][col + 1] == '\n'; + col == rows.text.at(row).length() - 1 || + rows.text.at(row).at(col + 1) == '\r' || + rows.text.at(row).at(col + 1) == '\n'; bool colorChanged = false; - if (!fgColor.has_value() || rows.FgAttr[row][col] != fgColor.value()) + if (!fgColor.has_value() || rows.FgAttr.at(row).at(col) != fgColor.value()) { - fgColor = rows.FgAttr[row][col]; + fgColor = rows.FgAttr.at(row).at(col); colorChanged = true; } - if (!bkColor.has_value() || rows.BkAttr[row][col] != bkColor.value()) + if (!bkColor.has_value() || rows.BkAttr.at(row).at(col) != bkColor.value()) { - bkColor = rows.BkAttr[row][col]; + bkColor = rows.BkAttr.at(row).at(col); colorChanged = true; } @@ -1145,7 +1145,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo { // note: this should be escaped (for '<', '>', and '&'), // however MS Word doesn't appear to support HTML entities - htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text[row].data() + startOffset, col - startOffset + includeCurrent)); + htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row).data() + startOffset, col - startOffset + includeCurrent)); startOffset = col; } }; diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 94cf4c8e47a..73b14010272 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -268,7 +268,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, do { hr = _analyzer->GetGlyphs( - &_text[textStart], + &_text.at(textStart), textLength, run.fontFace.Get(), run.isSideways, // isSideways, @@ -280,10 +280,10 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, nullptr, // featureLengths 0, // featureCount maxGlyphCount, // maxGlyphCount - &_glyphClusters[textStart], - &textProps[0], - &_glyphIndices[glyphStart], - &glyphProps[0], + &_glyphClusters.at(textStart), + &textProps.at(0), + &_glyphIndices.at(glyphStart), + &glyphProps.at(0), &actualGlyphCount); tries++; @@ -313,12 +313,12 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, const auto fontSize = fontSizeFormat * run.fontScale; hr = _analyzer->GetGlyphPlacements( - &_text[textStart], - &_glyphClusters[textStart], - &textProps[0], + &_text.at(textStart), + &_glyphClusters.at(textStart), + &textProps.at(0), textLength, - &_glyphIndices[glyphStart], - &glyphProps[0], + &_glyphIndices.at(glyphStart), + &glyphProps.at(0), actualGlyphCount, run.fontFace.Get(), fontSize, @@ -329,8 +329,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, NULL, // features NULL, // featureRangeLengths 0, // featureRanges - &_glyphAdvances[glyphStart], - &_glyphOffsets[glyphStart]); + &_glyphAdvances.at(glyphStart), + &_glyphOffsets.at(glyphStart)); RETURN_IF_FAILED(hr); @@ -391,13 +391,13 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, for (auto i = run.glyphStart; i < (run.glyphStart + run.glyphCount); i++) { // Advance is how wide in pixels the glyph is - auto& advance = _glyphAdvances[i]; + auto& advance = _glyphAdvances.at(i); // Offsets is how far to move the origin (in pixels) from where it is - auto& offset = _glyphOffsets[i]; + auto& offset = _glyphOffsets.at(i); // Get how many columns we expected the glyph to have and mutiply into pixels. - const auto columns = _textClusterColumns[i]; + const auto columns = _textClusterColumns.at(i); const auto advanceExpected = static_cast(columns * _width); // If what we expect is bigger than what we have... pad it out. @@ -419,7 +419,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // We need to retrieve the design information for this specific glyph so we can figure out the appropriate // height proportional to the width that we desire. INT32 advanceInDesignUnits; - RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices[i], &advanceInDesignUnits)); + RETURN_IF_FAILED(run.fontFace->GetDesignGlyphAdvances(1, &_glyphIndices.at(i), &advanceInDesignUnits)); // When things are drawn, we want the font size (as specified in the base font in the original format) // to be scaled by some factor. @@ -940,7 +940,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - - Updates internal state void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition) { - if (_runIndex < _runs.size() && _runs[_runIndex].ContainsTextPosition(textPosition)) + if (_runIndex < _runs.size() && _runs.at(_runIndex).ContainsTextPosition(textPosition)) { return; } @@ -974,7 +974,7 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition) } // Copy the old run to the end. - LinkedRun& frontHalf = _runs[_runIndex]; + LinkedRun& frontHalf = _runs.at(_runIndex); LinkedRun& backHalf = _runs.back(); backHalf = frontHalf; diff --git a/src/types/IInputEvent.cpp b/src/types/IInputEvent.cpp index f7a3a925a2a..5df614a6ca5 100644 --- a/src/types/IInputEvent.cpp +++ b/src/types/IInputEvent.cpp @@ -48,7 +48,7 @@ std::deque> IInputEvent::Create(const std::deque> outEvents; for (size_t i = 0; i < records.size(); ++i) { - std::unique_ptr event = IInputEvent::Create(records[i]); + std::unique_ptr event = IInputEvent::Create(records.at(i)); outEvents.push_back(std::move(event)); } return outEvents; diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index aff23dde5cc..8b043601103 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -388,14 +388,14 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ // fill the safe array for (LONG i = 0; i < static_cast(ranges.size()); ++i) { - hr = SafeArrayPutElement(*ppRetVal, &i, reinterpret_cast(ranges[i])); + hr = SafeArrayPutElement(*ppRetVal, &i, reinterpret_cast(ranges.at(i))); if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); *ppRetVal = nullptr; while (!ranges.empty()) { - UiaTextRangeBase* pRange = ranges[0]; + UiaTextRangeBase* pRange = ranges.at(0); ranges.pop_front(); pRange->Release(); } diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 47e7d502f32..f4932f70dec 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -487,7 +487,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ HRESULT hr; for (LONG i = 0; i < static_cast(coords.size()); ++i) { - hr = SafeArrayPutElement(*ppRetVal, &i, &coords[i]); + hr = SafeArrayPutElement(*ppRetVal, &i, &coords.at(i)); if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); diff --git a/src/types/convert.cpp b/src/types/convert.cpp index 7ca12962c98..966e9f4a5bc 100644 --- a/src/types/convert.cpp +++ b/src/types/convert.cpp @@ -284,7 +284,7 @@ std::deque> SynthesizeNumpadEvents(const wchar_t wch, // But it is absolutely valid as 0xFF or 255 unsigned as the correct CP437 character. // We need to treat it as unsigned because we're going to pretend it was a keypad entry // and you don't enter negative numbers on the keypad. - unsigned char const uch = static_cast(convertedChars[0]); + unsigned char const uch = static_cast(convertedChars.at(0)); // unsigned char values are in the range [0, 255] so we need to be // able to store up to 4 chars from the conversion (including the end of string char) diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 8554c00a19b..8806748a464 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -92,11 +92,11 @@ std::string Utils::ColorToHexString(const COLORREF color) COLORREF Utils::ColorFromHexString(const std::string str) { THROW_HR_IF(E_INVALIDARG, str.size() < 7 || str.size() >= 8); - THROW_HR_IF(E_INVALIDARG, str[0] != '#'); + THROW_HR_IF(E_INVALIDARG, str.at(0) != '#'); - std::string rStr{ &str[1], 2 }; - std::string gStr{ &str[3], 2 }; - std::string bStr{ &str[5], 2 }; + std::string rStr{ &str.at(1), 2 }; + std::string gStr{ &str.at(3), 2 }; + std::string bStr{ &str.at(5), 2 }; BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); @@ -490,8 +490,8 @@ GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const gsl::span buffer; THROW_IF_NTSTATUS_FAILED(BCryptFinishHash(hash.get(), buffer.data(), gsl::narrow(buffer.size()), 0)); - buffer[6] = (buffer[6] & 0x0F) | 0x50; // set the uuid version to 5 - buffer[8] = (buffer[8] & 0x3F) | 0x80; // set the variant to 2 (RFC4122) + buffer.at(6) = (buffer.at(6) & 0x0F) | 0x50; // set the uuid version to 5 + buffer.at(8) = (buffer.at(8) & 0x3F) | 0x80; // set the variant to 2 (RFC4122) // We're using memcpy here pursuant to N4713 6.7.2/3 [basic.types], // "...the underlying bytes making up the object can be copied into an array From 23897b1bd450fe856bb9cdf11f59814bb758c2b2 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 11:09:44 -0700 Subject: [PATCH 079/154] [Complex] C26446, Use .at instead of array indices - Reword UTF8OutPipeReader to use std::array so we can use .at and move some pointers to iterators. --- src/types/UTF8OutPipeReader.cpp | 20 +++++++++++--------- src/types/inc/UTF8OutPipeReader.hpp | 8 ++++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/types/UTF8OutPipeReader.cpp b/src/types/UTF8OutPipeReader.cpp index 5c646bc2166..876dafa75b2 100644 --- a/src/types/UTF8OutPipeReader.cpp +++ b/src/types/UTF8OutPipeReader.cpp @@ -9,6 +9,8 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : _outPipe{ outPipe } { + _buffer.fill(0); + _utf8Partials.fill(0); } // Method Description: @@ -30,17 +32,17 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : bool fSuccess{}; // in case of early escaping - *_buffer = 0; - strView = std::string_view{ reinterpret_cast(_buffer), 0 }; + _buffer.at(0) = 0; + strView = std::string_view{ reinterpret_cast(_buffer.at(0)), 0 }; // copy UTF-8 code units that were remaining from the previously read chunk (if any) if (_dwPartialsLen != 0) { - std::move(_utf8Partials, _utf8Partials + _dwPartialsLen, _buffer); + std::move(_utf8Partials.cbegin(), _utf8Partials.cbegin() + _dwPartialsLen, _buffer.begin()); } // try to read data - fSuccess = !!ReadFile(_outPipe, &_buffer[_dwPartialsLen], std::extent::value - _dwPartialsLen, &dwRead, nullptr); + fSuccess = !!ReadFile(_outPipe, &_buffer.at(_dwPartialsLen), std::extent::value - _dwPartialsLen, &dwRead, nullptr); dwRead += _dwPartialsLen; _dwPartialsLen = 0; @@ -65,8 +67,8 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : return S_OK; } - const BYTE* const endPtr{ _buffer + dwRead }; - const BYTE* backIter{ endPtr - 1 }; + const auto endPtr = _buffer.cbegin() + dwRead; + auto backIter = endPtr - 1; // If the last byte in the buffer was a byte belonging to a UTF-8 multi-byte character if ((*backIter & _Utf8BitMasks::MaskAsciiByte) > _Utf8BitMasks::IsAsciiByte) { @@ -80,9 +82,9 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : // Use the bitmask at index `dwSequenceLen`. Compare the result with the operand having the same index. If they // are not equal then the sequence has to be cached because it is a partial code point. Otherwise the // sequence is a complete UTF-8 code point and the whole buffer is ready for the conversion to hstring. - if ((*backIter & _cmpMasks[dwSequenceLen]) != _cmpOperands[dwSequenceLen]) + if ((*backIter & _cmpMasks.at(dwSequenceLen)) != _cmpOperands.at(dwSequenceLen)) { - std::move(backIter, endPtr, _utf8Partials); + std::move(backIter, endPtr, _utf8Partials.begin()); dwRead -= dwSequenceLen; _dwPartialsLen = dwSequenceLen; } @@ -93,6 +95,6 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : } // give back a view of the part of the buffer that contains complete code points only - strView = std::string_view{ reinterpret_cast(_buffer), dwRead }; + strView = std::string_view{ reinterpret_cast(_buffer.at(0)), dwRead }; return S_OK; } diff --git a/src/types/inc/UTF8OutPipeReader.hpp b/src/types/inc/UTF8OutPipeReader.hpp index 4383ad0366a..fb0da7eaa6c 100644 --- a/src/types/inc/UTF8OutPipeReader.hpp +++ b/src/types/inc/UTF8OutPipeReader.hpp @@ -46,7 +46,7 @@ class UTF8OutPipeReader final }; // array of bitmasks - constexpr const static BYTE _cmpMasks[]{ + constexpr const static std::array _cmpMasks{ 0, // unused _Utf8BitMasks::MaskContinuationByte, _Utf8BitMasks::MaskLeadByteTwoByteSequence, @@ -54,7 +54,7 @@ class UTF8OutPipeReader final }; // array of values for the comparisons - constexpr const static BYTE _cmpOperands[]{ + constexpr const static std::array _cmpOperands{ 0, // unused _Utf8BitMasks::IsAsciiByte, // intentionally conflicts with MaskContinuationByte _Utf8BitMasks::IsLeadByteTwoByteSequence, @@ -62,7 +62,7 @@ class UTF8OutPipeReader final }; HANDLE _outPipe; // non-owning reference to a pipe. - BYTE _buffer[4096]{ 0 }; // buffer for the chunk read - BYTE _utf8Partials[4]{ 0 }; // buffer for code units of a partial UTF-8 code point that have to be cached + std::array _buffer; // buffer for the chunk read. + std::array _utf8Partials; // buffer for code units of a partial UTF-8 code point that have to be cached DWORD _dwPartialsLen{}; // number of cached UTF-8 code units }; From bd2d5ddb4b3c1e924c2fc44e140d4fd047522642 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 11:12:55 -0700 Subject: [PATCH 080/154] C26477, don't use 0 or NULL, use nullptr. --- src/renderer/dx/CustomTextLayout.cpp | 4 ++-- src/renderer/dx/DxRenderer.cpp | 12 ++++++------ src/types/utils.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 73b14010272..42e6d7c74ae 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -481,7 +481,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // Prepare the glyph run and description objects by converting our // internal storage representation into something that matches DWrite's structures. - DWRITE_GLYPH_RUN glyphRun = { 0 }; + DWRITE_GLYPH_RUN glyphRun; glyphRun.bidiLevel = run.bidiLevel; glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale; glyphRun.fontFace = run.fontFace.Get(); @@ -491,7 +491,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, glyphRun.glyphOffsets = _glyphOffsets.data() + run.glyphStart; glyphRun.isSideways = false; - DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription = { 0 }; + DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription; glyphRunDescription.clusterMap = _glyphClusters.data(); glyphRunDescription.localeName = _localeName.data(); glyphRunDescription.string = _text.data(); diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 33f8790470b..b8549e04e2c 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -158,28 +158,28 @@ DxEngine::~DxEngine() // Trying hardware first for maximum performance, then trying WARP (software) renderer second // in case we're running inside a downlevel VM where hardware passthrough isn't enabled like // for Windows 7 in a VM. - const auto hardwareResult = D3D11CreateDevice(NULL, + const auto hardwareResult = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, - NULL, + nullptr, DeviceFlags, FeatureLevels, ARRAYSIZE(FeatureLevels), D3D11_SDK_VERSION, &_d3dDevice, - NULL, + nullptr, &_d3dDeviceContext); if (FAILED(hardwareResult)) { - RETURN_IF_FAILED(D3D11CreateDevice(NULL, + RETURN_IF_FAILED(D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, - NULL, + nullptr, DeviceFlags, FeatureLevels, ARRAYSIZE(FeatureLevels), D3D11_SDK_VERSION, &_d3dDevice, - NULL, + nullptr, &_d3dDeviceContext)); } diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 8806748a464..f3fae2b85e2 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -113,7 +113,7 @@ COLORREF Utils::ColorFromHexString(const std::string str) // - True if non zero and not set to invalid magic value. False otherwise. bool Utils::IsValidHandle(const HANDLE handle) noexcept { - return handle != 0 && handle != INVALID_HANDLE_VALUE; + return handle != nullptr && handle != INVALID_HANDLE_VALUE; } // Function Description: From b33a59816e907825c49354c08282e41b9014a99b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 11:27:39 -0700 Subject: [PATCH 081/154] C26496, mark const if it's never written after creation --- src/buffer/out/AttrRow.cpp | 4 ++-- src/buffer/out/CharRow.cpp | 4 ++-- src/buffer/out/OutputCellIterator.cpp | 2 +- src/buffer/out/TextAttribute.cpp | 12 +++++------ src/buffer/out/TextAttribute.hpp | 16 +++++++------- src/buffer/out/textBuffer.cpp | 4 ++-- src/renderer/dx/CustomTextLayout.cpp | 20 +++++++++--------- src/renderer/dx/CustomTextRenderer.cpp | 13 ++++++------ src/renderer/dx/DxRenderer.cpp | 20 +++++++++--------- src/types/CodepointWidthDetector.cpp | 2 +- src/types/ScreenInfoUiaProviderBase.cpp | 4 ++-- src/types/UTF8OutPipeReader.cpp | 4 ++-- src/types/UiaTextRangeBase.cpp | 28 ++++++++++++------------- src/types/WindowUiaProviderBase.cpp | 2 +- src/types/utils.cpp | 6 +++--- 15 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/buffer/out/AttrRow.cpp b/src/buffer/out/AttrRow.cpp index 37d77a02f06..c97bcbba884 100644 --- a/src/buffer/out/AttrRow.cpp +++ b/src/buffer/out/AttrRow.cpp @@ -290,10 +290,10 @@ void ATTR_ROW::ReplaceAttrs(const TextAttribute& toBeReplacedAttr, const TextAtt // two elements in our internal list. else if (_list.size() == 2 && newAttrs.at(0).GetLength() == 1) { - auto left = _list.begin(); + const auto left = _list.begin(); if (iStart == left->GetLength() && NewAttr == left->GetAttributes()) { - auto right = left + 1; + const auto right = left + 1; left->IncrementLength(); right->DecrementLength(); diff --git a/src/buffer/out/CharRow.cpp b/src/buffer/out/CharRow.cpp index 60a7d0d144c..d255b01674f 100644 --- a/src/buffer/out/CharRow.cpp +++ b/src/buffer/out/CharRow.cpp @@ -264,7 +264,7 @@ std::wstring CharRow::GetTextRaw() const wstr.reserve(_data.size()); for (size_t i = 0; i < _data.size(); ++i) { - auto glyph = GlyphAt(i); + const auto glyph = GlyphAt(i); for (auto it = glyph.begin(); it != glyph.end(); ++it) { wstr.push_back(*it); @@ -280,7 +280,7 @@ std::wstring CharRow::GetText() const for (size_t i = 0; i < _data.size(); ++i) { - auto glyph = GlyphAt(i); + const auto glyph = GlyphAt(i); if (!DbcsAttrAt(i).IsTrailing()) { for (auto it = glyph.begin(); it != glyph.end(); ++it) diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index 2068e73af58..fd8249061c9 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -485,7 +485,7 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt WORD cleanAttr = legacyAttr; WI_ClearAllFlags(cleanAttr, COMMON_LVB_SBCSDBCS); // don't use legacy lead/trailing byte flags for colors - TextAttribute attr(cleanAttr); + const TextAttribute attr(cleanAttr); return s_GenerateView(attr); } diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index 84e100afa30..4fc9e0aa232 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -89,8 +89,8 @@ void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept { _wAttrLegacy = static_cast(wLegacy & META_ATTRS); WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS); - BYTE fgIndex = static_cast(wLegacy & FG_ATTRS); - BYTE bgIndex = static_cast(wLegacy & BG_ATTRS) >> 4; + const BYTE fgIndex = static_cast(wLegacy & FG_ATTRS); + const BYTE bgIndex = static_cast(wLegacy & BG_ATTRS) >> 4; _foreground = TextColor(fgIndex); _background = TextColor(bgIndex); } @@ -102,12 +102,12 @@ void TextAttribute::SetLegacyAttributes(const WORD attrs, { if (setForeground) { - BYTE fgIndex = (BYTE)(attrs & FG_ATTRS); + const BYTE fgIndex = (BYTE)(attrs & FG_ATTRS); _foreground = TextColor(fgIndex); } if (setBackground) { - BYTE bgIndex = (BYTE)(attrs & BG_ATTRS) >> 4; + const BYTE bgIndex = (BYTE)(attrs & BG_ATTRS) >> 4; _background = TextColor(bgIndex); } if (setMeta) @@ -133,12 +133,12 @@ void TextAttribute::SetIndexedAttributes(const std::optional foregro { if (foreground) { - BYTE fgIndex = (*foreground) & 0xFF; + const BYTE fgIndex = (*foreground) & 0xFF; _foreground = TextColor(fgIndex); } if (background) { - BYTE bgIndex = (*background) & 0xFF; + const BYTE bgIndex = (*background) & 0xFF; _background = TextColor(bgIndex); } } diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index 1a53bbb611e..f35b306fc6b 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -59,9 +59,9 @@ class TextAttribute final constexpr WORD GetLegacyAttributes() const noexcept { - BYTE fg = (_foreground.GetIndex() & FG_ATTRS); - BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS; - WORD meta = (_wAttrLegacy & META_ATTRS); + const BYTE fg = (_foreground.GetIndex() & FG_ATTRS); + const BYTE bg = (_background.GetIndex() << 4) & BG_ATTRS; + const WORD meta = (_wAttrLegacy & META_ATTRS); return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0); } @@ -80,11 +80,11 @@ class TextAttribute final constexpr WORD GetLegacyAttributes(const BYTE defaultFgIndex, const BYTE defaultBgIndex) const noexcept { - BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex; - BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex; - BYTE fg = (fgIndex & FG_ATTRS); - BYTE bg = (bgIndex << 4) & BG_ATTRS; - WORD meta = (_wAttrLegacy & META_ATTRS); + const BYTE fgIndex = _foreground.IsLegacy() ? _foreground.GetIndex() : defaultFgIndex; + const BYTE bgIndex = _background.IsLegacy() ? _background.GetIndex() : defaultBgIndex; + const BYTE fg = (fgIndex & FG_ATTRS); + const BYTE bg = (bgIndex << 4) & BG_ATTRS; + const WORD meta = (_wAttrLegacy & META_ATTRS); return (fg | bg | meta) | (_isBold ? FOREGROUND_INTENSITY : 0); } diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 09cae28d62b..a5f79c0b4cb 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -542,7 +542,7 @@ bool TextBuffer::IncrementCircularBuffer() _renderTarget.TriggerCircling(); // First, clean out the old "first row" as it will become the "last row" of the buffer after the circle is performed. - bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes); + const bool fSuccess = _storage.at(_firstRow).Reset(_currentAttributes); if (fSuccess) { // Now proceed to increment. @@ -1122,7 +1122,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo { // do not include \r nor \n as they don't have attributes // and are not HTML friendly. For line break use '
' instead. - bool isLastCharInRow = + const bool isLastCharInRow = col == rows.text.at(row).length() - 1 || rows.text.at(row).at(col + 1) == '\r' || rows.text.at(row).at(col + 1) == '\n'; diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 42e6d7c74ae..027450a07d2 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -146,7 +146,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, } // Resequence the resulting runs in order before returning to caller. - size_t totalRuns = _runs.size(); + const size_t totalRuns = _runs.size(); std::vector runs; runs.resize(totalRuns); @@ -178,7 +178,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, const auto textLength = gsl::narrow(_text.size()); // Estimate the maximum number of glyph indices needed to hold a string. - UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength); + const UINT32 estimatedGlyphCount = _EstimateGlyphCount(textLength); _glyphIndices.resize(estimatedGlyphCount); _glyphOffsets.resize(estimatedGlyphCount); @@ -230,8 +230,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // will shape as if the line is not broken. Run& run = _runs.at(runIndex); - UINT32 textStart = run.textStart; - UINT32 textLength = run.textLength; + const UINT32 textStart = run.textStart; + const UINT32 textLength = run.textLength; UINT32 maxGlyphCount = static_cast(_glyphIndices.size() - glyphStart); UINT32 actualGlyphCount = 0; @@ -291,7 +291,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, { // Try again using a larger buffer. maxGlyphCount = _EstimateGlyphCount(maxGlyphCount); - UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount; + const UINT32 totalGlyphsArrayCount = glyphStart + maxGlyphCount; glyphProps.resize(maxGlyphCount); _glyphIndices.resize(totalGlyphsArrayCount); @@ -477,7 +477,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex) { // Get the run - Run& run = _runs.at(runIndex); + const Run& run = _runs.at(runIndex); // Prepare the glyph run and description objects by converting our // internal storage representation into something that matches DWrite's structures. @@ -912,7 +912,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, if (textLength < runTextLength) { runTextLength = textLength; // Limit to what's actually left. - UINT32 runTextStart = run.textStart; + const UINT32 runTextStart = run.textStart; _SplitCurrentRun(runTextStart + runTextLength); } @@ -957,13 +957,13 @@ void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition) // - - Updates internal state, the back half will be selected after running void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition) { - UINT32 runTextStart = _runs.at(_runIndex).textStart; + const UINT32 runTextStart = _runs.at(_runIndex).textStart; if (splitPosition <= runTextStart) return; // no change // Grow runs by one. - size_t totalRuns = _runs.size(); + const size_t totalRuns = _runs.size(); try { _runs.resize(totalRuns + 1); @@ -979,7 +979,7 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition) backHalf = frontHalf; // Adjust runs' text positions and lengths. - UINT32 splitPoint = splitPosition - runTextStart; + const UINT32 splitPoint = splitPosition - runTextStart; backHalf.textStart += splitPoint; backHalf.textLength -= splitPoint; frontHalf.textLength = splitPoint; diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index b99c263a635..e459e97d1d7 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -165,7 +165,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, brush = static_cast(clientDrawingEffect); } - D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness); + const D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness); drawingContext->renderTarget->FillRectangle(&rect, brush); } @@ -233,12 +233,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, // Since we've delegated the drawing of the background of the text into this function, the origin passed in isn't actually the baseline. // It's the top left corner. Save that off first. - D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY); + const D2D1_POINT_2F origin = D2D1::Point2F(baselineOriginX, baselineOriginY); // Then make a copy for the baseline origin (which is part way down the left side of the text, not the top or bottom). // We'll use this baseline Origin for drawing the actual text. - D2D1_POINT_2F baselineOrigin = origin; - baselineOrigin.y += drawingContext->spacing.baseline; + const D2D1_POINT_2F baselineOrigin = { origin.x, origin.y + drawingContext->spacing.baseline }; ::Microsoft::WRL::ComPtr d2dContext; RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf())); @@ -270,7 +269,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, RETURN_IF_FAILED(drawingContext->dwriteFactory->QueryInterface(dwriteFactory4.GetAddressOf())); // The list of glyph image formats this renderer is prepared to support. - DWRITE_GLYPH_IMAGE_FORMATS supportedFormats = + const DWRITE_GLYPH_IMAGE_FORMATS supportedFormats = DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE | DWRITE_GLYPH_IMAGE_FORMATS_CFF | DWRITE_GLYPH_IMAGE_FORMATS_COLR | @@ -283,7 +282,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, // Determine whether there are any color glyph runs within glyphRun. If // there are, glyphRunEnumerator can be used to iterate through them. ::Microsoft::WRL::ComPtr glyphRunEnumerator; - HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin, + const HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, supportedFormats, @@ -320,7 +319,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, DWRITE_COLOR_GLYPH_RUN1 const* colorRun; RETURN_IF_FAILED(glyphRunEnumerator->GetCurrentRun(&colorRun)); - D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY); + const D2D1_POINT_2F currentBaselineOrigin = D2D1::Point2F(colorRun->baselineOriginX, colorRun->baselineOriginY); switch (colorRun->glyphImageFormat) { diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index b8549e04e2c..76a31b7be6a 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -147,7 +147,7 @@ DxEngine::~DxEngine() // D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED; - D3D_FEATURE_LEVEL FeatureLevels[] = { + const D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, @@ -277,7 +277,7 @@ DxEngine::~DxEngine() { RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&_dxgiSurface))); - D2D1_RENDER_TARGET_PROPERTIES props = + const D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties( D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), @@ -440,7 +440,7 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() noexcept // - S_OK [[nodiscard]] HRESULT DxEngine::InvalidateCursor(const COORD* const pcoordCursor) noexcept { - SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToInclusive(); + const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToInclusive(); return Invalidate(&sr); } @@ -671,7 +671,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept { UnionRect(&_invalidRect, &_invalidRect, &rc); - RECT rcScreen = _GetDisplayRect(); + const RECT rcScreen = _GetDisplayRect(); IntersectRect(&_invalidRect, &_invalidRect, &rcScreen); } else @@ -766,7 +766,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept { _presentDirty = _invalidRect; - RECT display = _GetDisplayRect(); + const RECT display = _GetDisplayRect(); SubtractRect(&_presentScroll, &display, &_presentDirty); _presentOffset.x = _invalidScroll.cx; _presentOffset.y = _invalidScroll.cy; @@ -1219,8 +1219,8 @@ enum class CursorPaintType [[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept { - short widthInChars = static_cast(viewInPixels.Width() / _glyphCell.cx); - short heightInChars = static_cast(viewInPixels.Height() / _glyphCell.cy); + const short widthInChars = static_cast(viewInPixels.Width() / _glyphCell.cx); + const short heightInChars = static_cast(viewInPixels.Height() / _glyphCell.cy); return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars }); } @@ -1345,7 +1345,7 @@ float DxEngine::GetScaling() const noexcept // - S_OK or relevant DirectWrite error. [[nodiscard]] HRESULT DxEngine::IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept { - Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout. + const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout. // Create the text layout CustomTextLayout layout(_dwriteFactory.Get(), @@ -1691,9 +1691,9 @@ float DxEngine::GetScaling() const noexcept // Unscaled is for the purposes of re-communicating this font back to the renderer again later. // As such, we need to give the same original size parameter back here without padding // or rounding or scaling manipulation. - COORD unscaled = desired.GetEngineSize(); + const COORD unscaled = desired.GetEngineSize(); - COORD scaled = coordSize; + const COORD scaled = coordSize; actual.SetFromEngine(fontName.data(), desired.GetFamily(), diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index c627a2b1fd8..241a0021657 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -443,7 +443,7 @@ bool CodepointWidthDetector::_checkFallbackViaCache(const std::wstring_view glyp const std::wstring findMe{ glyph }; // TODO: Cache needs to be emptied when font changes. - auto it = _fallbackCache.find(findMe); + const auto it = _fallbackCache.find(findMe); if (it == _fallbackCache.end()) { auto result = _pfnFallbackMethod(glyph); diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 8b043601103..531e5fd9b24 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -85,7 +85,7 @@ ScreenInfoUiaProviderBase::Release() { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::Release, nullptr); - long val = InterlockedDecrement(&_cRefs); + const long val = InterlockedDecrement(&_cRefs); if (val == 0) { delete this; @@ -260,7 +260,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ *ppRuntimeId = nullptr; // AppendRuntimeId is a magic Number that tells UIAutomation to Append its own Runtime ID(From the HWND) - int rId[] = { UiaAppendRuntimeId, -1 }; + const int rId[] = { UiaAppendRuntimeId, -1 }; // BuildIntSafeArray is a custom function to hide the SafeArray creation *ppRuntimeId = BuildIntSafeArray(rId, 2); RETURN_IF_NULL_ALLOC(*ppRuntimeId); diff --git a/src/types/UTF8OutPipeReader.cpp b/src/types/UTF8OutPipeReader.cpp index 876dafa75b2..85b60b30293 100644 --- a/src/types/UTF8OutPipeReader.cpp +++ b/src/types/UTF8OutPipeReader.cpp @@ -49,7 +49,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : if (!fSuccess) // reading failed (we must check this first, because dwRead will also be 0.) { - auto lastError = GetLastError(); + const auto lastError = GetLastError(); if (lastError == ERROR_BROKEN_PIPE) { // This is a successful, but detectable, exit. @@ -73,7 +73,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : if ((*backIter & _Utf8BitMasks::MaskAsciiByte) > _Utf8BitMasks::IsAsciiByte) { // Check only up to 3 last bytes, if no Lead Byte was found then the byte before must be the Lead Byte and no partials are in the buffer - for (DWORD dwSequenceLen{ 1UL }, stop{ dwRead < 4UL ? dwRead : 4UL }; dwSequenceLen < stop; ++dwSequenceLen, --backIter) + for (DWORD dwSequenceLen{ 1UL }; dwSequenceLen < std::min(dwRead, 4UL); ++dwSequenceLen, --backIter) { // If Lead Byte found if ((*backIter & _Utf8BitMasks::MaskContinuationByte) > _Utf8BitMasks::IsContinuationByte) diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index f4932f70dec..5db8c1329fb 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -469,7 +469,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ const unsigned int totalRowsInRange = _rowCountInRange(_pData); for (unsigned int i = 0; i < totalRowsInRange; ++i) { - ScreenInfoRow screenInfoRow = _textBufferRowToScreenInfoRow(_pData, startRow + i); + const ScreenInfoRow screenInfoRow = _textBufferRowToScreenInfoRow(_pData, startRow + i); if (!_isScreenInfoRowInViewport(_pData, screenInfoRow)) { continue; @@ -657,12 +657,12 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit, moveFunc = &_moveByLine; } - MovementDirection moveDirection = (count > 0) ? MovementDirection::Forward : MovementDirection::Backward; + const MovementDirection moveDirection = (count > 0) ? MovementDirection::Forward : MovementDirection::Backward; std::pair newEndpoints; try { - MoveState moveState{ _pData, *this, moveDirection }; + const MoveState moveState{ _pData, *this, moveDirection }; newEndpoints = moveFunc(_pData, count, moveState, @@ -721,7 +721,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin _outputRowConversions(); #endif - MovementDirection moveDirection = (count > 0) ? MovementDirection::Forward : MovementDirection::Backward; + const MovementDirection moveDirection = (count > 0) ? MovementDirection::Forward : MovementDirection::Backward; auto moveFunc = &_moveEndpointByUnitDocument; if (unit == TextUnit::TextUnit_Character) @@ -736,7 +736,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin std::tuple moveResults; try { - MoveState moveState{ _pData, *this, moveDirection }; + const MoveState moveState{ _pData, *this, moveDirection }; moveResults = moveFunc(_pData, count, endpoint, moveState, pRetVal); } CATCH_RETURN(); @@ -1207,7 +1207,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, const SMALL_RECT viewport) { - ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); + const ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && viewportRow < static_cast(_getViewportHeight(viewport)); } @@ -1445,7 +1445,7 @@ std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData _Out_ int* const pAmountMoved) { *pAmountMoved = 0; - int count = moveCount; + const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; Column currentColumn = moveState.StartColumn; @@ -1492,7 +1492,7 @@ std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaDat { THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; - int count = moveCount; + const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; Column currentColumn = moveState.StartColumn; @@ -1645,7 +1645,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, { THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; - int count = moveCount; + const int count = moveCount; ScreenInfoRow currentScreenInfoRow; Column currentColumn; @@ -1693,7 +1693,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; + const Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; bool degenerate = false; @@ -1735,7 +1735,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, { THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; - int count = moveCount; + const int count = moveCount; ScreenInfoRow currentScreenInfoRow; Column currentColumn; @@ -1784,7 +1784,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; + const Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; bool degenerate = false; @@ -1849,7 +1849,7 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(I return std::make_tuple(start, end, degenerate); } - MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward; + const MovementDirection moveDirection = (moveCount > 0) ? MovementDirection::Forward : MovementDirection::Backward; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { @@ -1954,7 +1954,7 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(I } // translate the row back to an endpoint and handle any crossed endpoints - Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; + const Endpoint convertedEndpoint = _screenInfoRowToEndpoint(pData, currentScreenInfoRow) + currentColumn; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { start = convertedEndpoint; diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index 8faa65ff1f9..e2072046b4c 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -26,7 +26,7 @@ WindowUiaProviderBase::AddRef() IFACEMETHODIMP_(ULONG) WindowUiaProviderBase::Release() { - long val = InterlockedDecrement(&_cRefs); + const long val = InterlockedDecrement(&_cRefs); if (val == 0) { delete this; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index f3fae2b85e2..adfeccf97ab 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -98,9 +98,9 @@ COLORREF Utils::ColorFromHexString(const std::string str) std::string gStr{ &str.at(3), 2 }; std::string bStr{ &str.at(5), 2 }; - BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); - BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); - BYTE b = static_cast(std::stoul(bStr, nullptr, 16)); + const BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); + const BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); + const BYTE b = static_cast(std::stoul(bStr, nullptr, 16)); return RGB(r, g, b); } From c63289b1143dcc46a4bb0ad6e1d2b0971aa45e7a Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 12:45:16 -0700 Subject: [PATCH 082/154] C26493, no C-style casts. --- src/buffer/out/TextAttribute.cpp | 4 ++-- src/buffer/out/cursor.cpp | 14 +++++++------- src/renderer/dx/CustomTextRenderer.cpp | 2 +- src/renderer/dx/DxRenderer.cpp | 18 +++++++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index 4fc9e0aa232..36d28b1605d 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -102,12 +102,12 @@ void TextAttribute::SetLegacyAttributes(const WORD attrs, { if (setForeground) { - const BYTE fgIndex = (BYTE)(attrs & FG_ATTRS); + const BYTE fgIndex = gsl::narrow_cast(attrs & FG_ATTRS); _foreground = TextColor(fgIndex); } if (setBackground) { - const BYTE bgIndex = (BYTE)(attrs & BG_ATTRS) >> 4; + const BYTE bgIndex = gsl::narrow_cast(attrs & BG_ATTRS) >> 4; _background = TextColor(bgIndex); } if (setMeta) diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 6cb69597897..946d0ecfe7e 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -207,7 +207,7 @@ void Cursor::SetPosition(const COORD cPosition) void Cursor::SetXPosition(const int NewX) { _RedrawCursor(); - _cPosition.X = (SHORT)NewX; + _cPosition.X = gsl::narrow(NewX); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -215,7 +215,7 @@ void Cursor::SetXPosition(const int NewX) void Cursor::SetYPosition(const int NewY) { _RedrawCursor(); - _cPosition.Y = (SHORT)NewY; + _cPosition.Y = gsl::narrow(NewY); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -223,7 +223,7 @@ void Cursor::SetYPosition(const int NewY) void Cursor::IncrementXPosition(const int DeltaX) { _RedrawCursor(); - _cPosition.X += (SHORT)DeltaX; + _cPosition.X += gsl::narrow(DeltaX); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -231,7 +231,7 @@ void Cursor::IncrementXPosition(const int DeltaX) void Cursor::IncrementYPosition(const int DeltaY) { _RedrawCursor(); - _cPosition.Y += (SHORT)DeltaY; + _cPosition.Y += gsl::narrow(DeltaY); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -239,7 +239,7 @@ void Cursor::IncrementYPosition(const int DeltaY) void Cursor::DecrementXPosition(const int DeltaX) { _RedrawCursor(); - _cPosition.X -= (SHORT)DeltaX; + _cPosition.X -= gsl::narrow(DeltaX); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -247,7 +247,7 @@ void Cursor::DecrementXPosition(const int DeltaX) void Cursor::DecrementYPosition(const int DeltaY) { _RedrawCursor(); - _cPosition.Y -= (SHORT)DeltaY; + _cPosition.Y -= gsl::narrow(DeltaY); _RedrawCursor(); ResetDelayEOLWrap(); } @@ -342,7 +342,7 @@ const COLORREF Cursor::GetColor() const void Cursor::SetColor(const unsigned int color) { - _color = (COLORREF)color; + _color = static_cast(color); } void Cursor::SetType(const CursorType type) diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index e459e97d1d7..1c7edfff295 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -63,7 +63,7 @@ using namespace Microsoft::Console::Render; DrawingContext* drawingContext = static_cast(clientDrawingContext); // Matrix structures are defined identically - drawingContext->renderTarget->GetTransform((D2D1_MATRIX_3X2_F*)transform); + drawingContext->renderTarget->GetTransform(reinterpret_cast(transform)); return S_OK; } #pragma endregion diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 76a31b7be6a..49c4794662e 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -377,8 +377,8 @@ void DxEngine::_ReleaseDeviceResources() noexcept return _dwriteFactory->CreateTextLayout(string, static_cast(stringLength), _dwriteTextFormat.Get(), - (float)_displaySizePixels.cx, - _glyphCell.cy != 0 ? _glyphCell.cy : (float)_displaySizePixels.cy, + gsl::narrow(_displaySizePixels.cx), + _glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow( _displaySizePixels.cy), ppTextLayout); } @@ -1089,7 +1089,7 @@ enum class CursorPaintType { // Enforce min/max cursor height ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, s_ulMinCursorHeightPercent, s_ulMaxCursorHeightPercent); - ulHeight = (ULONG)((_glyphCell.cy * ulHeight) / 100); + ulHeight = gsl::narrow((_glyphCell.cy * ulHeight) / 100); rect.top = rect.bottom - ulHeight; break; } @@ -1300,10 +1300,10 @@ float DxEngine::GetScaling() const noexcept [[nodiscard]] SMALL_RECT DxEngine::GetDirtyRectInChars() noexcept { SMALL_RECT r; - r.Top = (SHORT)(floor(_invalidRect.top / _glyphCell.cy)); - r.Left = (SHORT)(floor(_invalidRect.left / _glyphCell.cx)); - r.Bottom = (SHORT)(floor(_invalidRect.bottom / _glyphCell.cy)); - r.Right = (SHORT)(floor(_invalidRect.right / _glyphCell.cx)); + r.Top = gsl::narrow(floor(_invalidRect.top / _glyphCell.cy)); + r.Left = gsl::narrow(floor(_invalidRect.left / _glyphCell.cx)); + r.Bottom = gsl::narrow(floor(_invalidRect.bottom / _glyphCell.cy)); + r.Right = gsl::narrow(floor(_invalidRect.right / _glyphCell.cx)); // Exclusive to inclusive r.Bottom--; @@ -1321,7 +1321,7 @@ float DxEngine::GetScaling() const noexcept // - Nearest integer short x and y values for each cell. [[nodiscard]] COORD DxEngine::_GetFontSize() const noexcept { - return { (SHORT)(_glyphCell.cx), (SHORT)(_glyphCell.cy) }; + return { gsl::narrow(_glyphCell.cx), gsl::narrow(_glyphCell.cy) }; } // Routine Description: @@ -1371,7 +1371,7 @@ float DxEngine::GetScaling() const noexcept // - S_OK [[nodiscard]] HRESULT DxEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept { - return PostMessageW(_hwndTarget, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL; + return PostMessageW(_hwndTarget, CM_UPDATE_TITLE, 0, 0) ? S_OK : E_FAIL; } // Routine Description: From a381f6a042c953c573cc19b6b74b94c798f06fe8 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 13:07:08 -0700 Subject: [PATCH 083/154] C26435, choose one of `virtual`, `override`, or `final` --- src/renderer/dx/CustomTextLayout.h | 50 ++++++++++----------- src/renderer/dx/CustomTextRenderer.h | 66 ++++++++++++++-------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index 6e57dc5c986..b02d263a0eb 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -35,34 +35,34 @@ namespace Microsoft::Console::Render FLOAT originY); // IDWriteTextAnalysisSource methods - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition, - _Outptr_result_buffer_(*textLength) WCHAR const** textString, - _Out_ UINT32* textLength) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition, - _Outptr_result_buffer_(*textLength) WCHAR const** textString, - _Out_ UINT32* textLength) override; - [[nodiscard]] virtual DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition, + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition, + _Outptr_result_buffer_(*textLength) WCHAR const** textString, + _Out_ UINT32* textLength) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition, + _Outptr_result_buffer_(*textLength) WCHAR const** textString, + _Out_ UINT32* textLength) override; + [[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition, + _Out_ UINT32* textLength, + _Outptr_result_z_ WCHAR const** localeName) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition, _Out_ UINT32* textLength, - _Outptr_result_z_ WCHAR const** localeName) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition, - _Out_ UINT32* textLength, - _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) override; + _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) override; // IDWriteTextAnalysisSink methods - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition, - UINT32 textLength, - _In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition, - UINT32 textLength, - _In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition, - UINT32 textLength, - UINT8 explicitLevel, - UINT8 resolvedLevel) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition, - UINT32 textLength, - _In_ IDWriteNumberSubstitution* numberSubstitution) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition, + UINT32 textLength, + _In_ DWRITE_SCRIPT_ANALYSIS const* scriptAnalysis) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE SetLineBreakpoints(UINT32 textPosition, + UINT32 textLength, + _In_reads_(textLength) DWRITE_LINE_BREAKPOINT const* lineBreakpoints) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE SetBidiLevel(UINT32 textPosition, + UINT32 textLength, + UINT8 explicitLevel, + UINT8 resolvedLevel) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE SetNumberSubstitution(UINT32 textPosition, + UINT32 textLength, + _In_ IDWriteNumberSubstitution* numberSubstitution) override; protected: // A single contiguous run of characters containing the same analysis results. diff --git a/src/renderer/dx/CustomTextRenderer.h b/src/renderer/dx/CustomTextRenderer.h index f64623c11e8..e539f945372 100644 --- a/src/renderer/dx/CustomTextRenderer.h +++ b/src/renderer/dx/CustomTextRenderer.h @@ -42,43 +42,43 @@ namespace Microsoft::Console::Render // https://docs.microsoft.com/en-us/windows/desktop/DirectWrite/how-to-implement-a-custom-text-renderer // IDWritePixelSnapping methods - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void* clientDrawingContext, - _Out_ BOOL* isDisabled) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void* clientDrawingContext, + _Out_ BOOL* isDisabled) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void* clientDrawingContext, - _Out_ FLOAT* pixelsPerDip) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void* clientDrawingContext, + _Out_ FLOAT* pixelsPerDip) override; - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void* clientDrawingContext, - _Out_ DWRITE_MATRIX* transform) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE GetCurrentTransform(void* clientDrawingContext, + _Out_ DWRITE_MATRIX* transform) override; // IDWriteTextRenderer methods - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - DWRITE_MEASURING_MODE measuringMode, - _In_ const DWRITE_GLYPH_RUN* glyphRun, - _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, - IUnknown* clientDrawingEffect) override; - - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - _In_ const DWRITE_UNDERLINE* underline, - IUnknown* clientDrawingEffect) override; - - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void* clientDrawingContext, - FLOAT baselineOriginX, - FLOAT baselineOriginY, - _In_ const DWRITE_STRIKETHROUGH* strikethrough, - IUnknown* clientDrawingEffect) override; - - [[nodiscard]] virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void* clientDrawingContext, - FLOAT originX, - FLOAT originY, - IDWriteInlineObject* inlineObject, - BOOL isSideways, - BOOL isRightToLeft, - IUnknown* clientDrawingEffect) override; + [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawGlyphRun(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE measuringMode, + _In_ const DWRITE_GLYPH_RUN* glyphRun, + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, + IUnknown* clientDrawingEffect) override; + + [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawUnderline(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + _In_ const DWRITE_UNDERLINE* underline, + IUnknown* clientDrawingEffect) override; + + [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawStrikethrough(void* clientDrawingContext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + _In_ const DWRITE_STRIKETHROUGH* strikethrough, + IUnknown* clientDrawingEffect) override; + + [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawInlineObject(void* clientDrawingContext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject* inlineObject, + BOOL isSideways, + BOOL isRightToLeft, + IUnknown* clientDrawingEffect) override; private: void _FillRectangle(void* clientDrawingContext, From 8ea7401dc97203bb3097e33a2c4dd12211aabe09 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 13:19:01 -0700 Subject: [PATCH 084/154] C26472, no static_cast for arithmetic conversions. narrow or narrow_cast --- src/buffer/out/TextAttribute.cpp | 6 ++-- src/buffer/out/TextAttribute.hpp | 6 ++-- src/buffer/out/cursor.cpp | 2 +- src/buffer/out/textBuffer.cpp | 6 ++-- src/renderer/dx/CustomTextLayout.cpp | 8 +++--- src/renderer/dx/DxRenderer.cpp | 6 ++-- src/types/ScreenInfoUiaProviderBase.cpp | 8 +++--- src/types/UiaTextRangeBase.cpp | 38 ++++++++++++------------- src/types/WindowUiaProviderBase.cpp | 2 +- src/types/inc/utils.hpp | 2 +- src/types/utils.cpp | 6 ++-- 11 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index 36d28b1605d..b224c6c8ad5 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -87,10 +87,10 @@ void TextAttribute::SetBackground(const COLORREF rgbBackground) void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept { - _wAttrLegacy = static_cast(wLegacy & META_ATTRS); + _wAttrLegacy = gsl::narrow_cast(wLegacy & META_ATTRS); WI_ClearAllFlags(_wAttrLegacy, COMMON_LVB_SBCSDBCS); - const BYTE fgIndex = static_cast(wLegacy & FG_ATTRS); - const BYTE bgIndex = static_cast(wLegacy & BG_ATTRS) >> 4; + const BYTE fgIndex = gsl::narrow_cast(wLegacy & FG_ATTRS); + const BYTE bgIndex = gsl::narrow_cast(wLegacy & BG_ATTRS) >> 4; _foreground = TextColor(fgIndex); _background = TextColor(bgIndex); } diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index f35b306fc6b..eb402c61f35 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -39,9 +39,9 @@ class TextAttribute final } constexpr TextAttribute(const WORD wLegacyAttr) noexcept : - _wAttrLegacy{ static_cast(wLegacyAttr & META_ATTRS) }, - _foreground{ static_cast(wLegacyAttr & FG_ATTRS) }, - _background{ static_cast((wLegacyAttr & BG_ATTRS) >> 4) }, + _wAttrLegacy{ gsl::narrow_cast(wLegacyAttr & META_ATTRS) }, + _foreground{ gsl::narrow_cast(wLegacyAttr & FG_ATTRS) }, + _background{ gsl::narrow_cast((wLegacyAttr & BG_ATTRS) >> 4) }, _isBold{ false } { // If we're given lead/trailing byte information with the legacy color, strip it. diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 946d0ecfe7e..97ed5f1ea8a 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -342,7 +342,7 @@ const COLORREF Cursor::GetColor() const void Cursor::SetColor(const unsigned int color) { - _color = static_cast(color); + _color = gsl::narrow_cast(color); } void Cursor::SetType(const CursorType type) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index a5f79c0b4cb..c563fc1e562 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -62,7 +62,7 @@ void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) // - Total number of rows in the buffer UINT TextBuffer::TotalRowCount() const { - return static_cast(_storage.size()); + return gsl::narrow(_storage.size()); } // Routine Description: @@ -586,7 +586,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y); // The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character) - coordEndOfText.X = static_cast(pCurrRow->GetCharRow().MeasureRight()) - 1; + coordEndOfText.X = gsl::narrow(pCurrRow->GetCharRow().MeasureRight()) - 1; // If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text. const auto viewportTop = viewport.Top(); @@ -597,7 +597,7 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View pCurrRow = &GetRowByOffset(coordEndOfText.Y); // We need to back up to the previous row if this line is empty, AND there are more rows - coordEndOfText.X = static_cast(pCurrRow->GetCharRow().MeasureRight()) - 1; + coordEndOfText.X = gsl::narrow(pCurrRow->GetCharRow().MeasureRight()) - 1; fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop); } diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 027450a07d2..230d1ba56de 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -232,7 +232,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, Run& run = _runs.at(runIndex); const UINT32 textStart = run.textStart; const UINT32 textLength = run.textLength; - UINT32 maxGlyphCount = static_cast(_glyphIndices.size() - glyphStart); + UINT32 maxGlyphCount = gsl::narrow(_glyphIndices.size() - glyphStart); UINT32 actualGlyphCount = 0; run.glyphStart = glyphStart; @@ -945,7 +945,7 @@ void CustomTextLayout::_SetCurrentRun(const UINT32 textPosition) return; } - _runIndex = static_cast( + _runIndex = gsl::narrow( std::find(_runs.begin(), _runs.end(), textPosition) - _runs.begin()); } @@ -983,7 +983,7 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition) backHalf.textStart += splitPoint; backHalf.textLength -= splitPoint; frontHalf.textLength = splitPoint; - frontHalf.nextRunIndex = static_cast(totalRuns); - _runIndex = static_cast(totalRuns); + frontHalf.nextRunIndex = gsl::narrow(totalRuns); + _runIndex = gsl::narrow(totalRuns); } #pragma endregion diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 49c4794662e..cecdb78e797 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -375,7 +375,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept _Out_ IDWriteTextLayout** ppTextLayout) noexcept { return _dwriteFactory->CreateTextLayout(string, - static_cast(stringLength), + gsl::narrow(stringLength), _dwriteTextFormat.Get(), gsl::narrow(_displaySizePixels.cx), _glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow( _displaySizePixels.cy), @@ -1219,8 +1219,8 @@ enum class CursorPaintType [[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept { - const short widthInChars = static_cast(viewInPixels.Width() / _glyphCell.cx); - const short heightInChars = static_cast(viewInPixels.Height() / _glyphCell.cy); + const short widthInChars = gsl::narrow(viewInPixels.Width() / _glyphCell.cx); + const short heightInChars = gsl::narrow(viewInPixels.Height() / _glyphCell.cy); return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars }); } diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 531e5fd9b24..c68dd7d1750 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -379,14 +379,14 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ //apiMsg.SelectionRowCount = static_cast(ranges.size()); // make a safe array - *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, static_cast(ranges.size())); + *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, gsl::narrow(ranges.size())); if (*ppRetVal == nullptr) { return E_OUTOFMEMORY; } // fill the safe array - for (LONG i = 0; i < static_cast(ranges.size()); ++i) + for (LONG i = 0; i < gsl::narrow(ranges.size()); ++i) { hr = SafeArrayPutElement(*ppRetVal, &i, reinterpret_cast(ranges.at(i))); if (FAILED(hr)) @@ -428,7 +428,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben // make a safe array const size_t rowCount = viewport.Height(); - *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, static_cast(rowCount)); + *ppRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, gsl::narrow(rowCount)); if (*ppRetVal == nullptr) { return E_OUTOFMEMORY; @@ -473,7 +473,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben return hr; } - LONG currentIndex = static_cast(i); + LONG currentIndex = gsl::narrow(i); hr = SafeArrayPutElement(*ppRetVal, ¤tIndex, reinterpret_cast(range)); if (FAILED(hr)) { diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 5db8c1329fb..3deb97ad49e 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -479,13 +479,13 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ } // convert to a safearray - *ppRetVal = SafeArrayCreateVector(VT_R8, 0, static_cast(coords.size())); + *ppRetVal = SafeArrayCreateVector(VT_R8, 0, gsl::narrow(coords.size())); if (*ppRetVal == nullptr) { return E_OUTOFMEMORY; } HRESULT hr; - for (LONG i = 0; i < static_cast(coords.size()); ++i) + for (LONG i = 0; i < gsl::narrow(coords.size()); ++i) { hr = SafeArrayPutElement(*ppRetVal, &i, &coords.at(i)); if (FAILED(hr)) @@ -880,11 +880,11 @@ IFACEMETHODIMP UiaTextRangeBase::Select() COORD coordStart; COORD coordEnd; - coordStart.X = static_cast(_endpointToColumn(_pData, _start)); - coordStart.Y = static_cast(_endpointToScreenInfoRow(_pData, _start)); + coordStart.X = gsl::narrow(_endpointToColumn(_pData, _start)); + coordStart.Y = gsl::narrow(_endpointToScreenInfoRow(_pData, _start)); - coordEnd.X = static_cast(_endpointToColumn(_pData, _end)); - coordEnd.Y = static_cast(_endpointToScreenInfoRow(_pData, _end)); + coordEnd.X = gsl::narrow(_endpointToColumn(_pData, _end)); + coordEnd.Y = gsl::narrow(_endpointToScreenInfoRow(_pData, _end)); _pData->SelectNewRegion(coordStart, coordEnd); } @@ -948,15 +948,15 @@ IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) if (startScreenInfoRow + viewportHeight <= bottomRow) { // we can align to the top - newViewport.Top = static_cast(startScreenInfoRow); - newViewport.Bottom = static_cast(startScreenInfoRow + viewportHeight - 1); + newViewport.Top = gsl::narrow(startScreenInfoRow); + newViewport.Bottom = gsl::narrow(startScreenInfoRow + viewportHeight - 1); } else { // we can align to the top so we'll just move the viewport // to the bottom of the screen buffer - newViewport.Bottom = static_cast(bottomRow); - newViewport.Top = static_cast(bottomRow - viewportHeight + 1); + newViewport.Bottom = gsl::narrow(bottomRow); + newViewport.Top = gsl::narrow(bottomRow - viewportHeight + 1); } } else @@ -966,20 +966,20 @@ IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) if (endScreenInfoRow >= viewportHeight) { // we can align to bottom - newViewport.Bottom = static_cast(endScreenInfoRow); - newViewport.Top = static_cast(endScreenInfoRow - viewportHeight + 1); + newViewport.Bottom = gsl::narrow(endScreenInfoRow); + newViewport.Top = gsl::narrow(endScreenInfoRow - viewportHeight + 1); } else { // we can't align to bottom so we'll move the viewport to // the top of the screen buffer - newViewport.Top = static_cast(topRow); - newViewport.Bottom = static_cast(topRow + viewportHeight - 1); + newViewport.Top = gsl::narrow(topRow); + newViewport.Bottom = gsl::narrow(topRow + viewportHeight - 1); } } - FAIL_FAST_IF(!(newViewport.Top >= static_cast(topRow))); - FAIL_FAST_IF(!(newViewport.Bottom <= static_cast(bottomRow))); + FAIL_FAST_IF(!(newViewport.Top >= gsl::narrow(topRow))); + FAIL_FAST_IF(!(newViewport.Bottom <= gsl::narrow(bottomRow))); FAIL_FAST_IF(!(_getViewportHeight(oldViewport) == _getViewportHeight(newViewport))); try @@ -1209,7 +1209,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, { const ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && - viewportRow < static_cast(_getViewportHeight(viewport)); + viewportRow < gsl::narrow(_getViewportHeight(viewport)); } // Routine Description: @@ -1513,7 +1513,7 @@ std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaDat // get the right cell for the next row const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); - currentColumn = static_cast((right == 0) ? 0 : right - 1); + currentColumn = gsl::narrow((right == 0) ? 0 : right - 1); } else { @@ -1768,7 +1768,7 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, // get the right cell for the next row const ROW& row = pData->GetTextBuffer().GetRowByOffset(currentScreenInfoRow); const size_t right = row.GetCharRow().MeasureRight(); - currentColumn = static_cast((right == 0) ? 0 : right - 1); + currentColumn = gsl::narrow((right == 0) ? 0 : right - 1); } else { diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index e2072046b4c..7ff719081bd 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -155,7 +155,7 @@ IFACEMETHODIMP WindowUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_res } catch (...) { - return static_cast(UIA_E_ELEMENTNOTAVAILABLE); + return gsl::narrow_cast(UIA_E_ELEMENTNOTAVAILABLE); } } #pragma endregion diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index 8d3cd50cc87..646dfe10ed4 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -46,7 +46,7 @@ namespace Microsoft::Console::Utils constexpr unsigned long EndianSwap(unsigned long value) { - return static_cast(EndianSwap(static_cast(value))); + return gsl::narrow_cast(EndianSwap(gsl::narrow_cast(value))); } constexpr GUID EndianSwap(GUID value) diff --git a/src/types/utils.cpp b/src/types/utils.cpp index adfeccf97ab..710dd74df3c 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -98,9 +98,9 @@ COLORREF Utils::ColorFromHexString(const std::string str) std::string gStr{ &str.at(3), 2 }; std::string bStr{ &str.at(5), 2 }; - const BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); - const BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); - const BYTE b = static_cast(std::stoul(bStr, nullptr, 16)); + const BYTE r = gsl::narrow_cast(std::stoul(rStr, nullptr, 16)); + const BYTE g = gsl::narrow_cast(std::stoul(gStr, nullptr, 16)); + const BYTE b = gsl::narrow_cast(std::stoul(bStr, nullptr, 16)); return RGB(r, g, b); } From 50e2d0c4335fdca3a336ab9ad1a8257e233d5837 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 13:23:32 -0700 Subject: [PATCH 085/154] C26433, overrides should be explicit. --- src/buffer/out/OutputCell.hpp | 2 +- src/types/ScreenInfoUiaProviderBase.h | 32 +++++++++++----------- src/types/UiaTextRangeBase.hpp | 38 +++++++++++++-------------- src/types/WindowUiaProviderBase.hpp | 22 ++++++++-------- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/buffer/out/OutputCell.hpp b/src/buffer/out/OutputCell.hpp index 8599ddf7cb3..3d100bab6c6 100644 --- a/src/buffer/out/OutputCell.hpp +++ b/src/buffer/out/OutputCell.hpp @@ -25,7 +25,7 @@ Module Name: class InvalidCharInfoConversionException : public std::exception { - const char* what() const noexcept + const char* what() const noexcept override { return "Cannot convert to CHAR_INFO without explicit TextAttribute"; } diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h index 0b47454eefa..f859ec67b6f 100644 --- a/src/types/ScreenInfoUiaProviderBase.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -44,38 +44,38 @@ namespace Microsoft::Console::Types // IUnknown methods IFACEMETHODIMP_(ULONG) - AddRef(); + AddRef() override; IFACEMETHODIMP_(ULONG) - Release(); + Release() override; IFACEMETHODIMP QueryInterface(_In_ REFIID riid, - _COM_Outptr_result_maybenull_ void** ppInterface); + _COM_Outptr_result_maybenull_ void** ppInterface) override; // IRawElementProviderSimple methods - IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions); + IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) override; IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, - _COM_Outptr_result_maybenull_ IUnknown** ppInterface); + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) override; IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, - _Out_ VARIANT* pVariant); - IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider); + _Out_ VARIANT* pVariant) override; + IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) override; // IRawElementProviderFragment methods virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; - IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId); + IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) override; virtual IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) = 0; - IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots); - IFACEMETHODIMP SetFocus(); + IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) override; + IFACEMETHODIMP SetFocus() override; virtual IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) = 0; // ITextProvider - IFACEMETHODIMP GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); - IFACEMETHODIMP GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); + IFACEMETHODIMP GetSelection(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; + IFACEMETHODIMP GetVisibleRanges(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; IFACEMETHODIMP RangeFromChild(_In_ IRawElementProviderSimple* childElement, - _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); + _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; IFACEMETHODIMP RangeFromPoint(_In_ UiaPoint point, - _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); - IFACEMETHODIMP get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); - IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal); + _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + IFACEMETHODIMP get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) override; protected: virtual std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) = 0; diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 1d1f957af3f..336adbeca2a 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -150,49 +150,49 @@ namespace Microsoft::Console::Types // IUnknown methods IFACEMETHODIMP_(ULONG) - AddRef(); + AddRef() override; IFACEMETHODIMP_(ULONG) - Release(); + Release() override; IFACEMETHODIMP QueryInterface(_In_ REFIID riid, - _COM_Outptr_result_maybenull_ void** ppInterface); + _COM_Outptr_result_maybenull_ void** ppInterface) override; // ITextRangeProvider methods virtual IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; - IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal); + IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) override; IFACEMETHODIMP CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, _In_ ITextRangeProvider* pTargetRange, _In_ TextPatternRangeEndpoint targetEndpoint, - _Out_ int* pRetVal); - IFACEMETHODIMP ExpandToEnclosingUnit(_In_ TextUnit unit); + _Out_ int* pRetVal) override; + IFACEMETHODIMP ExpandToEnclosingUnit(_In_ TextUnit unit) override; IFACEMETHODIMP FindAttribute(_In_ TEXTATTRIBUTEID textAttributeId, _In_ VARIANT val, _In_ BOOL searchBackward, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal); + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; virtual IFACEMETHODIMP FindText(_In_ BSTR text, _In_ BOOL searchBackward, _In_ BOOL ignoreCase, _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; IFACEMETHODIMP GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, - _Out_ VARIANT* pRetVal); - IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); - IFACEMETHODIMP GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal); + _Out_ VARIANT* pRetVal) override; + IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; + IFACEMETHODIMP GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal) override; IFACEMETHODIMP GetText(_In_ int maxLength, - _Out_ BSTR* pRetVal); + _Out_ BSTR* pRetVal) override; IFACEMETHODIMP Move(_In_ TextUnit unit, _In_ int count, - _Out_ int* pRetVal); + _Out_ int* pRetVal) override; IFACEMETHODIMP MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint, _In_ TextUnit unit, _In_ int count, - _Out_ int* pRetVal); + _Out_ int* pRetVal) override; IFACEMETHODIMP MoveEndpointByRange(_In_ TextPatternRangeEndpoint endpoint, _In_ ITextRangeProvider* pTargetRange, - _In_ TextPatternRangeEndpoint targetEndpoint); - IFACEMETHODIMP Select(); - IFACEMETHODIMP AddToSelection(); - IFACEMETHODIMP RemoveFromSelection(); - IFACEMETHODIMP ScrollIntoView(_In_ BOOL alignToTop); - IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal); + _In_ TextPatternRangeEndpoint targetEndpoint) override; + IFACEMETHODIMP Select() override; + IFACEMETHODIMP AddToSelection() override; + IFACEMETHODIMP RemoveFromSelection() override; + IFACEMETHODIMP ScrollIntoView(_In_ BOOL alignToTop) override; + IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; protected: #if _DEBUG diff --git a/src/types/WindowUiaProviderBase.hpp b/src/types/WindowUiaProviderBase.hpp index d78739a511b..7b84dee8caa 100644 --- a/src/types/WindowUiaProviderBase.hpp +++ b/src/types/WindowUiaProviderBase.hpp @@ -37,28 +37,28 @@ namespace Microsoft::Console::Types // IUnknown methods IFACEMETHODIMP_(ULONG) - AddRef(); + AddRef() override; IFACEMETHODIMP_(ULONG) - Release(); + Release() override; IFACEMETHODIMP QueryInterface(_In_ REFIID riid, - _COM_Outptr_result_maybenull_ void** ppInterface); + _COM_Outptr_result_maybenull_ void** ppInterface) override; // IRawElementProviderSimple methods - IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions); + IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) override; IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, - _COM_Outptr_result_maybenull_ IUnknown** ppInterface); + _COM_Outptr_result_maybenull_ IUnknown** ppInterface) override; IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, - _Out_ VARIANT* pVariant); - IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider); + _Out_ VARIANT* pVariant) override; + IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) override; // IRawElementProviderFragment methods virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; - IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId); - IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect); - IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots); + IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) override; + IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) override; + IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) override; virtual IFACEMETHODIMP SetFocus() = 0; - IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider); + IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) override; // IRawElementProviderFragmentRoot methods virtual IFACEMETHODIMP ElementProviderFromPoint(_In_ double x, From 8579d8905a90710174fc170bc37fe8111dd2e02a Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 13:41:51 -0700 Subject: [PATCH 086/154] C26451, promote before arithmetic if storing in larger result size (or use safe math) --- src/buffer/out/TextColor.cpp | 4 ++-- src/buffer/out/textBuffer.cpp | 10 +++++----- src/buffer/out/textBufferCellIterator.cpp | 2 +- src/renderer/dx/CustomTextLayout.cpp | 8 ++++---- src/types/UiaTextRangeBase.cpp | 10 +++++----- src/types/WindowUiaProviderBase.cpp | 9 +++++++-- 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index af90f9ff857..cf5d35e9efe 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -102,8 +102,8 @@ COLORREF TextColor::GetColor(std::basic_string_view colorTable, if (brighten && _index < 8) { FAIL_FAST_IF(colorTable.size() < 16); - FAIL_FAST_IF((size_t)(_index + 8) > (size_t)(colorTable.size())); - return colorTable.at(_index + 8); + FAIL_FAST_IF(gsl::narrow_cast(_index) + 8 > colorTable.size()); + return colorTable.at(gsl::narrow_cast(_index) + 8); } else { diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index c563fc1e562..3e186629494 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -977,9 +977,9 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe std::vector selectionBkAttr; // preallocate to avoid reallocs - selectionText.reserve(highlight.Width() + 2); // + 2 for \r\n if we munged it - selectionFgAttr.reserve(highlight.Width() + 2); - selectionBkAttr.reserve(highlight.Width() + 2); + selectionText.reserve(gsl::narrow(highlight.Width()) + 2); // + 2 for \r\n if we munged it + selectionFgAttr.reserve(gsl::narrow(highlight.Width()) + 2); + selectionBkAttr.reserve(gsl::narrow(highlight.Width()) + 2); // copy char data into the string buffer, skipping trailing bytes while (it) @@ -1109,7 +1109,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo bool hasWrittenAnyText = false; std::optional fgColor = std::nullopt; std::optional bkColor = std::nullopt; - for (UINT row = 0; row < rows.text.size(); row++) + for (size_t row = 0; row < rows.text.size(); row++) { size_t startOffset = 0; @@ -1118,7 +1118,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo htmlBuilder << "
"; } - for (UINT col = 0; col < rows.text.at(row).length(); col++) + for (size_t col = 0; col < rows.text.at(row).length(); col++) { // do not include \r nor \n as they don't have attributes // and are not HTML friendly. For line break use '
' instead. diff --git a/src/buffer/out/textBufferCellIterator.cpp b/src/buffer/out/textBufferCellIterator.cpp index 50948ab6bbd..b0d09eec2e3 100644 --- a/src/buffer/out/textBufferCellIterator.cpp +++ b/src/buffer/out/textBufferCellIterator.cpp @@ -212,7 +212,7 @@ void TextBufferCellIterator::_SetPos(const COORD newPos) if (newPos.X != _pos.X) { - const ptrdiff_t diff = newPos.X - _pos.X; + const ptrdiff_t diff = gsl::narrow_cast(newPos.X) - gsl::narrow_cast(_pos.X); _attrIter += diff; } diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 230d1ba56de..f45bc75ba44 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -39,7 +39,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _width{ width } { // Fetch the locale name out once now from the format - _localeName.resize(format->GetLocaleNameLength() + 1); // +1 for null + _localeName.resize(gsl::narrow_cast(format->GetLocaleNameLength()) + 1); // +1 for null THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow(_localeName.size()))); for (const auto& cluster : clusters) @@ -306,8 +306,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // Get the placement of the all the glyphs. - _glyphAdvances.resize(std::max(static_cast(glyphStart + actualGlyphCount), _glyphAdvances.size())); - _glyphOffsets.resize(std::max(static_cast(glyphStart + actualGlyphCount), _glyphOffsets.size())); + _glyphAdvances.resize(std::max(gsl::narrow_cast(glyphStart) + gsl::narrow_cast(actualGlyphCount), _glyphAdvances.size())); + _glyphOffsets.resize(std::max(gsl::narrow_cast(glyphStart) + gsl::narrow_cast(actualGlyphCount), _glyphOffsets.size())); const auto fontSizeFormat = _format->GetFontSize(); const auto fontSize = fontSizeFormat * run.fontScale; @@ -799,7 +799,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, RETURN_IF_FAILED(format1->GetFontCollection(&collection)); std::wstring familyName; - familyName.resize(format1->GetFontFamilyNameLength() + 1); + familyName.resize(gsl::narrow_cast(format1->GetFontFamilyNameLength()) + 1); RETURN_IF_FAILED(format1->GetFontFamilyName(familyName.data(), gsl::narrow(familyName.size()))); const auto weight = format1->GetFontWeight(); diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 3deb97ad49e..903751e8f67 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -569,7 +569,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal if (currentScreenInfoRow == endScreenInfoRow) { // prevent the end from going past the last non-whitespace char in the row - endIndex = std::min(static_cast(endColumn + 1), rowRight); + endIndex = std::min(gsl::narrow_cast(endColumn) + 1, rowRight); } // if startIndex >= endIndex then _start is @@ -1457,11 +1457,11 @@ std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData // check if we're at the edge of the screen info buffer if (currentScreenInfoRow == moveState.LimitingRow && - currentColumn + 1 >= right) + gsl::narrow_cast(currentColumn) + 1 >= right) { break; } - else if (currentColumn + 1 >= right) + else if (gsl::narrow_cast(currentColumn) + 1 >= right) { // we're at the edge of a row and need to go to the next one currentColumn = moveState.FirstColumnInRow; @@ -1669,11 +1669,11 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, // check if we're at the edge of the screen info buffer if (currentScreenInfoRow == moveState.LimitingRow && - currentColumn + 1 >= right) + gsl::narrow_cast(currentColumn) + 1 >= right) { break; } - else if (currentColumn + 1 >= right) + else if (gsl::narrow_cast(currentColumn) + 1 >= right) { // we're at the edge of a row and need to go to the next one currentColumn = moveState.FirstColumnInRow; diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index 7ff719081bd..b98abc29184 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -182,8 +182,13 @@ IFACEMETHODIMP WindowUiaProviderBase::get_BoundingRectangle(_Out_ UiaRect* pRect pRect->left = rc.left; pRect->top = rc.top; - pRect->width = rc.right - rc.left; - pRect->height = rc.bottom - rc.top; + + LONG longWidth = 0; + RETURN_IF_FAILED(LongSub(rc.right, rc.left, &longWidth)); + pRect->width = longWidth; + LONG longHeight = 0; + RETURN_IF_FAILED(LongSub(rc.bottom, rc.top, &longHeight)); + pRect->height = longHeight; return S_OK; } From 8c3a629b52a5d81139776e1e878a7d8a1bc804f6 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 14:08:47 -0700 Subject: [PATCH 087/154] C26481, don't use pointer arithemetic. use span. --- src/buffer/out/OutputCellRect.cpp | 4 ++-- src/buffer/out/textBuffer.cpp | 2 +- src/renderer/dx/CustomTextLayout.cpp | 8 ++++---- src/renderer/dx/CustomTextRenderer.cpp | 7 ++++--- src/types/ScreenInfoUiaProviderBase.cpp | 4 +++- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/buffer/out/OutputCellRect.cpp b/src/buffer/out/OutputCellRect.cpp index 37711c5343b..aec19e9fb3d 100644 --- a/src/buffer/out/OutputCellRect.cpp +++ b/src/buffer/out/OutputCellRect.cpp @@ -64,7 +64,7 @@ OutputCellIterator OutputCellRect::GetRowIter(const size_t row) const // - Pointer to the location in the rectangle that represents the start of the requested row. OutputCell* OutputCellRect::_FindRowOffset(const size_t row) { - return (_storage.data() + (row * _cols)); + return &_storage.at(row * _cols); } // Routine Description: @@ -76,7 +76,7 @@ OutputCell* OutputCellRect::_FindRowOffset(const size_t row) // - Pointer to the location in the rectangle that represents the start of the requested row. const OutputCell* OutputCellRect::_FindRowOffset(const size_t row) const { - return (_storage.data() + (row * _cols)); + return &_storage.at(row * _cols); } // Routine Description: diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 3e186629494..5e29ecddcc8 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1145,7 +1145,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo { // note: this should be escaped (for '<', '>', and '&'), // however MS Word doesn't appear to support HTML entities - htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row).data() + startOffset, col - startOffset + includeCurrent)); + htmlBuilder << ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent)); startOffset = col; } }; diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index f45bc75ba44..38e9f1d338f 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -485,10 +485,10 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, glyphRun.bidiLevel = run.bidiLevel; glyphRun.fontEmSize = _format->GetFontSize() * run.fontScale; glyphRun.fontFace = run.fontFace.Get(); - glyphRun.glyphAdvances = _glyphAdvances.data() + run.glyphStart; + glyphRun.glyphAdvances = &_glyphAdvances.at(run.glyphStart); glyphRun.glyphCount = run.glyphCount; - glyphRun.glyphIndices = _glyphIndices.data() + run.glyphStart; - glyphRun.glyphOffsets = _glyphOffsets.data() + run.glyphStart; + glyphRun.glyphIndices = &_glyphIndices.at(run.glyphStart); + glyphRun.glyphOffsets = &_glyphOffsets.at(run.glyphStart); glyphRun.isSideways = false; DWRITE_GLYPH_RUN_DESCRIPTION glyphRunDescription; @@ -566,7 +566,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, if (textPosition < _text.size()) { - *textString = _text.data() + textPosition; + *textString = &_text.at(textPosition); *textLength = gsl::narrow(_text.size()) - textPosition; } diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index 1c7edfff295..79dec1dc672 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -248,12 +248,13 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, rect.bottom = rect.top + drawingContext->cellSize.height; rect.left = origin.x; rect.right = rect.left; + const auto advancesSpan = gsl::make_span(glyphRun->glyphAdvances, glyphRun->glyphCount); - for (UINT32 i = 0; i < glyphRun->glyphCount; i++) + for (const auto& advance : advancesSpan) { - rect.right += glyphRun->glyphAdvances[i]; + rect.right += advance; } - + d2dContext->FillRectangle(rect, drawingContext->backgroundBrush); // Now go onto drawing the text. diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index c68dd7d1750..c06919d23f7 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -15,9 +15,11 @@ SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, length); if (psa != nullptr) { + const auto dataSpan = gsl::make_span(data, length); + for (long i = 0; i < length; i++) { - if (FAILED(SafeArrayPutElement(psa, &i, (void*)&(data[i])))) + if (FAILED(SafeArrayPutElement(psa, &i, (void*)&(dataSpan.at(i))))) { SafeArrayDestroy(psa); psa = nullptr; From 4f1157c0446631e38c50224961eeaf2714264b4b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 29 Aug 2019 15:23:07 -0700 Subject: [PATCH 088/154] C26447,C26440 - is noexcept but can throw or doesn't throw but not noexcept --- src/buffer/out/AttrRowIterator.cpp | 8 +- src/buffer/out/AttrRowIterator.hpp | 8 +- src/buffer/out/CharRow.cpp | 8 +- src/buffer/out/CharRow.hpp | 8 +- src/buffer/out/CharRowCell.cpp | 6 +- src/buffer/out/CharRowCell.hpp | 6 +- src/buffer/out/CharRowCellReference.hpp | 2 +- src/buffer/out/DbcsAttribute.hpp | 2 +- src/buffer/out/OutputCellIterator.cpp | 28 +- src/buffer/out/OutputCellIterator.hpp | 28 +- src/buffer/out/OutputCellView.cpp | 2 +- src/buffer/out/OutputCellView.hpp | 2 +- src/buffer/out/Row.cpp | 8 +- src/buffer/out/Row.hpp | 8 +- src/buffer/out/RowCellIterator.cpp | 12 +- src/buffer/out/RowCellIterator.hpp | 12 +- src/buffer/out/TextAttribute.cpp | 16 +- src/buffer/out/TextAttribute.hpp | 16 +- src/buffer/out/TextColor.cpp | 10 +- src/buffer/out/TextColor.h | 10 +- src/buffer/out/UnicodeStorage.cpp | 2 +- src/buffer/out/UnicodeStorage.hpp | 2 +- src/buffer/out/cursor.cpp | 58 +-- src/buffer/out/cursor.h | 62 +-- src/buffer/out/textBuffer.cpp | 20 +- src/buffer/out/textBuffer.hpp | 20 +- src/buffer/out/textBufferCellIterator.cpp | 4 +- src/buffer/out/textBufferCellIterator.hpp | 4 +- src/buffer/out/textBufferTextIterator.cpp | 6 +- src/buffer/out/textBufferTextIterator.hpp | 6 +- src/renderer/dx/CustomTextLayout.cpp | 10 +- src/renderer/dx/CustomTextLayout.h | 14 +- src/renderer/dx/CustomTextRenderer.cpp | 18 +- src/renderer/dx/CustomTextRenderer.h | 20 +- src/renderer/dx/DxRenderer.cpp | 437 ++++++++++++---------- src/renderer/dx/DxRenderer.hpp | 6 +- src/types/CodepointWidthDetector.cpp | 4 +- src/types/GlyphWidth.cpp | 4 +- src/types/KeyEvent.cpp | 2 +- src/types/ScreenInfoUiaProviderBase.cpp | 22 +- src/types/ScreenInfoUiaProviderBase.h | 16 +- src/types/UiaTextRangeBase.cpp | 74 ++-- src/types/UiaTextRangeBase.hpp | 54 +-- src/types/Utf16Parser.cpp | 2 +- src/types/inc/CodepointWidthDetector.hpp | 2 +- src/types/inc/GlyphWidth.hpp | 4 +- src/types/inc/IInputEvent.hpp | 2 +- src/types/inc/Utf16Parser.hpp | 2 +- src/types/inc/utils.hpp | 4 +- src/types/inc/viewport.hpp | 2 +- src/types/utils.cpp | 4 +- src/types/viewport.cpp | 2 +- 52 files changed, 565 insertions(+), 524 deletions(-) diff --git a/src/buffer/out/AttrRowIterator.cpp b/src/buffer/out/AttrRowIterator.cpp index 0462afa144c..c4e735d3e11 100644 --- a/src/buffer/out/AttrRowIterator.cpp +++ b/src/buffer/out/AttrRowIterator.cpp @@ -6,21 +6,21 @@ #include "AttrRowIterator.hpp" #include "AttrRow.hpp" -AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow) +AttrRowIterator AttrRowIterator::CreateEndIterator(const ATTR_ROW* const attrRow) noexcept { AttrRowIterator it{ attrRow }; it._setToEnd(); return it; } -AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) : +AttrRowIterator::AttrRowIterator(const ATTR_ROW* const attrRow) noexcept : _pAttrRow{ attrRow }, _run{ attrRow->_list.cbegin() }, _currentAttributeIndex{ 0 } { } -AttrRowIterator::operator bool() const noexcept +AttrRowIterator::operator bool() const { return _run < _pAttrRow->_list.cend(); } @@ -139,7 +139,7 @@ void AttrRowIterator::_decrement(size_t count) // Routine Description: // - sets fields on the iterator to describe the end() state of the ATTR_ROW -void AttrRowIterator::_setToEnd() +void AttrRowIterator::_setToEnd() noexcept { _run = _pAttrRow->_list.cend(); _currentAttributeIndex = 0; diff --git a/src/buffer/out/AttrRowIterator.hpp b/src/buffer/out/AttrRowIterator.hpp index 5ca7f72c125..c9e053ea247 100644 --- a/src/buffer/out/AttrRowIterator.hpp +++ b/src/buffer/out/AttrRowIterator.hpp @@ -29,11 +29,11 @@ class AttrRowIterator final using pointer = TextAttribute*; using reference = TextAttribute&; - static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow); + static AttrRowIterator CreateEndIterator(const ATTR_ROW* const attrRow) noexcept; - AttrRowIterator(const ATTR_ROW* const attrRow); + AttrRowIterator(const ATTR_ROW* const attrRow) noexcept; - operator bool() const noexcept; + operator bool() const; bool operator==(const AttrRowIterator& it) const; bool operator!=(const AttrRowIterator& it) const; @@ -57,5 +57,5 @@ class AttrRowIterator final void _increment(size_t count); void _decrement(size_t count); - void _setToEnd(); + void _setToEnd() noexcept; }; diff --git a/src/buffer/out/CharRow.cpp b/src/buffer/out/CharRow.cpp index d255b01674f..8173e72ad25 100644 --- a/src/buffer/out/CharRow.cpp +++ b/src/buffer/out/CharRow.cpp @@ -84,7 +84,7 @@ size_t CharRow::size() const noexcept // - sRowWidth - The width of the row. // Return Value: // - -void CharRow::Reset() +void CharRow::Reset() noexcept { for (auto& cell : _data) { @@ -292,12 +292,12 @@ std::wstring CharRow::GetText() const return wstr; } -UnicodeStorage& CharRow::GetUnicodeStorage() +UnicodeStorage& CharRow::GetUnicodeStorage() noexcept { return _pParent->GetUnicodeStorage(); } -const UnicodeStorage& CharRow::GetUnicodeStorage() const +const UnicodeStorage& CharRow::GetUnicodeStorage() const noexcept { return _pParent->GetUnicodeStorage(); } @@ -308,7 +308,7 @@ const UnicodeStorage& CharRow::GetUnicodeStorage() const // - column - the column to generate the key for // Return Value: // - the COORD key for data access from UnicodeStorage for the column -COORD CharRow::GetStorageKey(const size_t column) const +COORD CharRow::GetStorageKey(const size_t column) const noexcept { return { gsl::narrow(column), _pParent->GetId() }; } diff --git a/src/buffer/out/CharRow.hpp b/src/buffer/out/CharRow.hpp index c9ff3a0ae17..b7f33a9eb52 100644 --- a/src/buffer/out/CharRow.hpp +++ b/src/buffer/out/CharRow.hpp @@ -53,7 +53,7 @@ class CharRow final void SetDoubleBytePadded(const bool doubleBytePadded) noexcept; bool WasDoubleBytePadded() const noexcept; size_t size() const noexcept; - void Reset(); + void Reset() noexcept; [[nodiscard]] HRESULT Resize(const size_t newSize) noexcept; size_t MeasureLeft() const; size_t MeasureRight() const noexcept; @@ -78,9 +78,9 @@ class CharRow final iterator end() noexcept; const_iterator cend() const noexcept; - UnicodeStorage& GetUnicodeStorage(); - const UnicodeStorage& GetUnicodeStorage() const; - COORD GetStorageKey(const size_t column) const; + UnicodeStorage& GetUnicodeStorage() noexcept; + const UnicodeStorage& GetUnicodeStorage() const noexcept; + COORD GetStorageKey(const size_t column) const noexcept; void UpdateParent(ROW* const pParent) noexcept; diff --git a/src/buffer/out/CharRowCell.cpp b/src/buffer/out/CharRowCell.cpp index a6c2d2fb66e..38da6a1720e 100644 --- a/src/buffer/out/CharRowCell.cpp +++ b/src/buffer/out/CharRowCell.cpp @@ -8,13 +8,13 @@ // default glyph value, used for reseting the character data portion of a cell static constexpr wchar_t DefaultValue = UNICODE_SPACE; -CharRowCell::CharRowCell() : +CharRowCell::CharRowCell() noexcept: _wch{ DefaultValue }, _attr{} { } -CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) : +CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept: _wch{ wch }, _attr{ attr } { @@ -22,7 +22,7 @@ CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) : // Routine Description: // - "erases" the glyph. really sets it back to the default "empty" value -void CharRowCell::EraseChars() +void CharRowCell::EraseChars() noexcept { if (_attr.IsGlyphStored()) { diff --git a/src/buffer/out/CharRowCell.hpp b/src/buffer/out/CharRowCell.hpp index fd8785bafd7..9ce8ee16b45 100644 --- a/src/buffer/out/CharRowCell.hpp +++ b/src/buffer/out/CharRowCell.hpp @@ -27,10 +27,10 @@ Author(s): class CharRowCell final { public: - CharRowCell(); - CharRowCell(const wchar_t wch, const DbcsAttribute attr); + CharRowCell() noexcept; + CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept; - void EraseChars(); + void EraseChars() noexcept; void Reset() noexcept; bool IsSpace() const noexcept; diff --git a/src/buffer/out/CharRowCellReference.hpp b/src/buffer/out/CharRowCellReference.hpp index 64a42a17710..24e2ae45fe9 100644 --- a/src/buffer/out/CharRowCellReference.hpp +++ b/src/buffer/out/CharRowCellReference.hpp @@ -25,7 +25,7 @@ class CharRowCellReference final public: using const_iterator = const wchar_t*; - CharRowCellReference(CharRow& parent, const size_t index) : + CharRowCellReference(CharRow& parent, const size_t index) noexcept : _parent{ parent }, _index{ index } { diff --git a/src/buffer/out/DbcsAttribute.hpp b/src/buffer/out/DbcsAttribute.hpp index f92a707b167..442505052c3 100644 --- a/src/buffer/out/DbcsAttribute.hpp +++ b/src/buffer/out/DbcsAttribute.hpp @@ -63,7 +63,7 @@ class DbcsAttribute final return _glyphStored; } - void SetGlyphStored(const bool stored) + void SetGlyphStored(const bool stored) noexcept { _glyphStored = stored; } diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index fd8249061c9..ff33b2c091a 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -17,7 +17,7 @@ static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLO // Arguments: // - wch - The character to use for filling // - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0. -OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) : +OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimit) noexcept : _mode(Mode::Fill), _currentView(s_GenerateView(wch)), _run(), @@ -33,7 +33,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const size_t fillLimi // Arguments: // - attr - The color attribute to use for filling // - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0. -OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) : +OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t fillLimit) noexcept : _mode(Mode::Fill), _currentView(s_GenerateView(attr)), _run(), @@ -50,7 +50,7 @@ OutputCellIterator::OutputCellIterator(const TextAttribute& attr, const size_t f // - wch - The character to use for filling // - attr - The color attribute to use for filling // - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0. -OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) : +OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit) noexcept : _mode(Mode::Fill), _currentView(s_GenerateView(wch, attr)), _run(), @@ -66,7 +66,7 @@ OutputCellIterator::OutputCellIterator(const wchar_t& wch, const TextAttribute& // Arguments: // - charInfo - The legacy character and color data to use for fililng (uses Unicode portion of text data) // - fillLimit - How many times to allow this value to be viewed/filled. Infinite if 0. -OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) : +OutputCellIterator::OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit) noexcept : _mode(Mode::Fill), _currentView(s_GenerateView(charInfo)), _run(), @@ -116,7 +116,7 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const // razzle cannot distinguish between a std::wstring_view and a std::basic_string_view // NOTE: This one internally casts to wchar_t because Razzle sees WORD and wchar_t as the same type // despite that Visual Studio build can tell the difference. -OutputCellIterator::OutputCellIterator(const std::basic_string_view legacyAttrs, const bool /*unused*/) : +OutputCellIterator::OutputCellIterator(const std::basic_string_view legacyAttrs, const bool /*unused*/) noexcept : _mode(Mode::LegacyAttr), _currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))), _run(std::wstring_view(reinterpret_cast(legacyAttrs.data()), legacyAttrs.size())), @@ -131,7 +131,7 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view legacy // - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute. // Arguments: // - charInfos - Multiple cell with unicode text and legacy color data. -OutputCellIterator::OutputCellIterator(const std::basic_string_view charInfos) : +OutputCellIterator::OutputCellIterator(const std::basic_string_view charInfos) noexcept : _mode(Mode::CharInfo), _currentView(s_GenerateView(charInfos.at(0))), _run(charInfos), @@ -315,7 +315,7 @@ OutputCellIterator OutputCellIterator::operator++(int) // - Reference the view to fully-formed output cell data representing the underlying data source. // Return Value: // - Reference to the view -const OutputCellView& OutputCellIterator::operator*() const +const OutputCellView& OutputCellIterator::operator*() const noexcept { return _currentView; } @@ -324,7 +324,7 @@ const OutputCellView& OutputCellIterator::operator*() const // - Get pointer to the view to fully-formed output cell data representing the underlying data source. // Return Value: // - Pointer to the view -const OutputCellView* OutputCellIterator::operator->() const +const OutputCellView* OutputCellIterator::operator->() const noexcept { return &_currentView; } @@ -338,7 +338,7 @@ const OutputCellView* OutputCellIterator::operator->() const // - True if we just turned a lead half into a trailing half (and caller doesn't // need to further update the view). // - False if this wasn't applicable and the caller should update the view. -bool OutputCellIterator::_TryMoveTrailing() +bool OutputCellIterator::_TryMoveTrailing() noexcept { if (_currentView.DbcsAttr().IsLeading()) { @@ -421,7 +421,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const std::wstring_view view, // - wch - View representing a single UTF-16 character (that can be represented without surrogates) // Return Value: // - Object representing the view into this cell -OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) +OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) noexcept { const auto glyph = std::wstring_view(&wch, 1); @@ -443,7 +443,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch) // - attr - View representing a single color // Return Value: // - Object representing the view into this cell -OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) +OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) noexcept { return OutputCellView({}, {}, attr, TextAttributeBehavior::StoredOnly); } @@ -458,7 +458,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const TextAttribute& attr) // - attr - View representing a single color // Return Value: // - Object representing the view into this cell -OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) +OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept { const auto glyph = std::wstring_view(&wch, 1); @@ -480,7 +480,7 @@ OutputCellView OutputCellIterator::s_GenerateView(const wchar_t& wch, const Text // - legacyAttr - View representing a single legacy color // Return Value: // - Object representing the view into this cell -OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr) +OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept { WORD cleanAttr = legacyAttr; WI_ClearAllFlags(cleanAttr, COMMON_LVB_SBCSDBCS); // don't use legacy lead/trailing byte flags for colors @@ -498,7 +498,7 @@ OutputCellView OutputCellIterator::s_GenerateViewLegacyAttr(const WORD& legacyAt // - charInfo - character and attribute pair representing a single cell // Return Value: // - Object representing the view into this cell -OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) +OutputCellView OutputCellIterator::s_GenerateView(const CHAR_INFO& charInfo) noexcept { const auto glyph = std::wstring_view(&charInfo.Char.UnicodeChar, 1); diff --git a/src/buffer/out/OutputCellIterator.hpp b/src/buffer/out/OutputCellIterator.hpp index b5b29050760..02f6aefa54e 100644 --- a/src/buffer/out/OutputCellIterator.hpp +++ b/src/buffer/out/OutputCellIterator.hpp @@ -33,14 +33,14 @@ class OutputCellIterator final using pointer = OutputCellView*; using reference = OutputCellView&; - OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0); - OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0); - OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0); - OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0); + OutputCellIterator(const wchar_t& wch, const size_t fillLimit = 0) noexcept; + OutputCellIterator(const TextAttribute& attr, const size_t fillLimit = 0) noexcept; + OutputCellIterator(const wchar_t& wch, const TextAttribute& attr, const size_t fillLimit = 0) noexcept; + OutputCellIterator(const CHAR_INFO& charInfo, const size_t fillLimit = 0) noexcept; OutputCellIterator(const std::wstring_view utf16Text); OutputCellIterator(const std::wstring_view utf16Text, const TextAttribute attribute); - OutputCellIterator(const std::basic_string_view legacyAttributes, const bool unused); - OutputCellIterator(const std::basic_string_view charInfos); + OutputCellIterator(const std::basic_string_view legacyAttributes, const bool unused) noexcept; + OutputCellIterator(const std::basic_string_view charInfos) noexcept; OutputCellIterator(const std::basic_string_view cells); ~OutputCellIterator() = default; @@ -55,8 +55,8 @@ class OutputCellIterator final OutputCellIterator& operator++(); OutputCellIterator operator++(int); - const OutputCellView& operator*() const; - const OutputCellView* operator->() const; + const OutputCellView& operator*() const noexcept; + const OutputCellView* operator->() const noexcept; private: enum class Mode @@ -97,7 +97,7 @@ class OutputCellIterator final TextAttribute _attr; - bool _TryMoveTrailing(); + bool _TryMoveTrailing() noexcept; static OutputCellView s_GenerateView(const std::wstring_view view); @@ -108,11 +108,11 @@ class OutputCellIterator final const TextAttribute attr, const TextAttributeBehavior behavior); - static OutputCellView s_GenerateView(const wchar_t& wch); - static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr); - static OutputCellView s_GenerateView(const TextAttribute& attr); - static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr); - static OutputCellView s_GenerateView(const CHAR_INFO& charInfo); + static OutputCellView s_GenerateView(const wchar_t& wch) noexcept; + static OutputCellView s_GenerateViewLegacyAttr(const WORD& legacyAttr) noexcept; + static OutputCellView s_GenerateView(const TextAttribute& attr) noexcept; + static OutputCellView s_GenerateView(const wchar_t& wch, const TextAttribute& attr) noexcept; + static OutputCellView s_GenerateView(const CHAR_INFO& charInfo) noexcept; static OutputCellView s_GenerateView(const OutputCell& cell); diff --git a/src/buffer/out/OutputCellView.cpp b/src/buffer/out/OutputCellView.cpp index 434333c030f..37e46457d93 100644 --- a/src/buffer/out/OutputCellView.cpp +++ b/src/buffer/out/OutputCellView.cpp @@ -15,7 +15,7 @@ OutputCellView::OutputCellView(const std::wstring_view view, const DbcsAttribute dbcsAttr, const TextAttribute textAttr, - const TextAttributeBehavior behavior) : + const TextAttributeBehavior behavior) noexcept: _view(view), _dbcsAttr(dbcsAttr), _textAttr(textAttr), diff --git a/src/buffer/out/OutputCellView.hpp b/src/buffer/out/OutputCellView.hpp index 2c425d8088a..06c9b86af21 100644 --- a/src/buffer/out/OutputCellView.hpp +++ b/src/buffer/out/OutputCellView.hpp @@ -28,7 +28,7 @@ class OutputCellView OutputCellView(const std::wstring_view view, const DbcsAttribute dbcsAttr, const TextAttribute textAttr, - const TextAttributeBehavior behavior); + const TextAttributeBehavior behavior) noexcept; const std::wstring_view& Chars() const noexcept; size_t Columns() const noexcept; diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index 44ffd941f04..a46df44ceb5 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -30,12 +30,12 @@ size_t ROW::size() const noexcept return _rowWidth; } -const CharRow& ROW::GetCharRow() const +const CharRow& ROW::GetCharRow() const noexcept { return _charRow; } -CharRow& ROW::GetCharRow() +CharRow& ROW::GetCharRow() noexcept { return const_cast(static_cast(this)->GetCharRow()); } @@ -132,12 +132,12 @@ RowCellIterator ROW::AsCellIter(const size_t startIndex, const size_t count) con return RowCellIterator(*this, startIndex, count); } -UnicodeStorage& ROW::GetUnicodeStorage() +UnicodeStorage& ROW::GetUnicodeStorage() noexcept { return _pParent->GetUnicodeStorage(); } -const UnicodeStorage& ROW::GetUnicodeStorage() const +const UnicodeStorage& ROW::GetUnicodeStorage() const noexcept { return _pParent->GetUnicodeStorage(); } diff --git a/src/buffer/out/Row.hpp b/src/buffer/out/Row.hpp index 5bf05c858f6..27d2a390928 100644 --- a/src/buffer/out/Row.hpp +++ b/src/buffer/out/Row.hpp @@ -36,8 +36,8 @@ class ROW final size_t size() const noexcept; - const CharRow& GetCharRow() const; - CharRow& GetCharRow(); + const CharRow& GetCharRow() const noexcept; + CharRow& GetCharRow() noexcept; const ATTR_ROW& GetAttrRow() const noexcept; ATTR_ROW& GetAttrRow() noexcept; @@ -54,8 +54,8 @@ class ROW final RowCellIterator AsCellIter(const size_t startIndex) const; RowCellIterator AsCellIter(const size_t startIndex, const size_t count) const; - UnicodeStorage& GetUnicodeStorage(); - const UnicodeStorage& GetUnicodeStorage() const; + UnicodeStorage& GetUnicodeStorage() noexcept; + const UnicodeStorage& GetUnicodeStorage() const noexcept; OutputCellIterator WriteCells(OutputCellIterator it, const size_t index, const bool setWrap, std::optional limitRight = std::nullopt); diff --git a/src/buffer/out/RowCellIterator.cpp b/src/buffer/out/RowCellIterator.cpp index 0381e4d933e..0dedc264a25 100644 --- a/src/buffer/out/RowCellIterator.cpp +++ b/src/buffer/out/RowCellIterator.cpp @@ -37,38 +37,38 @@ bool RowCellIterator::operator!=(const RowCellIterator& it) const noexcept return !(*this == it); } -RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement) +RowCellIterator& RowCellIterator::operator+=(const ptrdiff_t& movement) noexcept { _pos += movement; return (*this); } -RowCellIterator& RowCellIterator::operator++() +RowCellIterator& RowCellIterator::operator++() noexcept { return this->operator+=(1); } -RowCellIterator RowCellIterator::operator++(int) +RowCellIterator RowCellIterator::operator++(int) noexcept { auto temp(*this); operator++(); return temp; } -RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement) +RowCellIterator RowCellIterator::operator+(const ptrdiff_t& movement) noexcept { auto temp(*this); temp += movement; return temp; } -const OutputCellView& RowCellIterator::operator*() const +const OutputCellView& RowCellIterator::operator*() const noexcept { return _view; } -const OutputCellView* RowCellIterator::operator->() const +const OutputCellView* RowCellIterator::operator->() const noexcept { return &_view; } diff --git a/src/buffer/out/RowCellIterator.hpp b/src/buffer/out/RowCellIterator.hpp index c386bb6afa1..62d6ffc1c14 100644 --- a/src/buffer/out/RowCellIterator.hpp +++ b/src/buffer/out/RowCellIterator.hpp @@ -38,13 +38,13 @@ class RowCellIterator final bool operator==(const RowCellIterator& it) const noexcept; bool operator!=(const RowCellIterator& it) const noexcept; - RowCellIterator& operator+=(const ptrdiff_t& movement); - RowCellIterator& operator++(); - RowCellIterator operator++(int); - RowCellIterator operator+(const ptrdiff_t& movement); + RowCellIterator& operator+=(const ptrdiff_t& movement) noexcept; + RowCellIterator& operator++() noexcept; + RowCellIterator operator++(int) noexcept; + RowCellIterator operator+(const ptrdiff_t& movement) noexcept; - const OutputCellView& operator*() const; - const OutputCellView* operator->() const; + const OutputCellView& operator*() const noexcept; + const OutputCellView* operator->() const noexcept; private: const ROW& _row; diff --git a/src/buffer/out/TextAttribute.cpp b/src/buffer/out/TextAttribute.cpp index b224c6c8ad5..0c964b848e9 100644 --- a/src/buffer/out/TextAttribute.cpp +++ b/src/buffer/out/TextAttribute.cpp @@ -16,7 +16,7 @@ bool TextAttribute::IsLegacy() const noexcept // - color that should be displayed as the foreground color COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view colorTable, COLORREF defaultFgColor, - COLORREF defaultBgColor) const + COLORREF defaultBgColor) const noexcept { return _IsReverseVideo() ? _GetRgbBackground(colorTable, defaultBgColor) : _GetRgbForeground(colorTable, defaultFgColor); } @@ -29,7 +29,7 @@ COLORREF TextAttribute::CalculateRgbForeground(std::basic_string_view // - color that should be displayed as the background color COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view colorTable, COLORREF defaultFgColor, - COLORREF defaultBgColor) const + COLORREF defaultBgColor) const noexcept { return _IsReverseVideo() ? _GetRgbForeground(colorTable, defaultFgColor) : _GetRgbBackground(colorTable, defaultBgColor); } @@ -42,7 +42,7 @@ COLORREF TextAttribute::CalculateRgbBackground(std::basic_string_view // Return Value: // - color that is stored as the foreground color COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view colorTable, - COLORREF defaultColor) const + COLORREF defaultColor) const noexcept { return _foreground.GetColor(colorTable, defaultColor, _isBold); } @@ -55,7 +55,7 @@ COLORREF TextAttribute::_GetRgbForeground(std::basic_string_view color // Return Value: // - color that is stored as the background color COLORREF TextAttribute::_GetRgbBackground(std::basic_string_view colorTable, - COLORREF defaultColor) const + COLORREF defaultColor) const noexcept { return _background.GetColor(colorTable, defaultColor, false); } @@ -75,12 +75,12 @@ WORD TextAttribute::GetMetaAttributes() const noexcept return wMeta; } -void TextAttribute::SetForeground(const COLORREF rgbForeground) +void TextAttribute::SetForeground(const COLORREF rgbForeground) noexcept { _foreground = TextColor(rgbForeground); } -void TextAttribute::SetBackground(const COLORREF rgbBackground) +void TextAttribute::SetBackground(const COLORREF rgbBackground) noexcept { _background = TextColor(rgbBackground); } @@ -98,7 +98,7 @@ void TextAttribute::SetFromLegacy(const WORD wLegacy) noexcept void TextAttribute::SetLegacyAttributes(const WORD attrs, const bool setForeground, const bool setBackground, - const bool setMeta) + const bool setMeta) noexcept { if (setForeground) { @@ -143,7 +143,7 @@ void TextAttribute::SetIndexedAttributes(const std::optional foregro } } -void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground) +void TextAttribute::SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept { if (fIsForeground) { diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index eb402c61f35..eb8cc6111df 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -90,10 +90,10 @@ class TextAttribute final COLORREF CalculateRgbForeground(std::basic_string_view colorTable, COLORREF defaultFgColor, - COLORREF defaultBgColor) const; + COLORREF defaultBgColor) const noexcept; COLORREF CalculateRgbBackground(std::basic_string_view colorTable, COLORREF defaultFgColor, - COLORREF defaultBgColor) const; + COLORREF defaultBgColor) const noexcept; bool IsLeadingByte() const noexcept; bool IsTrailingByte() const noexcept; @@ -110,7 +110,7 @@ class TextAttribute final void SetLegacyAttributes(const WORD attrs, const bool setForeground, const bool setBackground, - const bool setMeta); + const bool setMeta) noexcept; void SetIndexedAttributes(const std::optional foreground, const std::optional background) noexcept; @@ -133,9 +133,9 @@ class TextAttribute final bool IsLegacy() const noexcept; bool IsBold() const noexcept; - void SetForeground(const COLORREF rgbForeground); - void SetBackground(const COLORREF rgbBackground); - void SetColor(const COLORREF rgbColor, const bool fIsForeground); + void SetForeground(const COLORREF rgbForeground) noexcept; + void SetBackground(const COLORREF rgbBackground) noexcept; + void SetColor(const COLORREF rgbColor, const bool fIsForeground) noexcept; void SetDefaultForeground() noexcept; void SetDefaultBackground() noexcept; @@ -150,9 +150,9 @@ class TextAttribute final private: COLORREF _GetRgbForeground(std::basic_string_view colorTable, - COLORREF defaultColor) const; + COLORREF defaultColor) const noexcept; COLORREF _GetRgbBackground(std::basic_string_view colorTable, - COLORREF defaultColor) const; + COLORREF defaultColor) const noexcept; bool _IsReverseVideo() const noexcept; void _SetBoldness(const bool isBold) noexcept; diff --git a/src/buffer/out/TextColor.cpp b/src/buffer/out/TextColor.cpp index cf5d35e9efe..606afdac148 100644 --- a/src/buffer/out/TextColor.cpp +++ b/src/buffer/out/TextColor.cpp @@ -11,7 +11,7 @@ // - rgbColor: the COLORREF containing the color information for this TextColor // Return Value: // - -void TextColor::SetColor(const COLORREF rgbColor) +void TextColor::SetColor(const COLORREF rgbColor) noexcept { _meta = ColorType::IsRgb; _red = GetRValue(rgbColor); @@ -25,7 +25,7 @@ void TextColor::SetColor(const COLORREF rgbColor) // - index: the index of the colortable we should use for this TextColor. // Return Value: // - -void TextColor::SetIndex(const BYTE index) +void TextColor::SetIndex(const BYTE index) noexcept { _meta = ColorType::IsIndex; _index = index; @@ -38,7 +38,7 @@ void TextColor::SetIndex(const BYTE index) // - // Return Value: // - -void TextColor::SetDefault() +void TextColor::SetDefault() noexcept { _meta = ColorType::IsDefault; } @@ -63,7 +63,7 @@ void TextColor::SetDefault() // - a COLORREF containing the real value of this TextColor. COLORREF TextColor::GetColor(std::basic_string_view colorTable, const COLORREF defaultColor, - bool brighten) const + bool brighten) const noexcept { if (IsDefault()) { @@ -119,7 +119,7 @@ COLORREF TextColor::GetColor(std::basic_string_view colorTable, // - // Return Value: // - a COLORREF containing our stored value -COLORREF TextColor::_GetRGB() const +COLORREF TextColor::_GetRGB() const noexcept { return RGB(_red, _green, _blue); } diff --git a/src/buffer/out/TextColor.h b/src/buffer/out/TextColor.h index 6ba9284148c..f551f4e863a 100644 --- a/src/buffer/out/TextColor.h +++ b/src/buffer/out/TextColor.h @@ -91,13 +91,13 @@ struct TextColor return _meta == ColorType::IsRgb; } - void SetColor(const COLORREF rgbColor); - void SetIndex(const BYTE index); - void SetDefault(); + void SetColor(const COLORREF rgbColor) noexcept; + void SetIndex(const BYTE index) noexcept; + void SetDefault() noexcept; COLORREF GetColor(std::basic_string_view colorTable, const COLORREF defaultColor, - const bool brighten) const; + const bool brighten) const noexcept; constexpr BYTE GetIndex() const noexcept { @@ -113,7 +113,7 @@ struct TextColor BYTE _green; BYTE _blue; - COLORREF _GetRGB() const; + COLORREF _GetRGB() const noexcept; #ifdef UNIT_TESTING friend class TextBufferTests; diff --git a/src/buffer/out/UnicodeStorage.cpp b/src/buffer/out/UnicodeStorage.cpp index db5dd318255..ae71f0f5380 100644 --- a/src/buffer/out/UnicodeStorage.cpp +++ b/src/buffer/out/UnicodeStorage.cpp @@ -35,7 +35,7 @@ void UnicodeStorage::StoreGlyph(const key_type key, const mapped_type& glyph) // - erases key and its associated data from the storage // Arguments: // - key - the key to remove -void UnicodeStorage::Erase(const key_type key) noexcept +void UnicodeStorage::Erase(const key_type key) { _map.erase(key); } diff --git a/src/buffer/out/UnicodeStorage.hpp b/src/buffer/out/UnicodeStorage.hpp index 1b386da4792..88669f79259 100644 --- a/src/buffer/out/UnicodeStorage.hpp +++ b/src/buffer/out/UnicodeStorage.hpp @@ -53,7 +53,7 @@ class UnicodeStorage final void StoreGlyph(const key_type key, const mapped_type& glyph); - void Erase(const key_type key) noexcept; + void Erase(const key_type key); void Remap(const std::map& rowMap, const std::optional width); diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 97ed5f1ea8a..73624784752 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -11,7 +11,7 @@ // - Constructor to set default properties for Cursor // Arguments: // - ulSize - The height of the cursor within this buffer -Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) : +Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept: _parentBuffer{ parentBuffer }, _cPosition{ 0 }, _fHasMoved(false), @@ -87,36 +87,36 @@ ULONG Cursor::GetSize() const noexcept return _ulSize; } -void Cursor::SetHasMoved(const bool fHasMoved) +void Cursor::SetHasMoved(const bool fHasMoved) noexcept { _fHasMoved = fHasMoved; } -void Cursor::SetIsVisible(const bool fIsVisible) +void Cursor::SetIsVisible(const bool fIsVisible) noexcept { _fIsVisible = fIsVisible; _RedrawCursor(); } -void Cursor::SetIsOn(const bool fIsOn) +void Cursor::SetIsOn(const bool fIsOn) noexcept { _fIsOn = fIsOn; _RedrawCursorAlways(); } -void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) +void Cursor::SetBlinkingAllowed(const bool fBlinkingAllowed) noexcept { _fBlinkingAllowed = fBlinkingAllowed; _RedrawCursorAlways(); } -void Cursor::SetIsDouble(const bool fIsDouble) +void Cursor::SetIsDouble(const bool fIsDouble) noexcept { _fIsDouble = fIsDouble; _RedrawCursor(); } -void Cursor::SetIsConversionArea(const bool fIsConversionArea) +void Cursor::SetIsConversionArea(const bool fIsConversionArea) noexcept { // Functionally the same as "Hide cursor" // Never called with TRUE, it's only used in the creation of a @@ -125,19 +125,19 @@ void Cursor::SetIsConversionArea(const bool fIsConversionArea) _RedrawCursorAlways(); } -void Cursor::SetIsPopupShown(const bool fIsPopupShown) +void Cursor::SetIsPopupShown(const bool fIsPopupShown) noexcept { // Functionally the same as "Hide cursor" _fIsPopupShown = fIsPopupShown; _RedrawCursorAlways(); } -void Cursor::SetDelay(const bool fDelay) +void Cursor::SetDelay(const bool fDelay) noexcept { _fDelay = fDelay; } -void Cursor::SetSize(const ULONG ulSize) +void Cursor::SetSize(const ULONG ulSize) noexcept { _ulSize = ulSize; _RedrawCursor(); @@ -195,7 +195,7 @@ void Cursor::_RedrawCursorAlways() noexcept CATCH_LOG(); } -void Cursor::SetPosition(const COORD cPosition) +void Cursor::SetPosition(const COORD cPosition) noexcept { _RedrawCursor(); _cPosition.X = cPosition.X; @@ -204,7 +204,7 @@ void Cursor::SetPosition(const COORD cPosition) ResetDelayEOLWrap(); } -void Cursor::SetXPosition(const int NewX) +void Cursor::SetXPosition(const int NewX) noexcept { _RedrawCursor(); _cPosition.X = gsl::narrow(NewX); @@ -212,7 +212,7 @@ void Cursor::SetXPosition(const int NewX) ResetDelayEOLWrap(); } -void Cursor::SetYPosition(const int NewY) +void Cursor::SetYPosition(const int NewY) noexcept { _RedrawCursor(); _cPosition.Y = gsl::narrow(NewY); @@ -220,7 +220,7 @@ void Cursor::SetYPosition(const int NewY) ResetDelayEOLWrap(); } -void Cursor::IncrementXPosition(const int DeltaX) +void Cursor::IncrementXPosition(const int DeltaX) noexcept { _RedrawCursor(); _cPosition.X += gsl::narrow(DeltaX); @@ -228,7 +228,7 @@ void Cursor::IncrementXPosition(const int DeltaX) ResetDelayEOLWrap(); } -void Cursor::IncrementYPosition(const int DeltaY) +void Cursor::IncrementYPosition(const int DeltaY) noexcept { _RedrawCursor(); _cPosition.Y += gsl::narrow(DeltaY); @@ -236,7 +236,7 @@ void Cursor::IncrementYPosition(const int DeltaY) ResetDelayEOLWrap(); } -void Cursor::DecrementXPosition(const int DeltaX) +void Cursor::DecrementXPosition(const int DeltaX) noexcept { _RedrawCursor(); _cPosition.X -= gsl::narrow(DeltaX); @@ -244,7 +244,7 @@ void Cursor::DecrementXPosition(const int DeltaX) ResetDelayEOLWrap(); } -void Cursor::DecrementYPosition(const int DeltaY) +void Cursor::DecrementYPosition(const int DeltaY) noexcept { _RedrawCursor(); _cPosition.Y -= gsl::narrow(DeltaY); @@ -262,7 +262,7 @@ void Cursor::DecrementYPosition(const int DeltaY) // - OtherCursor - The cursor to copy properties from // Return Value: // - -void Cursor::CopyProperties(const Cursor& OtherCursor) +void Cursor::CopyProperties(const Cursor& OtherCursor) noexcept { // We shouldn't copy the position as it will be already rearranged by the resize operation. //_cPosition = pOtherCursor->_cPosition; @@ -288,34 +288,34 @@ void Cursor::CopyProperties(const Cursor& OtherCursor) _color = OtherCursor._color; } -void Cursor::DelayEOLWrap(const COORD coordDelayedAt) +void Cursor::DelayEOLWrap(const COORD coordDelayedAt) noexcept { _coordDelayedAt = coordDelayedAt; _fDelayedEolWrap = true; } -void Cursor::ResetDelayEOLWrap() +void Cursor::ResetDelayEOLWrap() noexcept { _coordDelayedAt = { 0 }; _fDelayedEolWrap = false; } -COORD Cursor::GetDelayedAtPosition() const +COORD Cursor::GetDelayedAtPosition() const noexcept { return _coordDelayedAt; } -bool Cursor::IsDelayedEOLWrap() const +bool Cursor::IsDelayedEOLWrap() const noexcept { return _fDelayedEolWrap; } -void Cursor::StartDeferDrawing() +void Cursor::StartDeferDrawing() noexcept { _fDeferCursorRedraw = true; } -void Cursor::EndDeferDrawing() +void Cursor::EndDeferDrawing() noexcept { if (_fHaveDeferredCursorRedraw) { @@ -325,27 +325,27 @@ void Cursor::EndDeferDrawing() _fDeferCursorRedraw = FALSE; } -const CursorType Cursor::GetType() const +const CursorType Cursor::GetType() const noexcept { return _cursorType; } -const bool Cursor::IsUsingColor() const +const bool Cursor::IsUsingColor() const noexcept { return GetColor() != INVALID_COLOR; } -const COLORREF Cursor::GetColor() const +const COLORREF Cursor::GetColor() const noexcept { return _color; } -void Cursor::SetColor(const unsigned int color) +void Cursor::SetColor(const unsigned int color) noexcept { _color = gsl::narrow_cast(color); } -void Cursor::SetType(const CursorType type) +void Cursor::SetType(const CursorType type) noexcept { _cursorType = type; } diff --git a/src/buffer/out/cursor.h b/src/buffer/out/cursor.h index 496e96f1ff2..140dcc9492a 100644 --- a/src/buffer/out/cursor.h +++ b/src/buffer/out/cursor.h @@ -28,7 +28,7 @@ class Cursor final public: static const unsigned int s_InvertCursorColor = INVALID_COLOR; - Cursor(const ULONG ulSize, TextBuffer& parentBuffer); + Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept; ~Cursor(); @@ -50,41 +50,41 @@ class Cursor final ULONG GetSize() const noexcept; COORD GetPosition() const noexcept; - const CursorType GetType() const; - const bool IsUsingColor() const; - const COLORREF GetColor() const; - - void StartDeferDrawing(); - void EndDeferDrawing(); - - void SetHasMoved(const bool fHasMoved); - void SetIsVisible(const bool fIsVisible); - void SetIsOn(const bool fIsOn); - void SetBlinkingAllowed(const bool fIsOn); - void SetIsDouble(const bool fIsDouble); - void SetIsConversionArea(const bool fIsConversionArea); - void SetIsPopupShown(const bool fIsPopupShown); - void SetDelay(const bool fDelay); - void SetSize(const ULONG ulSize); + const CursorType GetType() const noexcept; + const bool IsUsingColor() const noexcept; + const COLORREF GetColor() const noexcept; + + void StartDeferDrawing() noexcept; + void EndDeferDrawing() noexcept; + + void SetHasMoved(const bool fHasMoved) noexcept; + void SetIsVisible(const bool fIsVisible) noexcept; + void SetIsOn(const bool fIsOn) noexcept; + void SetBlinkingAllowed(const bool fIsOn) noexcept; + void SetIsDouble(const bool fIsDouble) noexcept; + void SetIsConversionArea(const bool fIsConversionArea) noexcept; + void SetIsPopupShown(const bool fIsPopupShown) noexcept; + void SetDelay(const bool fDelay) noexcept; + void SetSize(const ULONG ulSize) noexcept; void SetStyle(const ULONG ulSize, const COLORREF color, const CursorType type) noexcept; - void SetPosition(const COORD cPosition); - void SetXPosition(const int NewX); - void SetYPosition(const int NewY); - void IncrementXPosition(const int DeltaX); - void IncrementYPosition(const int DeltaY); - void DecrementXPosition(const int DeltaX); - void DecrementYPosition(const int DeltaY); + void SetPosition(const COORD cPosition) noexcept; + void SetXPosition(const int NewX) noexcept; + void SetYPosition(const int NewY) noexcept; + void IncrementXPosition(const int DeltaX) noexcept; + void IncrementYPosition(const int DeltaY) noexcept; + void DecrementXPosition(const int DeltaX) noexcept; + void DecrementYPosition(const int DeltaY) noexcept; - void CopyProperties(const Cursor& OtherCursor); + void CopyProperties(const Cursor& OtherCursor) noexcept; - void DelayEOLWrap(const COORD coordDelayedAt); - void ResetDelayEOLWrap(); - COORD GetDelayedAtPosition() const; - bool IsDelayedEOLWrap() const; + void DelayEOLWrap(const COORD coordDelayedAt) noexcept; + void ResetDelayEOLWrap() noexcept; + COORD GetDelayedAtPosition() const noexcept; + bool IsDelayedEOLWrap() const noexcept; - void SetColor(const unsigned int color); - void SetType(const CursorType type); + void SetColor(const unsigned int color) noexcept; + void SetType(const CursorType type) noexcept; private: TextBuffer& _parentBuffer; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 5e29ecddcc8..9103f5664ec 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -49,7 +49,7 @@ TextBuffer::TextBuffer(const COORD screenBufferSize, // - OtherBuffer - The text buffer to copy properties from // Return Value: // - -void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) +void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) noexcept { GetCursor().CopyProperties(OtherBuffer.GetCursor()); } @@ -60,7 +60,7 @@ void TextBuffer::CopyProperties(const TextBuffer& OtherBuffer) // - // Return Value: // - Total number of rows in the buffer -UINT TextBuffer::TotalRowCount() const +UINT TextBuffer::TotalRowCount() const noexcept { return gsl::narrow(_storage.size()); } @@ -640,7 +640,7 @@ COORD TextBuffer::_GetPreviousFromCursor() const return coordPosition; } -const SHORT TextBuffer::GetFirstRowIndex() const +const SHORT TextBuffer::GetFirstRowIndex() const noexcept { return _firstRow; } @@ -649,7 +649,7 @@ const Viewport TextBuffer::GetSize() const return Viewport::FromDimensions({ 0, 0 }, { gsl::narrow(_storage.at(0).size()), gsl::narrow(_storage.size()) }); } -void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) +void TextBuffer::_SetFirstRowIndex(const SHORT FirstRowIndex) noexcept { _firstRow = FirstRowIndex; } @@ -755,12 +755,12 @@ void TextBuffer::ScrollRows(const SHORT firstRow, const SHORT size, const SHORT _RefreshRowIDs(std::nullopt); } -Cursor& TextBuffer::GetCursor() +Cursor& TextBuffer::GetCursor() noexcept { return _cursor; } -const Cursor& TextBuffer::GetCursor() const +const Cursor& TextBuffer::GetCursor() const noexcept { return _cursor; } @@ -795,7 +795,7 @@ void TextBuffer::Reset() // - newSize - new size of screen. // Return Value: // - Success if successful. Invalid parameter if screen buffer size is unexpected. No memory if allocation failed. -[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize) noexcept +[[nodiscard]] NTSTATUS TextBuffer::ResizeTraditional(const COORD newSize) { RETURN_HR_IF(E_INVALIDARG, newSize.X < 0 || newSize.Y < 0); @@ -843,12 +843,12 @@ void TextBuffer::Reset() return S_OK; } -const UnicodeStorage& TextBuffer::GetUnicodeStorage() const +const UnicodeStorage& TextBuffer::GetUnicodeStorage() const noexcept { return _unicodeStorage; } -UnicodeStorage& TextBuffer::GetUnicodeStorage() +UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept { return _unicodeStorage; } @@ -932,7 +932,7 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row) // - // Return Value: // - This buffer's current render target. -Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() +Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcept { return _renderTarget; } diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index 97b3375606d..beb477fbbd6 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -72,7 +72,7 @@ class TextBuffer final ~TextBuffer() = default; // Used for duplicating properties to another text buffer - void CopyProperties(const TextBuffer& OtherBuffer); + void CopyProperties(const TextBuffer& OtherBuffer) noexcept; // row manipulation const ROW& GetRowByOffset(const size_t index) const; @@ -107,16 +107,16 @@ class TextBuffer final COORD GetLastNonSpaceCharacter() const; COORD GetLastNonSpaceCharacter(const Microsoft::Console::Types::Viewport viewport) const; - Cursor& GetCursor(); - const Cursor& GetCursor() const; + Cursor& GetCursor() noexcept; + const Cursor& GetCursor() const noexcept; - const SHORT GetFirstRowIndex() const; + const SHORT GetFirstRowIndex() const noexcept; const Microsoft::Console::Types::Viewport GetSize() const; void ScrollRows(const SHORT firstRow, const SHORT size, const SHORT delta); - UINT TotalRowCount() const; + UINT TotalRowCount() const noexcept; [[nodiscard]] TextAttribute GetCurrentAttributes() const noexcept; @@ -124,12 +124,12 @@ class TextBuffer final void Reset(); - [[nodiscard]] HRESULT ResizeTraditional(const COORD newSize) noexcept; + [[nodiscard]] HRESULT ResizeTraditional(const COORD newSize); - const UnicodeStorage& GetUnicodeStorage() const; - UnicodeStorage& GetUnicodeStorage(); + const UnicodeStorage& GetUnicodeStorage() const noexcept; + UnicodeStorage& GetUnicodeStorage() noexcept; - Microsoft::Console::Render::IRenderTarget& GetRenderTarget(); + Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept; class TextAndColor { @@ -165,7 +165,7 @@ class TextBuffer final Microsoft::Console::Render::IRenderTarget& _renderTarget; - void _SetFirstRowIndex(const SHORT FirstRowIndex); + void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept; COORD _GetPreviousFromCursor() const; diff --git a/src/buffer/out/textBufferCellIterator.cpp b/src/buffer/out/textBufferCellIterator.cpp index b0d09eec2e3..999370cfa7d 100644 --- a/src/buffer/out/textBufferCellIterator.cpp +++ b/src/buffer/out/textBufferCellIterator.cpp @@ -65,7 +65,7 @@ TextBufferCellIterator::operator bool() const noexcept // - it - The other iterator to compare to this one. // Return Value: // - True if it's the same text buffer and same cell position. False otherwise. -bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const noexcept +bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const { return _pos == it._pos && &_buffer == &it._buffer && @@ -81,7 +81,7 @@ bool TextBufferCellIterator::operator==(const TextBufferCellIterator& it) const // - it - The other iterator to compare to this one. // Return Value: // - True if it's the same text buffer and different cell position or if they're different buffers. False otherwise. -bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const noexcept +bool TextBufferCellIterator::operator!=(const TextBufferCellIterator& it) const { return !(*this == it); } diff --git a/src/buffer/out/textBufferCellIterator.hpp b/src/buffer/out/textBufferCellIterator.hpp index 730154f28ab..68c6e1531a5 100644 --- a/src/buffer/out/textBufferCellIterator.hpp +++ b/src/buffer/out/textBufferCellIterator.hpp @@ -32,8 +32,8 @@ class TextBufferCellIterator operator bool() const noexcept; - bool operator==(const TextBufferCellIterator& it) const noexcept; - bool operator!=(const TextBufferCellIterator& it) const noexcept; + bool operator==(const TextBufferCellIterator& it) const; + bool operator!=(const TextBufferCellIterator& it) const; TextBufferCellIterator& operator+=(const ptrdiff_t& movement); TextBufferCellIterator& operator-=(const ptrdiff_t& movement); diff --git a/src/buffer/out/textBufferTextIterator.cpp b/src/buffer/out/textBufferTextIterator.cpp index 05c6a4c997d..b8a17d11368 100644 --- a/src/buffer/out/textBufferTextIterator.cpp +++ b/src/buffer/out/textBufferTextIterator.cpp @@ -16,7 +16,7 @@ using namespace Microsoft::Console::Types; // - Narrows the view of a cell iterator into a text only iterator. // Arguments: // - A cell iterator -TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) : +TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) noexcept: TextBufferCellIterator(cellIt) { } @@ -25,7 +25,7 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data -const std::wstring_view TextBufferTextIterator::operator*() const +const std::wstring_view TextBufferTextIterator::operator*() const noexcept { return _view.Chars(); } @@ -34,7 +34,7 @@ const std::wstring_view TextBufferTextIterator::operator*() const // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data -const std::wstring_view* TextBufferTextIterator::operator->() const +const std::wstring_view* TextBufferTextIterator::operator->() const noexcept { return &_view.Chars(); } diff --git a/src/buffer/out/textBufferTextIterator.hpp b/src/buffer/out/textBufferTextIterator.hpp index 845e86e19c8..634745eec63 100644 --- a/src/buffer/out/textBufferTextIterator.hpp +++ b/src/buffer/out/textBufferTextIterator.hpp @@ -22,10 +22,10 @@ class SCREEN_INFORMATION; class TextBufferTextIterator final : public TextBufferCellIterator { public: - TextBufferTextIterator(const TextBufferCellIterator& cellIter); + TextBufferTextIterator(const TextBufferCellIterator& cellIter) noexcept; - const std::wstring_view operator*() const; - const std::wstring_view* operator->() const; + const std::wstring_view operator*() const noexcept; + const std::wstring_view* operator->() const noexcept; protected: #if UNIT_TESTING diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 38e9f1d338f..8d7d290ae3b 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -88,7 +88,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::Draw(_In_opt_ void* clientDrawingContext, _In_ IDWriteTextRenderer* renderer, FLOAT originX, - FLOAT originY) + FLOAT originY) noexcept { RETURN_IF_FAILED(_AnalyzeRuns()); RETURN_IF_FAILED(_ShapeGlyphRuns()); @@ -585,7 +585,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - S_OK or appropriate STL/GSL failure code. [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetTextBeforePosition(UINT32 textPosition, _Outptr_result_buffer_(*textLength) WCHAR const** textString, - _Out_ UINT32* textLength) + _Out_ UINT32* textLength) noexcept { *textString = nullptr; *textLength = 0; @@ -606,7 +606,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - // Return Value: // - The reading direction held for this layout from construction -[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection() +[[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE CustomTextLayout::GetParagraphReadingDirection() noexcept { return _readingDirection; } @@ -622,7 +622,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - S_OK or appropriate STL/GSL failure code. [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetLocaleName(UINT32 textPosition, _Out_ UINT32* textLength, - _Outptr_result_z_ WCHAR const** localeName) + _Outptr_result_z_ WCHAR const** localeName) noexcept { *localeName = _localeName.data(); *textLength = gsl::narrow(_text.size()) - textPosition; @@ -641,7 +641,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - S_OK or appropriate STL/GSL failure code. [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetNumberSubstitution(UINT32 textPosition, _Out_ UINT32* textLength, - _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) + _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept { *numberSubstitution = nullptr; *textLength = gsl::narrow(_text.size()) - textPosition; diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index b02d263a0eb..3363139b9c5 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -32,7 +32,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT STDMETHODCALLTYPE Draw(_In_opt_ void* clientDrawingContext, _In_ IDWriteTextRenderer* renderer, FLOAT originX, - FLOAT originY); + FLOAT originY) noexcept; // IDWriteTextAnalysisSource methods [[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextAtPosition(UINT32 textPosition, @@ -40,14 +40,14 @@ namespace Microsoft::Console::Render _Out_ UINT32* textLength) override; [[nodiscard]] HRESULT STDMETHODCALLTYPE GetTextBeforePosition(UINT32 textPosition, _Outptr_result_buffer_(*textLength) WCHAR const** textString, - _Out_ UINT32* textLength) override; - [[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() override; + _Out_ UINT32* textLength) noexcept override; + [[nodiscard]] DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection() noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE GetLocaleName(UINT32 textPosition, _Out_ UINT32* textLength, - _Outptr_result_z_ WCHAR const** localeName) override; + _Outptr_result_z_ WCHAR const** localeName) noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE GetNumberSubstitution(UINT32 textPosition, _Out_ UINT32* textLength, - _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) override; + _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept override; // IDWriteTextAnalysisSink methods [[nodiscard]] HRESULT STDMETHODCALLTYPE SetScriptAnalysis(UINT32 textPosition, @@ -93,12 +93,12 @@ namespace Microsoft::Console::Render ::Microsoft::WRL::ComPtr fontFace; FLOAT fontScale; - inline bool ContainsTextPosition(UINT32 desiredTextPosition) const + inline bool ContainsTextPosition(UINT32 desiredTextPosition) const noexcept { return desiredTextPosition >= textStart && desiredTextPosition < textStart + textLength; } - inline bool operator==(UINT32 desiredTextPosition) const + inline bool operator==(UINT32 desiredTextPosition) const noexcept { // Search by text position using std::find return ContainsTextPosition(desiredTextPosition); diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index 79dec1dc672..bdcab5164a1 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -21,7 +21,7 @@ using namespace Microsoft::Console::Render; // Return Value: // - S_OK [[nodiscard]] HRESULT CustomTextRenderer::IsPixelSnappingDisabled(void* /*clientDrawingContext*/, - _Out_ BOOL* isDisabled) + _Out_ BOOL* isDisabled) noexcept { *isDisabled = false; return S_OK; @@ -38,7 +38,7 @@ using namespace Microsoft::Console::Render; // Return Value: // - S_OK [[nodiscard]] HRESULT CustomTextRenderer::GetPixelsPerDip(void* clientDrawingContext, - _Out_ FLOAT* pixelsPerDip) + _Out_ FLOAT* pixelsPerDip) noexcept { DrawingContext* drawingContext = static_cast(clientDrawingContext); @@ -58,7 +58,7 @@ using namespace Microsoft::Console::Render; // Return Value: // - S_OK [[nodiscard]] HRESULT CustomTextRenderer::GetCurrentTransform(void* clientDrawingContext, - DWRITE_MATRIX* transform) + DWRITE_MATRIX* transform) noexcept { DrawingContext* drawingContext = static_cast(clientDrawingContext); @@ -88,7 +88,7 @@ using namespace Microsoft::Console::Render; FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ const DWRITE_UNDERLINE* underline, - IUnknown* clientDrawingEffect) + IUnknown* clientDrawingEffect) noexcept { _FillRectangle(clientDrawingContext, clientDrawingEffect, @@ -120,7 +120,7 @@ using namespace Microsoft::Console::Render; FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ const DWRITE_STRIKETHROUGH* strikethrough, - IUnknown* clientDrawingEffect) + IUnknown* clientDrawingEffect) noexcept { _FillRectangle(clientDrawingContext, clientDrawingEffect, @@ -153,7 +153,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, float width, float thickness, DWRITE_READING_DIRECTION /*readingDirection*/, - DWRITE_FLOW_DIRECTION /*flowDirection*/) + DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept { DrawingContext* drawingContext = static_cast(clientDrawingContext); @@ -189,7 +189,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, IDWriteInlineObject* inlineObject, BOOL isSideways, BOOL isRightToLeft, - IUnknown* clientDrawingEffect) + IUnknown* clientDrawingEffect) noexcept { return inlineObject->Draw(clientDrawingContext, this, @@ -433,7 +433,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, D2D1_POINT_2F baselineOrigin, DWRITE_MEASURING_MODE /*measuringMode*/, _In_ const DWRITE_GLYPH_RUN* glyphRun, - _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept { // This is regular text but manually ::Microsoft::WRL::ComPtr d2dFactory; @@ -473,7 +473,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, D2D1_POINT_2F baselineOrigin, DWRITE_MEASURING_MODE /*measuringMode*/, _In_ const DWRITE_GLYPH_RUN* glyphRun, - _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept { // This is glow text manually ::Microsoft::WRL::ComPtr d2dFactory; diff --git a/src/renderer/dx/CustomTextRenderer.h b/src/renderer/dx/CustomTextRenderer.h index e539f945372..8c38e4bf77f 100644 --- a/src/renderer/dx/CustomTextRenderer.h +++ b/src/renderer/dx/CustomTextRenderer.h @@ -15,7 +15,7 @@ namespace Microsoft::Console::Render IDWriteFactory* dwriteFactory, const DWRITE_LINE_SPACING spacing, const D2D_SIZE_F cellSize, - const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE) + const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE) noexcept { this->renderTarget = renderTarget; this->foregroundBrush = foregroundBrush; @@ -43,13 +43,13 @@ namespace Microsoft::Console::Render // IDWritePixelSnapping methods [[nodiscard]] HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void* clientDrawingContext, - _Out_ BOOL* isDisabled) override; + _Out_ BOOL* isDisabled) noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void* clientDrawingContext, - _Out_ FLOAT* pixelsPerDip) override; + _Out_ FLOAT* pixelsPerDip) noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE GetCurrentTransform(void* clientDrawingContext, - _Out_ DWRITE_MATRIX* transform) override; + _Out_ DWRITE_MATRIX* transform) noexcept override; // IDWriteTextRenderer methods [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawGlyphRun(void* clientDrawingContext, @@ -64,13 +64,13 @@ namespace Microsoft::Console::Render FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ const DWRITE_UNDERLINE* underline, - IUnknown* clientDrawingEffect) override; + IUnknown* clientDrawingEffect) noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawStrikethrough(void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, _In_ const DWRITE_STRIKETHROUGH* strikethrough, - IUnknown* clientDrawingEffect) override; + IUnknown* clientDrawingEffect) noexcept override; [[nodiscard]] HRESULT STDMETHODCALLTYPE DrawInlineObject(void* clientDrawingContext, FLOAT originX, @@ -78,7 +78,7 @@ namespace Microsoft::Console::Render IDWriteInlineObject* inlineObject, BOOL isSideways, BOOL isRightToLeft, - IUnknown* clientDrawingEffect) override; + IUnknown* clientDrawingEffect) noexcept override; private: void _FillRectangle(void* clientDrawingContext, @@ -88,7 +88,7 @@ namespace Microsoft::Console::Render float width, float thickness, DWRITE_READING_DIRECTION readingDirection, - DWRITE_FLOW_DIRECTION flowDirection); + DWRITE_FLOW_DIRECTION flowDirection) noexcept; [[nodiscard]] HRESULT _DrawBasicGlyphRun(DrawingContext* clientDrawingContext, D2D1_POINT_2F baselineOrigin, @@ -101,12 +101,12 @@ namespace Microsoft::Console::Render D2D1_POINT_2F baselineOrigin, DWRITE_MEASURING_MODE measuringMode, _In_ const DWRITE_GLYPH_RUN* glyphRun, - _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription); + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription) noexcept; [[nodiscard]] HRESULT _DrawGlowGlyphRun(DrawingContext* clientDrawingContext, D2D1_POINT_2F baselineOrigin, DWRITE_MEASURING_MODE measuringMode, _In_ const DWRITE_GLYPH_RUN* glyphRun, - _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription); + _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription) noexcept; }; } diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index cecdb78e797..9b13f1a08eb 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -129,7 +129,7 @@ DxEngine::~DxEngine() _ReleaseDeviceResources(); } - auto freeOnFail = wil::scope_exit([&] { _ReleaseDeviceResources(); }); + auto freeOnFail = wil::scope_exit([&]() noexcept { _ReleaseDeviceResources(); }); RETURN_IF_FAILED(CreateDXGIFactory1(IID_PPV_ARGS(&_dxgiFactory2))); @@ -196,58 +196,62 @@ DxEngine::~DxEngine() SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; SwapChainDesc.Scaling = DXGI_SCALING_NONE; - switch (_chainMode) + try { - case SwapChainMode::ForHwnd: - { - // use the HWND's dimensions for the swap chain dimensions. - RECT rect = { 0 }; - RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect)); - - SwapChainDesc.Width = rect.right - rect.left; - SwapChainDesc.Height = rect.bottom - rect.top; - - // We can't do alpha for HWNDs. Set to ignore. It will fail otherwise. - SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - const auto createSwapChainResult = _dxgiFactory2->CreateSwapChainForHwnd(_d3dDevice.Get(), - _hwndTarget, - &SwapChainDesc, - nullptr, - nullptr, - &_dxgiSwapChain); - if (FAILED(createSwapChainResult)) + switch (_chainMode) { - SwapChainDesc.Scaling = DXGI_SCALING_STRETCH; - RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForHwnd(_d3dDevice.Get(), - _hwndTarget, - &SwapChainDesc, - nullptr, - nullptr, - &_dxgiSwapChain)); + case SwapChainMode::ForHwnd: + { + // use the HWND's dimensions for the swap chain dimensions. + RECT rect = { 0 }; + RETURN_IF_WIN32_BOOL_FALSE(GetClientRect(_hwndTarget, &rect)); + + SwapChainDesc.Width = rect.right - rect.left; + SwapChainDesc.Height = rect.bottom - rect.top; + + // We can't do alpha for HWNDs. Set to ignore. It will fail otherwise. + SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + const auto createSwapChainResult = _dxgiFactory2->CreateSwapChainForHwnd(_d3dDevice.Get(), + _hwndTarget, + &SwapChainDesc, + nullptr, + nullptr, + &_dxgiSwapChain); + if (FAILED(createSwapChainResult)) + { + SwapChainDesc.Scaling = DXGI_SCALING_STRETCH; + RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForHwnd(_d3dDevice.Get(), + _hwndTarget, + &SwapChainDesc, + nullptr, + nullptr, + &_dxgiSwapChain)); + } + + break; } + case SwapChainMode::ForComposition: + { + // Use the given target size for compositions. + SwapChainDesc.Width = _displaySizePixels.cx; + SwapChainDesc.Height = _displaySizePixels.cy; - break; - } - case SwapChainMode::ForComposition: - { - // Use the given target size for compositions. - SwapChainDesc.Width = _displaySizePixels.cx; - SwapChainDesc.Height = _displaySizePixels.cy; - - // We're doing advanced composition pretty much for the purpose of pretty alpha, so turn it on. - SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; - // It's 100% required to use scaling mode stretch for composition. There is no other choice. - SwapChainDesc.Scaling = DXGI_SCALING_STRETCH; - - RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForComposition(_d3dDevice.Get(), - &SwapChainDesc, - nullptr, - &_dxgiSwapChain)); - break; - } - default: - THROW_HR(E_NOTIMPL); + // We're doing advanced composition pretty much for the purpose of pretty alpha, so turn it on. + SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; + // It's 100% required to use scaling mode stretch for composition. There is no other choice. + SwapChainDesc.Scaling = DXGI_SCALING_STRETCH; + + RETURN_IF_FAILED(_dxgiFactory2->CreateSwapChainForComposition(_d3dDevice.Get(), + &SwapChainDesc, + nullptr, + &_dxgiSwapChain)); + break; + } + default: + THROW_HR(E_NOTIMPL); + } } + CATCH_RETURN(); // With a new swap chain, mark the entire thing as invalid. RETURN_IF_FAILED(InvalidateAll()); @@ -265,9 +269,14 @@ DxEngine::~DxEngine() freeOnFail.release(); // don't need to release if we made it to the bottom and everything was good. // Notify that swap chain changed. + if (_pfn) { - _pfn(); + try + { + _pfn(); + } + CATCH_LOG(); // A failure in the notification function isn't a failure to prepare, so just log it and go on. } return S_OK; @@ -275,54 +284,57 @@ DxEngine::~DxEngine() [[nodiscard]] HRESULT DxEngine::_PrepareRenderTarget() noexcept { - RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&_dxgiSurface))); - - const D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties( - D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), - 0.0f, - 0.0f); - - RETURN_IF_FAILED(_d2dFactory->CreateDxgiSurfaceRenderTarget(_dxgiSurface.Get(), - &props, - &_d2dRenderTarget)); - - _d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); - RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::DarkRed), - &_d2dBrushBackground)); - - RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), - &_d2dBrushForeground)); - - const D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties{ - D2D1_CAP_STYLE_SQUARE, // startCap - D2D1_CAP_STYLE_SQUARE, // endCap - D2D1_CAP_STYLE_SQUARE, // dashCap - D2D1_LINE_JOIN_MITER, // lineJoin - 0.f, // miterLimit - D2D1_DASH_STYLE_SOLID, // dashStyle - 0.f, // dashOffset - }; - RETURN_IF_FAILED(_d2dFactory->CreateStrokeStyle(&strokeStyleProperties, nullptr, 0, &_strokeStyle)); - - // If in composition mode, apply scaling factor matrix - if (_chainMode == SwapChainMode::ForComposition) + try { - const auto fdpi = static_cast(_dpi); - _d2dRenderTarget->SetDpi(fdpi, fdpi); + RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&_dxgiSurface))); + + const D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), + 0.0f, + 0.0f); + + RETURN_IF_FAILED(_d2dFactory->CreateDxgiSurfaceRenderTarget(_dxgiSurface.Get(), + &props, + &_d2dRenderTarget)); + + _d2dRenderTarget->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::DarkRed), + &_d2dBrushBackground)); + + RETURN_IF_FAILED(_d2dRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), + &_d2dBrushForeground)); + + const D2D1_STROKE_STYLE_PROPERTIES strokeStyleProperties{ + D2D1_CAP_STYLE_SQUARE, // startCap + D2D1_CAP_STYLE_SQUARE, // endCap + D2D1_CAP_STYLE_SQUARE, // dashCap + D2D1_LINE_JOIN_MITER, // lineJoin + 0.f, // miterLimit + D2D1_DASH_STYLE_SOLID, // dashStyle + 0.f, // dashOffset + }; + RETURN_IF_FAILED(_d2dFactory->CreateStrokeStyle(&strokeStyleProperties, nullptr, 0, &_strokeStyle)); + + // If in composition mode, apply scaling factor matrix + if (_chainMode == SwapChainMode::ForComposition) + { + const auto fdpi = static_cast(_dpi); + _d2dRenderTarget->SetDpi(fdpi, fdpi); - DXGI_MATRIX_3X2_F inverseScale = { 0 }; - inverseScale._11 = 1.0f / _scale; - inverseScale._22 = inverseScale._11; + DXGI_MATRIX_3X2_F inverseScale = { 0 }; + inverseScale._11 = 1.0f / _scale; + inverseScale._22 = inverseScale._11; - ::Microsoft::WRL::ComPtr sc2; - RETURN_IF_FAILED(_dxgiSwapChain.As(&sc2)); + ::Microsoft::WRL::ComPtr sc2; + RETURN_IF_FAILED(_dxgiSwapChain.As(&sc2)); - RETURN_IF_FAILED(sc2->SetMatrixTransform(&inverseScale)); + RETURN_IF_FAILED(sc2->SetMatrixTransform(&inverseScale)); + } + return S_OK; } - - return S_OK; + CATCH_RETURN(); } // Routine Description: @@ -333,31 +345,35 @@ DxEngine::~DxEngine() // - void DxEngine::_ReleaseDeviceResources() noexcept { - _haveDeviceResources = false; - _d2dBrushForeground.Reset(); - _d2dBrushBackground.Reset(); - - if (nullptr != _d2dRenderTarget.Get() && _isPainting) + try { - _d2dRenderTarget->EndDraw(); - } + _haveDeviceResources = false; + _d2dBrushForeground.Reset(); + _d2dBrushBackground.Reset(); - _d2dRenderTarget.Reset(); + if (nullptr != _d2dRenderTarget.Get() && _isPainting) + { + _d2dRenderTarget->EndDraw(); + } - _dxgiSurface.Reset(); - _dxgiSwapChain.Reset(); + _d2dRenderTarget.Reset(); - if (nullptr != _d3dDeviceContext.Get()) - { - // To ensure the swap chain goes away we must unbind any views from the - // D3D pipeline - _d3dDeviceContext->OMSetRenderTargets(0, nullptr, nullptr); - } - _d3dDeviceContext.Reset(); + _dxgiSurface.Reset(); + _dxgiSwapChain.Reset(); + + if (nullptr != _d3dDeviceContext.Get()) + { + // To ensure the swap chain goes away we must unbind any views from the + // D3D pipeline + _d3dDeviceContext->OMSetRenderTargets(0, nullptr, nullptr); + } + _d3dDeviceContext.Reset(); - _d3dDevice.Reset(); + _d3dDevice.Reset(); - _dxgiFactory2.Reset(); + _dxgiFactory2.Reset(); + } + CATCH_LOG(); } // Routine Description: @@ -410,7 +426,7 @@ void DxEngine::SetCallback(std::function pfn) _pfn = pfn; } -Microsoft::WRL::ComPtr DxEngine::GetSwapChain() noexcept +Microsoft::WRL::ComPtr DxEngine::GetSwapChain() { if (_dxgiSwapChain.Get() == nullptr) { @@ -484,39 +500,43 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() noexcept { if (pcoordDelta->X != 0 || pcoordDelta->Y != 0) { - POINT delta = { 0 }; - delta.x = pcoordDelta->X * _glyphCell.cx; - delta.y = pcoordDelta->Y * _glyphCell.cy; + try + { + POINT delta = { 0 }; + delta.x = pcoordDelta->X * _glyphCell.cx; + delta.y = pcoordDelta->Y * _glyphCell.cy; - _InvalidOffset(delta); + _InvalidOffset(delta); - _invalidScroll.cx += delta.x; - _invalidScroll.cy += delta.y; + _invalidScroll.cx += delta.x; + _invalidScroll.cy += delta.y; - // Add the revealed portion of the screen from the scroll to the invalid area. - const RECT display = _GetDisplayRect(); - RECT reveal = display; + // Add the revealed portion of the screen from the scroll to the invalid area. + const RECT display = _GetDisplayRect(); + RECT reveal = display; - // X delta first - OffsetRect(&reveal, delta.x, 0); - IntersectRect(&reveal, &reveal, &display); - SubtractRect(&reveal, &display, &reveal); + // X delta first + OffsetRect(&reveal, delta.x, 0); + IntersectRect(&reveal, &reveal, &display); + SubtractRect(&reveal, &display, &reveal); - if (!IsRectEmpty(&reveal)) - { - _InvalidOr(reveal); - } + if (!IsRectEmpty(&reveal)) + { + _InvalidOr(reveal); + } - // Y delta second (subtract rect won't work if you move both) - reveal = display; - OffsetRect(&reveal, 0, delta.y); - IntersectRect(&reveal, &reveal, &display); - SubtractRect(&reveal, &display, &reveal); + // Y delta second (subtract rect won't work if you move both) + reveal = display; + OffsetRect(&reveal, 0, delta.y); + IntersectRect(&reveal, &reveal, &display); + SubtractRect(&reveal, &display, &reveal); - if (!IsRectEmpty(&reveal)) - { - _InvalidOr(reveal); + if (!IsRectEmpty(&reveal)) + { + _InvalidOr(reveal); + } } + CATCH_RETURN(); } return S_OK; @@ -577,7 +597,7 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() noexcept return size; } default: - THROW_HR(E_NOTIMPL); + FAIL_FAST_HR(E_NOTIMPL); } } @@ -617,7 +637,7 @@ void _ScaleByFont(RECT& cellsToPixels, SIZE fontSize) noexcept // - -Y is up, Y is down, -X is left, X is right. // Return Value: // - -void DxEngine::_InvalidOffset(POINT delta) noexcept +void DxEngine::_InvalidOffset(POINT delta) { if (_isInvalidUsed) { @@ -706,37 +726,41 @@ void DxEngine::_InvalidOr(RECT rc) noexcept if (_isEnabled) { - const auto clientSize = _GetClientSize(); - if (!_haveDeviceResources) + try { - RETURN_IF_FAILED(_CreateDeviceResources(true)); - } - else if (_displaySizePixels.cy != clientSize.cy || - _displaySizePixels.cx != clientSize.cx) - { - // OK, we're going to play a dangerous game here for the sake of optimizing resize - // First, set up a complete clear of all device resources if something goes terribly wrong. - auto resetDeviceResourcesOnFailure = wil::scope_exit([&] { - _ReleaseDeviceResources(); - }); + const auto clientSize = _GetClientSize(); + if (!_haveDeviceResources) + { + RETURN_IF_FAILED(_CreateDeviceResources(true)); + } + else if (_displaySizePixels.cy != clientSize.cy || + _displaySizePixels.cx != clientSize.cx) + { + // OK, we're going to play a dangerous game here for the sake of optimizing resize + // First, set up a complete clear of all device resources if something goes terribly wrong. + auto resetDeviceResourcesOnFailure = wil::scope_exit([&]() noexcept { + _ReleaseDeviceResources(); + }); - // Now let go of a few of the device resources that get in the way of resizing buffers in the swap chain - _dxgiSurface.Reset(); - _d2dRenderTarget.Reset(); + // Now let go of a few of the device resources that get in the way of resizing buffers in the swap chain + _dxgiSurface.Reset(); + _d2dRenderTarget.Reset(); - // Change the buffer size and recreate the render target (and surface) - RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.cx, clientSize.cy, DXGI_FORMAT_B8G8R8A8_UNORM, 0)); - RETURN_IF_FAILED(_PrepareRenderTarget()); + // Change the buffer size and recreate the render target (and surface) + RETURN_IF_FAILED(_dxgiSwapChain->ResizeBuffers(2, clientSize.cx, clientSize.cy, DXGI_FORMAT_B8G8R8A8_UNORM, 0)); + RETURN_IF_FAILED(_PrepareRenderTarget()); - // OK we made it past the parts that can cause errors. We can release our failure handler. - resetDeviceResourcesOnFailure.release(); + // OK we made it past the parts that can cause errors. We can release our failure handler. + resetDeviceResourcesOnFailure.release(); - // And persist the new size. - _displaySizePixels = clientSize; - } + // And persist the new size. + _displaySizePixels = clientSize; + } - _d2dRenderTarget->BeginDraw(); - _isPainting = true; + _d2dRenderTarget->BeginDraw(); + _isPainting = true; + } + CATCH_RETURN(); } return S_OK; @@ -811,13 +835,17 @@ void DxEngine::_InvalidOr(RECT rc) noexcept // - Any DirectX error, a memory error, etc. [[nodiscard]] HRESULT DxEngine::_CopyFrontToBack() noexcept { - Microsoft::WRL::ComPtr backBuffer; - Microsoft::WRL::ComPtr frontBuffer; + try + { + Microsoft::WRL::ComPtr backBuffer; + Microsoft::WRL::ComPtr frontBuffer; - RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))); - RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(1, IID_PPV_ARGS(&frontBuffer))); + RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))); + RETURN_IF_FAILED(_dxgiSwapChain->GetBuffer(1, IID_PPV_ARGS(&frontBuffer))); - _d3dDeviceContext->CopyResource(backBuffer.Get(), frontBuffer.Get()); + _d3dDeviceContext->CopyResource(backBuffer.Get(), frontBuffer.Get()); + } + CATCH_RETURN(); return S_OK; } @@ -833,16 +861,20 @@ void DxEngine::_InvalidOr(RECT rc) noexcept { if (_presentReady) { - FAIL_FAST_IF_FAILED(_dxgiSwapChain->Present(1, 0)); - /*FAIL_FAST_IF_FAILED(_dxgiSwapChain->Present1(1, 0, &_presentParams));*/ + try + { + FAIL_FAST_IF_FAILED(_dxgiSwapChain->Present(1, 0)); + /*FAIL_FAST_IF_FAILED(_dxgiSwapChain->Present1(1, 0, &_presentParams));*/ - RETURN_IF_FAILED(_CopyFrontToBack()); - _presentReady = false; + RETURN_IF_FAILED(_CopyFrontToBack()); + _presentReady = false; - _presentDirty = { 0 }; - _presentOffset = { 0 }; - _presentScroll = { 0 }; - _presentParams = { 0 }; + _presentDirty = { 0 }; + _presentOffset = { 0 }; + _presentScroll = { 0 }; + _presentParams = { 0 }; + } + CATCH_RETURN(); } return S_OK; @@ -945,7 +977,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept COORD const coordTarget) noexcept { const auto existingColor = _d2dBrushForeground->GetColor(); - const auto restoreBrushOnExit = wil::scope_exit([&] { _d2dBrushForeground->SetColor(existingColor); }); + const auto restoreBrushOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); }); _d2dBrushForeground->SetColor(_ColorFFromColorRef(color)); @@ -1028,7 +1060,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept 0.5f); _d2dBrushForeground->SetColor(selectionColor); - const auto resetColorOnExit = wil::scope_exit([&] { _d2dBrushForeground->SetColor(existingColor); }); + const auto resetColorOnExit = wil::scope_exit([&]() noexcept { _d2dBrushForeground->SetColor(existingColor); }); RECT pixels; pixels.left = rect.Left * _glyphCell.cx; @@ -1202,19 +1234,24 @@ enum class CursorPaintType // - S_OK or relevant DirectX error [[nodiscard]] HRESULT DxEngine::UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo) noexcept { - const auto hr = _GetProposedFont(pfiFontInfoDesired, + RETURN_IF_FAILED(_GetProposedFont(pfiFontInfoDesired, fiFontInfo, _dpi, _dwriteTextFormat, _dwriteTextAnalyzer, - _dwriteFontFace); + _dwriteFontFace)); - const auto size = fiFontInfo.GetSize(); + try + { + const auto size = fiFontInfo.GetSize(); - _glyphCell.cx = size.X; - _glyphCell.cy = size.Y; - return hr; + _glyphCell.cx = size.X; + _glyphCell.cy = size.Y; + } + CATCH_RETURN(); + + return S_OK; } [[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept @@ -1297,7 +1334,7 @@ float DxEngine::GetScaling() const noexcept // - // Return Value: // - Rectangle describing dirty area in characters. -[[nodiscard]] SMALL_RECT DxEngine::GetDirtyRectInChars() noexcept +[[nodiscard]] SMALL_RECT DxEngine::GetDirtyRectInChars() { SMALL_RECT r; r.Top = gsl::narrow(floor(_invalidRect.top / _glyphCell.cy)); @@ -1345,20 +1382,24 @@ float DxEngine::GetScaling() const noexcept // - S_OK or relevant DirectWrite error. [[nodiscard]] HRESULT DxEngine::IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept { - const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout. + try + { + const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout. - // Create the text layout - CustomTextLayout layout(_dwriteFactory.Get(), - _dwriteTextAnalyzer.Get(), - _dwriteTextFormat.Get(), - _dwriteFontFace.Get(), - { &cluster, 1 }, - _glyphCell.cx); + // Create the text layout + CustomTextLayout layout(_dwriteFactory.Get(), + _dwriteTextAnalyzer.Get(), + _dwriteTextFormat.Get(), + _dwriteFontFace.Get(), + { &cluster, 1 }, + _glyphCell.cx); - UINT32 columns = 0; - RETURN_IF_FAILED(layout.GetColumns(&columns)); + UINT32 columns = 0; + RETURN_IF_FAILED(layout.GetColumns(&columns)); - *pResult = columns != 1; + *pResult = columns != 1; + } + CATCH_RETURN(); return S_OK; } @@ -1733,7 +1774,7 @@ float DxEngine::GetScaling() const noexcept return D2D1::ColorF(rgb, aFloat); } default: - THROW_HR(E_NOTIMPL); + FAIL_FAST_HR(E_NOTIMPL); } } diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index bc25588d3f1..fb3087840fa 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -45,7 +45,7 @@ namespace Microsoft::Console::Render void SetCallback(std::function pfn); - ::Microsoft::WRL::ComPtr GetSwapChain() noexcept; + ::Microsoft::WRL::ComPtr GetSwapChain(); // IRenderEngine Members [[nodiscard]] HRESULT Invalidate(const SMALL_RECT* const psrRegion) noexcept override; @@ -84,7 +84,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override; - [[nodiscard]] SMALL_RECT GetDirtyRectInChars() noexcept override; + [[nodiscard]] SMALL_RECT GetDirtyRectInChars() override; [[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override; [[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override; @@ -133,7 +133,7 @@ namespace Microsoft::Console::Render void _InvalidOr(SMALL_RECT sr) noexcept; void _InvalidOr(RECT rc) noexcept; - void _InvalidOffset(POINT pt) noexcept; + void _InvalidOffset(POINT pt); bool _presentReady; RECT _presentDirty; diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index 241a0021657..de3f6e6d3a0 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -14,7 +14,7 @@ namespace CodepointWidth width; }; - static bool operator<(const UnicodeRange& range, const unsigned int searchTerm) + static bool operator<(const UnicodeRange& range, const unsigned int searchTerm) noexcept { return range.upperBound < searchTerm; } @@ -316,7 +316,7 @@ namespace // - glyph - the utf16 encoded codepoint to search for // Return Value: // - the width type of the codepoint -CodepointWidth CodepointWidthDetector::GetWidth(const std::wstring_view glyph) const noexcept +CodepointWidth CodepointWidthDetector::GetWidth(const std::wstring_view glyph) const { if (glyph.empty()) { diff --git a/src/types/GlyphWidth.cpp b/src/types/GlyphWidth.cpp index b92935c31b6..8d60d507e2d 100644 --- a/src/types/GlyphWidth.cpp +++ b/src/types/GlyphWidth.cpp @@ -18,7 +18,7 @@ bool IsGlyphFullWidth(const std::wstring_view glyph) // Function Description: // - determines if the glyph represented by the single character should be // wide or not. See CodepointWidthDetector::IsWide -bool IsGlyphFullWidth(const wchar_t wch) +bool IsGlyphFullWidth(const wchar_t wch) noexcept { return widthDetector.IsWide(wch); } @@ -44,7 +44,7 @@ void SetGlyphWidthFallback(std::function pfnFallb // - // Return Value: // - -void NotifyGlyphWidthFontChanged() +void NotifyGlyphWidthFontChanged() noexcept { widthDetector.NotifyFontChanged(); } diff --git a/src/types/KeyEvent.cpp b/src/types/KeyEvent.cpp index c62a35c908b..869d302d5a4 100644 --- a/src/types/KeyEvent.cpp +++ b/src/types/KeyEvent.cpp @@ -68,7 +68,7 @@ void KeyEvent::ActivateModifierKey(const ModifierKeyState modifierKey) noexcept WI_SetAllFlags(_activeModifierKeys, bitFlag); } -bool KeyEvent::DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const noexcept +bool KeyEvent::DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const { DWORD consoleBits = 0; for (const ModifierKeyState& mod : consoleModifiers) diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index c06919d23f7..3842bc58192 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -10,7 +10,7 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing; // A helper function to create a SafeArray Version of an int array of a specified length -SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int length) +SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int length) noexcept { SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, length); if (psa != nullptr) @@ -133,7 +133,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, // Implementation of IRawElementProviderSimple::get_ProviderOptions. // Gets UI Automation provider options. -IFACEMETHODIMP ScreenInfoUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); @@ -169,7 +169,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patt // Implementation of IRawElementProviderSimple::get_PropertyValue. // Gets custom properties. IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyId, - _Out_ VARIANT* pVariant) + _Out_ VARIANT* pVariant) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetPropertyValue, nullptr); @@ -239,7 +239,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPropertyValue(_In_ PROPERTYID prope return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetHostRawElementProvider, nullptr); @@ -252,7 +252,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr #pragma region IRawElementProviderFragment -IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); @@ -270,7 +270,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ return S_OK; } -IFACEMETHODIMP ScreenInfoUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetEmbeddedFragmentRoots, nullptr); @@ -298,7 +298,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ //ApiMsgGetSelection apiMsg; _LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _UnlockConsole(); }); @@ -417,7 +417,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben //Tracing::s_TraceUia(this, ApiCall::GetVisibleRanges, nullptr); _LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _UnlockConsole(); }); @@ -573,7 +573,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_m return hr; } -IFACEMETHODIMP ScreenInfoUiaProviderBase::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) +IFACEMETHODIMP ScreenInfoUiaProviderBase::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr); @@ -589,12 +589,12 @@ const COORD ScreenInfoUiaProviderBase::_getScreenBufferCoords() const return _getTextBuffer().GetSize().Dimensions(); } -const TextBuffer& ScreenInfoUiaProviderBase::_getTextBuffer() const +const TextBuffer& ScreenInfoUiaProviderBase::_getTextBuffer() const noexcept { return _pData->GetTextBuffer(); } -const Viewport ScreenInfoUiaProviderBase::_getViewport() const +const Viewport ScreenInfoUiaProviderBase::_getViewport() const noexcept { return _pData->GetViewport(); } diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h index f859ec67b6f..3f7f63ad2ee 100644 --- a/src/types/ScreenInfoUiaProviderBase.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -51,19 +51,19 @@ namespace Microsoft::Console::Types _COM_Outptr_result_maybenull_ void** ppInterface) override; // IRawElementProviderSimple methods - IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) override; + IFACEMETHODIMP get_ProviderOptions(_Out_ ProviderOptions* pOptions) noexcept override; IFACEMETHODIMP GetPatternProvider(_In_ PATTERNID iid, _COM_Outptr_result_maybenull_ IUnknown** ppInterface) override; IFACEMETHODIMP GetPropertyValue(_In_ PROPERTYID idProp, - _Out_ VARIANT* pVariant) override; - IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) override; + _Out_ VARIANT* pVariant) noexcept override; + IFACEMETHODIMP get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) noexcept override; // IRawElementProviderFragment methods virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; - IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) override; + IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) noexcept override; virtual IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) = 0; - IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) override; + IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) noexcept override; IFACEMETHODIMP SetFocus() override; virtual IFACEMETHODIMP get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) = 0; @@ -75,7 +75,7 @@ namespace Microsoft::Console::Types IFACEMETHODIMP RangeFromPoint(_In_ UiaPoint point, _COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; IFACEMETHODIMP get_DocumentRange(_COM_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; - IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) override; + IFACEMETHODIMP get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) noexcept override; protected: virtual std::deque GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider) = 0; @@ -118,8 +118,8 @@ namespace Microsoft::Console::Types std::map _signalFiringMapping; const COORD _getScreenBufferCoords() const; - const TextBuffer& _getTextBuffer() const; - const Viewport _getViewport() const; + const TextBuffer& _getTextBuffer() const noexcept; + const Viewport _getViewport() const noexcept; void _LockConsole() noexcept; void _UnlockConsole() noexcept; }; diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 903751e8f67..e7bc49b823c 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -47,7 +47,7 @@ UiaTextRangeBase::MoveState::MoveState(const ScreenInfoRow startScreenInfoRow, const Column firstColumnInRow, const Column lastColumnInRow, const MovementIncrement increment, - const MovementDirection direction) : + const MovementDirection direction) noexcept : StartScreenInfoRow{ startScreenInfoRow }, StartColumn{ startColumn }, EndScreenInfoRow{ endScreenInfoRow }, @@ -184,7 +184,7 @@ void UiaTextRangeBase::Initialize(_In_ const UiaPoint point) _degenerate = true; } -UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) : +UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) noexcept: _cRefs{ 1 }, _pProvider{ a._pProvider }, _start{ a._start }, @@ -201,17 +201,17 @@ UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) : #endif } -const IdType UiaTextRangeBase::GetId() const +const IdType UiaTextRangeBase::GetId() const noexcept { return _id; } -const Endpoint UiaTextRangeBase::GetStart() const +const Endpoint UiaTextRangeBase::GetStart() const noexcept { return _start; } -const Endpoint UiaTextRangeBase::GetEnd() const +const Endpoint UiaTextRangeBase::GetEnd() const noexcept { return _end; } @@ -222,12 +222,12 @@ const Endpoint UiaTextRangeBase::GetEnd() const // - // Return Value: // - true if range is degenerate, false otherwise. -const bool UiaTextRangeBase::IsDegenerate() const +const bool UiaTextRangeBase::IsDegenerate() const noexcept { return _degenerate; } -void UiaTextRangeBase::SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) +void UiaTextRangeBase::SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) noexcept { _start = start; _end = end; @@ -288,10 +288,10 @@ IFACEMETHODIMP UiaTextRangeBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_re #pragma region ITextRangeProvider -IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) +IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) noexcept { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept{ _pData->UnlockConsole(); }); @@ -317,7 +317,7 @@ IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _O IFACEMETHODIMP UiaTextRangeBase::CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, _In_ ITextRangeProvider* pTargetRange, _In_ TextPatternRangeEndpoint targetEndpoint, - _Out_ int* pRetVal) + _Out_ int* pRetVal) noexcept { RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; @@ -369,7 +369,7 @@ IFACEMETHODIMP UiaTextRangeBase::CompareEndpoints(_In_ TextPatternRangeEndpoint IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -415,7 +415,7 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) IFACEMETHODIMP UiaTextRangeBase::FindAttribute(_In_ TEXTATTRIBUTEID /*textAttributeId*/, _In_ VARIANT /*val*/, _In_ BOOL /*searchBackward*/, - _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/) + _Outptr_result_maybenull_ ITextRangeProvider** /*ppRetVal*/) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::FindAttribute, nullptr); @@ -423,7 +423,7 @@ IFACEMETHODIMP UiaTextRangeBase::FindAttribute(_In_ TEXTATTRIBUTEID /*textAttrib } IFACEMETHODIMP UiaTextRangeBase::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, - _Out_ VARIANT* pRetVal) + _Out_ VARIANT* pRetVal) noexcept { RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); @@ -445,7 +445,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetAttributeValue(_In_ TEXTATTRIBUTEID textAttr IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -516,7 +516,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetEnclosingElement(_Outptr_result_maybenull_ I IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -619,7 +619,7 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit, _Out_ int* pRetVal) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -691,7 +691,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin _Out_ int* pRetVal) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -758,7 +758,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi _In_ TextPatternRangeEndpoint targetEndpoint) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -866,7 +866,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi IFACEMETHODIMP UiaTextRangeBase::Select() { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -895,7 +895,7 @@ IFACEMETHODIMP UiaTextRangeBase::Select() } // we don't support this -IFACEMETHODIMP UiaTextRangeBase::AddToSelection() +IFACEMETHODIMP UiaTextRangeBase::AddToSelection() noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::AddToSelection, nullptr); @@ -903,7 +903,7 @@ IFACEMETHODIMP UiaTextRangeBase::AddToSelection() } // we don't support this -IFACEMETHODIMP UiaTextRangeBase::RemoveFromSelection() +IFACEMETHODIMP UiaTextRangeBase::RemoveFromSelection() noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RemoveFromSelection, nullptr); @@ -913,7 +913,7 @@ IFACEMETHODIMP UiaTextRangeBase::RemoveFromSelection() IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&] { + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); }); @@ -997,7 +997,7 @@ IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) return S_OK; } -IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) +IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) noexcept { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetChildren, nullptr); @@ -1037,7 +1037,7 @@ const COORD UiaTextRangeBase::_getScreenFontSize() const // - // Return Value: // - The number of rows -const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) noexcept { return pData->GetTextBuffer().TotalRowCount(); } @@ -1109,7 +1109,7 @@ const unsigned int UiaTextRangeBase::_rowCountInRange(IUiaData* pData) const // Return Value: // - the equivalent ScreenInfoRow. const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row) + const TextBufferRow row) noexcept { const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row - firstRowIndex); @@ -1122,7 +1122,7 @@ const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pD // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) +const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) noexcept { const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); @@ -1136,7 +1136,7 @@ const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, // Return Value: // - the equivalent ViewportRow. const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) + const SMALL_RECT viewport) noexcept { return row - viewport.Top; } @@ -1149,7 +1149,7 @@ const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfo // - the non-normalized row index // Return Value: // - the normalized row index -const Row UiaTextRangeBase::_normalizeRow(IUiaData* pData, const Row row) +const Row UiaTextRangeBase::_normalizeRow(IUiaData* pData, const Row row) noexcept { const unsigned int totalRows = _getTotalRows(pData); return ((row + totalRows) % totalRows); @@ -1161,7 +1161,7 @@ const Row UiaTextRangeBase::_normalizeRow(IUiaData* pData, const Row row) // - viewport - The viewport to measure // Return Value: // - The viewport height -const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewport) +const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewport) noexcept { FAIL_FAST_IF(!(viewport.Bottom >= viewport.Top)); // + 1 because COORD is inclusive on both sides so subtracting top @@ -1175,7 +1175,7 @@ const unsigned int UiaTextRangeBase::_getViewportHeight(const SMALL_RECT viewpor // - viewport - The viewport to measure // Return Value: // - The viewport width -const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport) +const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport) noexcept { FAIL_FAST_IF(!(viewport.Right >= viewport.Left)); @@ -1192,8 +1192,8 @@ const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport // Return Value: // - true if the row is within the bounds of the viewport const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row) -{ + const ScreenInfoRow row) noexcept +{ return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); } @@ -1205,7 +1205,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, // Return Value: // - true if the row is within the bounds of the viewport const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, - const SMALL_RECT viewport) + const SMALL_RECT viewport) noexcept { const ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && @@ -1219,7 +1219,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, // Return Value: // - the equivalent TextBufferRow. const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row) + const ScreenInfoRow row) noexcept { const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row + firstRowIndex); @@ -1326,7 +1326,7 @@ void UiaTextRangeBase::_addScreenInfoRowBoundaries(IUiaData* pData, // - // Return Value: // - the index of the first row (0-indexed) of the screen info -const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() +const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() noexcept { return 0; } @@ -1337,7 +1337,7 @@ const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() // - // Return Value: // - the index of the last row (0-indexed) of the screen info -const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(IUiaData* pData) noexcept { return _getTotalRows(pData) - 1; } @@ -1348,7 +1348,7 @@ const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(IUiaData* pData) // - // Return Value: // - the index of the first column (0-indexed) of the screen info rows -const Column UiaTextRangeBase::_getFirstColumnIndex() +const Column UiaTextRangeBase::_getFirstColumnIndex() noexcept { return 0; } diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 336adbeca2a..239ba1b5469 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -129,7 +129,7 @@ namespace Microsoft::Console::Types const Column firstColumnInRow, const Column lastColumnInRow, const MovementIncrement increment, - const MovementDirection direction); + const MovementDirection direction) noexcept; #ifdef UNIT_TESTING friend class ::UiaTextRangeTests; @@ -139,14 +139,14 @@ namespace Microsoft::Console::Types public: virtual ~UiaTextRangeBase() = default; - const IdType GetId() const; - const Endpoint GetStart() const; - const Endpoint GetEnd() const; - const bool IsDegenerate() const; + const IdType GetId() const noexcept; + const Endpoint GetStart() const noexcept; + const Endpoint GetEnd() const noexcept; + const bool IsDegenerate() const noexcept; // TODO GitHub #605: // only used for UiaData::FindText. Remove after Search added properly - void SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate); + void SetRangeValues(const Endpoint start, const Endpoint end, const bool isDegenerate) noexcept; // IUnknown methods IFACEMETHODIMP_(ULONG) @@ -158,22 +158,22 @@ namespace Microsoft::Console::Types // ITextRangeProvider methods virtual IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; - IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) override; + IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) noexcept override; IFACEMETHODIMP CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint, _In_ ITextRangeProvider* pTargetRange, _In_ TextPatternRangeEndpoint targetEndpoint, - _Out_ int* pRetVal) override; + _Out_ int* pRetVal) noexcept override; IFACEMETHODIMP ExpandToEnclosingUnit(_In_ TextUnit unit) override; IFACEMETHODIMP FindAttribute(_In_ TEXTATTRIBUTEID textAttributeId, _In_ VARIANT val, _In_ BOOL searchBackward, - _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) override; + _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) noexcept override; virtual IFACEMETHODIMP FindText(_In_ BSTR text, _In_ BOOL searchBackward, _In_ BOOL ignoreCase, _Outptr_result_maybenull_ ITextRangeProvider** ppRetVal) = 0; IFACEMETHODIMP GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId, - _Out_ VARIANT* pRetVal) override; + _Out_ VARIANT* pRetVal) noexcept override; IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; IFACEMETHODIMP GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal) override; IFACEMETHODIMP GetText(_In_ int maxLength, @@ -189,10 +189,10 @@ namespace Microsoft::Console::Types _In_ ITextRangeProvider* pTargetRange, _In_ TextPatternRangeEndpoint targetEndpoint) override; IFACEMETHODIMP Select() override; - IFACEMETHODIMP AddToSelection() override; - IFACEMETHODIMP RemoveFromSelection() override; + IFACEMETHODIMP AddToSelection() noexcept override; + IFACEMETHODIMP RemoveFromSelection() noexcept override; IFACEMETHODIMP ScrollIntoView(_In_ BOOL alignToTop) override; - IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) override; + IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal) noexcept override; protected: #if _DEBUG @@ -225,7 +225,7 @@ namespace Microsoft::Console::Types void Initialize(_In_ const UiaPoint point); - UiaTextRangeBase(const UiaTextRangeBase& a); + UiaTextRangeBase(const UiaTextRangeBase& a) noexcept; // used to debug objects passed back and forth // between the provider and the client @@ -262,13 +262,13 @@ namespace Microsoft::Console::Types static const COORD _getScreenBufferCoords(IUiaData* pData); virtual const COORD _getScreenFontSize() const; - static const unsigned int _getTotalRows(IUiaData* pData); + static const unsigned int _getTotalRows(IUiaData* pData) noexcept; static const unsigned int _getRowWidth(IUiaData* pData); - static const unsigned int _getFirstScreenInfoRowIndex(); - static const unsigned int _getLastScreenInfoRowIndex(IUiaData* pData); + static const unsigned int _getFirstScreenInfoRowIndex() noexcept; + static const unsigned int _getLastScreenInfoRowIndex(IUiaData* pData) noexcept; - static const Column _getFirstColumnIndex(); + static const Column _getFirstColumnIndex() noexcept; static const Column _getLastColumnIndex(IUiaData* pData); const unsigned int _rowCountInRange(IUiaData* pData) const; @@ -276,10 +276,10 @@ namespace Microsoft::Console::Types static const TextBufferRow _endpointToTextBufferRow(IUiaData* pData, const Endpoint endpoint); static const ScreenInfoRow _textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row); + const TextBufferRow row) noexcept; static const TextBufferRow _screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row); + const ScreenInfoRow row) noexcept; static const Endpoint _textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row); static const ScreenInfoRow _endpointToScreenInfoRow(IUiaData* pData, @@ -295,20 +295,20 @@ namespace Microsoft::Console::Types static const Column _endpointToColumn(IUiaData* pData, const Endpoint endpoint); - static const Row _normalizeRow(IUiaData* pData, const Row row); + static const Row _normalizeRow(IUiaData* pData, const Row row) noexcept; static const ViewportRow _screenInfoRowToViewportRow(IUiaData* pData, - const ScreenInfoRow row); + const ScreenInfoRow row) noexcept; static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport); + const SMALL_RECT viewport) noexcept; static const bool _isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row); + const ScreenInfoRow row) noexcept; static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row, - const SMALL_RECT viewport); + const SMALL_RECT viewport) noexcept; - static const unsigned int _getViewportHeight(const SMALL_RECT viewport); - static const unsigned int _getViewportWidth(const SMALL_RECT viewport); + static const unsigned int _getViewportHeight(const SMALL_RECT viewport) noexcept; + static const unsigned int _getViewportWidth(const SMALL_RECT viewport) noexcept; void _addScreenInfoRowBoundaries(IUiaData* pData, const ScreenInfoRow screenInfoRow, diff --git a/src/types/Utf16Parser.cpp b/src/types/Utf16Parser.cpp index 2090af62312..777388c6406 100644 --- a/src/types/Utf16Parser.cpp +++ b/src/types/Utf16Parser.cpp @@ -14,7 +14,7 @@ // - wstr - The UTF-16 string to parse. // Return Value: // - A view into the string given of just the next codepoint unit. -std::wstring_view Utf16Parser::ParseNext(std::wstring_view wstr) +std::wstring_view Utf16Parser::ParseNext(std::wstring_view wstr) noexcept { for (size_t pos = 0; pos < wstr.size(); ++pos) { diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 6967ac942bc..22d9605c381 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -29,7 +29,7 @@ class CodepointWidthDetector final ~CodepointWidthDetector() = default; CodepointWidthDetector& operator=(const CodepointWidthDetector&) = delete; - CodepointWidth GetWidth(const std::wstring_view glyph) const noexcept; + CodepointWidth GetWidth(const std::wstring_view glyph) const; bool IsWide(const std::wstring_view glyph) const; bool IsWide(const wchar_t wch) const noexcept; void SetFallbackMethod(std::function pfnFallback); diff --git a/src/types/inc/GlyphWidth.hpp b/src/types/inc/GlyphWidth.hpp index e12261a6f83..7888915bebd 100644 --- a/src/types/inc/GlyphWidth.hpp +++ b/src/types/inc/GlyphWidth.hpp @@ -13,6 +13,6 @@ Module Name: #include bool IsGlyphFullWidth(const std::wstring_view glyph); -bool IsGlyphFullWidth(const wchar_t wch); +bool IsGlyphFullWidth(const wchar_t wch) noexcept; void SetGlyphWidthFallback(std::function pfnFallback); -void NotifyGlyphWidthFontChanged(); +void NotifyGlyphWidthFontChanged() noexcept; diff --git a/src/types/inc/IInputEvent.hpp b/src/types/inc/IInputEvent.hpp index ded7c903b7b..486971f82e6 100644 --- a/src/types/inc/IInputEvent.hpp +++ b/src/types/inc/IInputEvent.hpp @@ -245,7 +245,7 @@ class KeyEvent : public IInputEvent void SetActiveModifierKeys(const DWORD activeModifierKeys) noexcept; void DeactivateModifierKey(const ModifierKeyState modifierKey) noexcept; void ActivateModifierKey(const ModifierKeyState modifierKey) noexcept; - bool DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const noexcept; + bool DoActiveModifierKeysMatch(const std::unordered_set& consoleModifiers) const; bool IsCommandLineEditingKey() const noexcept; bool IsPopupKey() const noexcept; diff --git a/src/types/inc/Utf16Parser.hpp b/src/types/inc/Utf16Parser.hpp index d093b3396ea..f48bdc0cda2 100644 --- a/src/types/inc/Utf16Parser.hpp +++ b/src/types/inc/Utf16Parser.hpp @@ -28,7 +28,7 @@ class Utf16Parser final public: static std::vector> Parse(std::wstring_view wstr); - static std::wstring_view ParseNext(std::wstring_view wstr); + static std::wstring_view ParseNext(std::wstring_view wstr) noexcept; // Routine Description: // - checks if wchar is a utf16 leading surrogate diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index 646dfe10ed4..a44df865067 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -15,7 +15,7 @@ namespace Microsoft::Console::Utils { bool IsValidHandle(const HANDLE handle) noexcept; - short ClampToShortMax(const long value, const short min); + short ClampToShortMax(const long value, const short min) noexcept; std::wstring GuidToString(const GUID guid); GUID GuidFromString(const std::wstring wstr); @@ -28,7 +28,7 @@ namespace Microsoft::Console::Utils void InitializeCampbellColorTableForConhost(gsl::span& table); void SwapANSIColorOrderForConhost(gsl::span& table); void Initialize256ColorTable(gsl::span& table); - void SetColorTableAlpha(gsl::span& table, const BYTE newAlpha); + void SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) noexcept; constexpr uint16_t EndianSwap(uint16_t value) { diff --git a/src/types/inc/viewport.hpp b/src/types/inc/viewport.hpp index d9d8d5c2439..25f5b9d7f66 100644 --- a/src/types/inc/viewport.hpp +++ b/src/types/inc/viewport.hpp @@ -59,7 +59,7 @@ namespace Microsoft::Console::Types bool IsInBounds(const COORD& pos) const noexcept; void Clamp(COORD& pos) const; - Viewport Clamp(const Viewport& other) const; + Viewport Clamp(const Viewport& other) const noexcept; bool MoveInBounds(const ptrdiff_t move, COORD& pos) const noexcept; bool IncrementInBounds(COORD& pos) const noexcept; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 710dd74df3c..eb9ecabe3a4 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -13,7 +13,7 @@ using namespace Microsoft::Console; // - min: the minimum value to clamp to // Return Value: // - The clamped value as a short. -short Utils::ClampToShortMax(const long value, const short min) +short Utils::ClampToShortMax(const long value, const short min) noexcept { return static_cast(std::clamp(value, static_cast(min), @@ -454,7 +454,7 @@ void Utils::Initialize256ColorTable(gsl::span& table) // - newAlpha: the new value to use as the alpha for all the entries in that table. // Return Value: // - -void Utils::SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) +void Utils::SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) noexcept { const auto shiftedAlpha = newAlpha << 24; for (auto& color : table) diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index cb088c0519a..f0afc75bec5 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -194,7 +194,7 @@ void Viewport::Clamp(COORD& pos) const // - other - Viewport to clamp to the inside of this viewport // Return Value: // - Clamped viewport -Viewport Viewport::Clamp(const Viewport& other) const +Viewport Viewport::Clamp(const Viewport& other) const noexcept { auto clampMe = other.ToInclusive(); From 7ec6bfc01c521861d8d23c5c94a9c20758f7510e Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 29 Aug 2019 17:31:53 -0700 Subject: [PATCH 089/154] catch failure to open clipboard (#2590) --- src/cascadia/TerminalApp/App.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 439a6367907..d8720d21bbf 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -1547,8 +1547,12 @@ namespace winrt::TerminalApp::implementation dataPack.SetHtmlFormat(htmlData); } - Clipboard::SetContent(dataPack); - Clipboard::Flush(); + try + { + Clipboard::SetContent(dataPack); + Clipboard::Flush(); + } + CATCH_LOG(); }); } From feb5b18296801ef47cc75dbb8d5957d4d114af8e Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Thu, 29 Aug 2019 17:32:27 -0700 Subject: [PATCH 090/154] doc: move cascadia specs and rename them to spec format (#2593) --- .../#1142 - Keybinding Arguments.md} | 0 .../Panes.md => specs/#532 - Panes and Split Windows.md} | 4 +++- .../#754 - Cascading Default Settings.md} | 0 3 files changed, 3 insertions(+), 1 deletion(-) rename doc/{cascadia/Keybindings-Arguments.md => specs/#1142 - Keybinding Arguments.md} (100%) rename doc/{cascadia/Panes.md => specs/#532 - Panes and Split Windows.md} (97%) rename doc/{cascadia/Cascading-Default-Settings.md => specs/#754 - Cascading Default Settings.md} (100%) diff --git a/doc/cascadia/Keybindings-Arguments.md b/doc/specs/#1142 - Keybinding Arguments.md similarity index 100% rename from doc/cascadia/Keybindings-Arguments.md rename to doc/specs/#1142 - Keybinding Arguments.md diff --git a/doc/cascadia/Panes.md b/doc/specs/#532 - Panes and Split Windows.md similarity index 97% rename from doc/cascadia/Panes.md rename to doc/specs/#532 - Panes and Split Windows.md index e165c208ab4..afbd31e24fa 100644 --- a/doc/cascadia/Panes.md +++ b/doc/specs/#532 - Panes and Split Windows.md @@ -1,6 +1,8 @@ --- author: "Mike Griese @zadjii-msft" -created on: 2019-May-16 +created on: 2019-05-16 +last updated: 2019-07-07 +issue id: 523 --- # Panes in the Windows Terminal diff --git a/doc/cascadia/Cascading-Default-Settings.md b/doc/specs/#754 - Cascading Default Settings.md similarity index 100% rename from doc/cascadia/Cascading-Default-Settings.md rename to doc/specs/#754 - Cascading Default Settings.md From 30e8e7f3a34e86567be960063dddb1ff278a0c82 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 08:46:24 -0700 Subject: [PATCH 091/154] C26429, symbols not tested for nullness. --- src/buffer/out/textBuffer.cpp | 8 +-- src/renderer/dx/CustomTextLayout.cpp | 17 +++++ src/renderer/dx/CustomTextRenderer.cpp | 88 +++++++++++++++---------- src/renderer/dx/CustomTextRenderer.h | 16 ++--- src/renderer/dx/DxRenderer.cpp | 14 ++++ src/types/ScreenInfoUiaProviderBase.cpp | 41 ++++++++++-- src/types/UiaTextRangeBase.cpp | 30 ++++++--- src/types/UiaTextRangeBase.hpp | 10 +-- src/types/WindowUiaProviderBase.cpp | 9 +++ 9 files changed, 167 insertions(+), 66 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 9103f5664ec..bf8a3b3c94b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -584,9 +584,9 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View // Search the given viewport by starting at the bottom. coordEndOfText.Y = viewport.BottomInclusive(); - const ROW* pCurrRow = &GetRowByOffset(coordEndOfText.Y); + const auto& currRow = GetRowByOffset(coordEndOfText.Y); // The X position of the end of the valid text is the Right draw boundary (which is one beyond the final valid character) - coordEndOfText.X = gsl::narrow(pCurrRow->GetCharRow().MeasureRight()) - 1; + coordEndOfText.X = gsl::narrow(currRow.GetCharRow().MeasureRight()) - 1; // If the X coordinate turns out to be -1, the row was empty, we need to search backwards for the real end of text. const auto viewportTop = viewport.Top(); @@ -594,10 +594,10 @@ COORD TextBuffer::GetLastNonSpaceCharacter(const Microsoft::Console::Types::View while (fDoBackUp) { coordEndOfText.Y--; - pCurrRow = &GetRowByOffset(coordEndOfText.Y); + const auto& backupRow = GetRowByOffset(coordEndOfText.Y); // We need to back up to the previous row if this line is empty, AND there are more rows - coordEndOfText.X = gsl::narrow(pCurrRow->GetCharRow().MeasureRight()) - 1; + coordEndOfText.X = gsl::narrow(backupRow.GetCharRow().MeasureRight()) - 1; fDoBackUp = (coordEndOfText.X < 0 && coordEndOfText.Y > viewportTop); } diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 8d7d290ae3b..f3de0eeacd3 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -38,6 +38,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _runIndex{ 0 }, _width{ width } { + THROW_HR_IF_NULL(E_INVALIDARG, format); + // Fetch the locale name out once now from the format _localeName.resize(gsl::narrow_cast(format->GetLocaleNameLength()) + 1); // +1 for null THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow(_localeName.size()))); @@ -58,6 +60,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // - S_OK or suitable DirectX/DirectWrite/Direct2D result code. [[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::GetColumns(_Out_ UINT32* columns) { + RETURN_HR_IF_NULL(E_INVALIDARG, columns); *columns = 0; RETURN_IF_FAILED(_AnalyzeRuns()); @@ -467,6 +470,8 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, IDWriteTextRenderer* renderer, const D2D_POINT_2F origin) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, renderer); + try { // We're going to start from the origin given and walk to the right for each @@ -561,6 +566,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _Outptr_result_buffer_(*textLength) WCHAR const** textString, _Out_ UINT32* textLength) { + RETURN_HR_IF_NULL(E_INVALIDARG, textString); + RETURN_HR_IF_NULL(E_INVALIDARG, textLength); + *textString = nullptr; *textLength = 0; @@ -587,6 +595,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _Outptr_result_buffer_(*textLength) WCHAR const** textString, _Out_ UINT32* textLength) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, textString); + RETURN_HR_IF_NULL(E_INVALIDARG, textLength); + *textString = nullptr; *textLength = 0; @@ -624,6 +635,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _Out_ UINT32* textLength, _Outptr_result_z_ WCHAR const** localeName) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, textLength); + RETURN_HR_IF_NULL(E_INVALIDARG, localeName); + *localeName = _localeName.data(); *textLength = gsl::narrow(_text.size()) - textPosition; @@ -643,6 +657,9 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _Out_ UINT32* textLength, _COM_Outptr_ IDWriteNumberSubstitution** numberSubstitution) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, textLength); + RETURN_HR_IF_NULL(E_INVALIDARG, numberSubstitution); + *numberSubstitution = nullptr; *textLength = gsl::narrow(_text.size()) - textPosition; diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index bdcab5164a1..f47a0a2020f 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -23,6 +23,8 @@ using namespace Microsoft::Console::Render; [[nodiscard]] HRESULT CustomTextRenderer::IsPixelSnappingDisabled(void* /*clientDrawingContext*/, _Out_ BOOL* isDisabled) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, isDisabled); + *isDisabled = false; return S_OK; } @@ -40,7 +42,10 @@ using namespace Microsoft::Console::Render; [[nodiscard]] HRESULT CustomTextRenderer::GetPixelsPerDip(void* clientDrawingContext, _Out_ FLOAT* pixelsPerDip) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pixelsPerDip); + DrawingContext* drawingContext = static_cast(clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); float dpiX, dpiY; drawingContext->renderTarget->GetDpi(&dpiX, &dpiY); @@ -60,7 +65,10 @@ using namespace Microsoft::Console::Render; [[nodiscard]] HRESULT CustomTextRenderer::GetCurrentTransform(void* clientDrawingContext, DWRITE_MATRIX* transform) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, transform); + DrawingContext* drawingContext = static_cast(clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); // Matrix structures are defined identically drawingContext->renderTarget->GetTransform(reinterpret_cast(transform)); @@ -90,15 +98,14 @@ using namespace Microsoft::Console::Render; _In_ const DWRITE_UNDERLINE* underline, IUnknown* clientDrawingEffect) noexcept { - _FillRectangle(clientDrawingContext, - clientDrawingEffect, - baselineOriginX, - baselineOriginY + underline->offset, - underline->width, - underline->thickness, - underline->readingDirection, - underline->flowDirection); - return S_OK; + return _FillRectangle(clientDrawingContext, + clientDrawingEffect, + baselineOriginX, + baselineOriginY + underline->offset, + underline->width, + underline->thickness, + underline->readingDirection, + underline->flowDirection); } // Routine Description: @@ -122,15 +129,14 @@ using namespace Microsoft::Console::Render; _In_ const DWRITE_STRIKETHROUGH* strikethrough, IUnknown* clientDrawingEffect) noexcept { - _FillRectangle(clientDrawingContext, - clientDrawingEffect, - baselineOriginX, - baselineOriginY + strikethrough->offset, - strikethrough->width, - strikethrough->thickness, - strikethrough->readingDirection, - strikethrough->flowDirection); - return S_OK; + return _FillRectangle(clientDrawingContext, + clientDrawingEffect, + baselineOriginX, + baselineOriginY + strikethrough->offset, + strikethrough->width, + strikethrough->thickness, + strikethrough->readingDirection, + strikethrough->flowDirection); } // Routine Description: @@ -146,16 +152,17 @@ using namespace Microsoft::Console::Render; // - flowDirection - textual flow information that could affect the rectangle // Return Value: // - S_OK -void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, - IUnknown* clientDrawingEffect, - float x, - float y, - float width, - float thickness, - DWRITE_READING_DIRECTION /*readingDirection*/, - DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept +[[nodiscard]] HRESULT CustomTextRenderer::_FillRectangle(void* clientDrawingContext, + IUnknown* clientDrawingEffect, + float x, + float y, + float width, + float thickness, + DWRITE_READING_DIRECTION /*readingDirection*/, + DWRITE_FLOW_DIRECTION /*flowDirection*/) noexcept { DrawingContext* drawingContext = static_cast(clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); // Get brush ID2D1Brush* brush = drawingContext->foregroundBrush; @@ -191,6 +198,8 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, BOOL isRightToLeft, IUnknown* clientDrawingEffect) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, inlineObject); + return inlineObject->Draw(clientDrawingContext, this, originX, @@ -254,7 +263,7 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, { rect.right += advance; } - + d2dContext->FillRectangle(rect, drawingContext->backgroundBrush); // Now go onto drawing the text. @@ -284,13 +293,13 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, // there are, glyphRunEnumerator can be used to iterate through them. ::Microsoft::WRL::ComPtr glyphRunEnumerator; const HRESULT hr = dwriteFactory4->TranslateColorGlyphRun(baselineOrigin, - glyphRun, - glyphRunDescription, - supportedFormats, - measuringMode, - nullptr, - 0, - &glyphRunEnumerator); + glyphRun, + glyphRunDescription, + supportedFormats, + measuringMode, + nullptr, + 0, + &glyphRunEnumerator); // If the analysis found no color glyphs in the run, just draw normally. if (hr == DWRITE_E_NOCOLOR) @@ -414,6 +423,11 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, ID2D1Brush* brush) { + RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun); + RETURN_HR_IF_NULL(E_INVALIDARG, glyphRunDescription); + RETURN_HR_IF_NULL(E_INVALIDARG, brush); + ::Microsoft::WRL::ComPtr d2dContext; RETURN_IF_FAILED(clientDrawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf())); @@ -435,6 +449,9 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, _In_ const DWRITE_GLYPH_RUN* glyphRun, _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun); + // This is regular text but manually ::Microsoft::WRL::ComPtr d2dFactory; clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf()); @@ -475,6 +492,9 @@ void CustomTextRenderer::_FillRectangle(void* clientDrawingContext, _In_ const DWRITE_GLYPH_RUN* glyphRun, _In_ const DWRITE_GLYPH_RUN_DESCRIPTION* /*glyphRunDescription*/) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, clientDrawingContext); + RETURN_HR_IF_NULL(E_INVALIDARG, glyphRun); + // This is glow text manually ::Microsoft::WRL::ComPtr d2dFactory; clientDrawingContext->renderTarget->GetFactory(d2dFactory.GetAddressOf()); diff --git a/src/renderer/dx/CustomTextRenderer.h b/src/renderer/dx/CustomTextRenderer.h index 8c38e4bf77f..c3e2fc5219b 100644 --- a/src/renderer/dx/CustomTextRenderer.h +++ b/src/renderer/dx/CustomTextRenderer.h @@ -81,14 +81,14 @@ namespace Microsoft::Console::Render IUnknown* clientDrawingEffect) noexcept override; private: - void _FillRectangle(void* clientDrawingContext, - IUnknown* clientDrawingEffect, - float x, - float y, - float width, - float thickness, - DWRITE_READING_DIRECTION readingDirection, - DWRITE_FLOW_DIRECTION flowDirection) noexcept; + [[nodiscard]] HRESULT _FillRectangle(void* clientDrawingContext, + IUnknown* clientDrawingEffect, + float x, + float y, + float width, + float thickness, + DWRITE_READING_DIRECTION readingDirection, + DWRITE_FLOW_DIRECTION flowDirection) noexcept; [[nodiscard]] HRESULT _DrawBasicGlyphRun(DrawingContext* clientDrawingContext, D2D1_POINT_2F baselineOrigin, diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 9b13f1a08eb..fd3bd8c925f 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -444,6 +444,8 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() // - S_OK [[nodiscard]] HRESULT DxEngine::Invalidate(const SMALL_RECT* const psrRegion) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, psrRegion); + _InvalidOr(*psrRegion); return S_OK; } @@ -456,6 +458,8 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() // - S_OK [[nodiscard]] HRESULT DxEngine::InvalidateCursor(const COORD* const pcoordCursor) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pcoordCursor); + const SMALL_RECT sr = Microsoft::Console::Types::Viewport::FromCoord(*pcoordCursor).ToInclusive(); return Invalidate(&sr); } @@ -468,6 +472,8 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() // - S_OK [[nodiscard]] HRESULT DxEngine::InvalidateSystem(const RECT* const prcDirtyClient) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, prcDirtyClient); + _InvalidOr(*prcDirtyClient); return S_OK; @@ -564,6 +570,8 @@ Microsoft::WRL::ComPtr DxEngine::GetSwapChain() // - S_FALSE because we don't use this. [[nodiscard]] HRESULT DxEngine::InvalidateCircling(_Out_ bool* const pForcePaint) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pForcePaint); + *pForcePaint = false; return S_FALSE; } @@ -709,6 +717,8 @@ void DxEngine::_InvalidOr(RECT rc) noexcept // - S_FALSE because this is unused. [[nodiscard]] HRESULT DxEngine::PrepareForTeardown(_Out_ bool* const pForcePaint) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pForcePaint); + *pForcePaint = false; return S_FALSE; } @@ -1382,6 +1392,8 @@ float DxEngine::GetScaling() const noexcept // - S_OK or relevant DirectWrite error. [[nodiscard]] HRESULT DxEngine::IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pResult); + try { const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout. @@ -1532,6 +1544,8 @@ float DxEngine::GetScaling() const noexcept [[nodiscard]] std::wstring DxEngine::_GetFontFamilyName(IDWriteFontFamily* const fontFamily, std::wstring& localeName) const { + THROW_HR_IF_NULL(E_INVALIDARG, fontFamily); + // See: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontcollection Microsoft::WRL::ComPtr familyNames; THROW_IF_FAILED(fontFamily->GetFamilyNames(&familyNames)); diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 3842bc58192..2a8994ce2a9 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -98,6 +98,8 @@ ScreenInfoUiaProviderBase::Release() IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); if (riid == __uuidof(IUnknown)) @@ -135,6 +137,8 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, // Gets UI Automation provider options. IFACEMETHODIMP ScreenInfoUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pOptions); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetProviderOptions, nullptr); *pOptions = ProviderOptions_ServerSideProvider; @@ -302,7 +306,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ _UnlockConsole(); }); - RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal); *ppRetVal = nullptr; HRESULT hr = S_OK; @@ -324,6 +328,14 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ IRawElementProviderSimple* pProvider; hr = this->QueryInterface(IID_PPV_ARGS(&pProvider)); + if (SUCCEEDED(hr)) + { + if (pProvider == nullptr) + { + hr = E_POINTER; + } + } + if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); @@ -365,6 +377,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ std::deque ranges; IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(QueryInterface(IID_PPV_ARGS(&pProvider))); + RETURN_HR_IF_NULL(E_POINTER, pProvider); try { ranges = GetSelectionRanges(pProvider); @@ -399,7 +412,10 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ { UiaTextRangeBase* pRange = ranges.at(0); ranges.pop_front(); - pRange->Release(); + if (pRange) + { + pRange->Release(); + } } return hr; } @@ -421,7 +437,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben _UnlockConsole(); }); - RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal); *ppRetVal = nullptr; const auto viewport = _getViewport(); @@ -446,6 +462,14 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben IRawElementProviderSimple* pProvider; HRESULT hr = this->QueryInterface(IID_PPV_ARGS(&pProvider)); + if (SUCCEEDED(hr)) + { + if (pProvider == nullptr) + { + hr = E_POINTER; + } + } + if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); @@ -493,11 +517,12 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromChild(_In_ IRawElementProvide // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RangeFromChild, nullptr); - RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal); *ppRetVal = nullptr; IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); + RETURN_HR_IF_NULL(E_POINTER, pProvider); HRESULT hr = S_OK; try @@ -520,11 +545,12 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point, // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::RangeFromPoint, nullptr); - RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal); *ppRetVal = nullptr; IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); + RETURN_HR_IF_NULL(E_POINTER, pProvider); HRESULT hr = S_OK; try @@ -547,11 +573,12 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_m // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetDocumentRange, nullptr); - RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); + RETURN_HR_IF_NULL(E_INVALIDARG, ppRetVal); *ppRetVal = nullptr; IRawElementProviderSimple* pProvider; RETURN_IF_FAILED(this->QueryInterface(IID_PPV_ARGS(&pProvider))); + RETURN_HR_IF_NULL(E_POINTER, pProvider); HRESULT hr = S_OK; try @@ -575,6 +602,8 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_m IFACEMETHODIMP ScreenInfoUiaProviderBase::get_SupportedTextSelection(_Out_ SupportedTextSelection* pRetVal) noexcept { + RETURN_HR_IF_NULL(E_INVALIDARG, pRetVal); + // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetSupportedTextSelection, nullptr); diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index e7bc49b823c..c29d8956b1f 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -1017,6 +1017,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY const COORD UiaTextRangeBase::_getScreenBufferCoords(IUiaData* pData) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); return pData->GetTextBuffer().GetSize().Dimensions(); } @@ -1037,8 +1038,9 @@ const COORD UiaTextRangeBase::_getScreenFontSize() const // - // Return Value: // - The number of rows -const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) noexcept +const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); return pData->GetTextBuffer().TotalRowCount(); } @@ -1109,8 +1111,9 @@ const unsigned int UiaTextRangeBase::_rowCountInRange(IUiaData* pData) const // Return Value: // - the equivalent ScreenInfoRow. const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row) noexcept + const TextBufferRow row) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row - firstRowIndex); } @@ -1122,8 +1125,9 @@ const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pD // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) noexcept +const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); } @@ -1192,8 +1196,9 @@ const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport // Return Value: // - true if the row is within the bounds of the viewport const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row) noexcept -{ + const ScreenInfoRow row) +{ + THROW_HR_IF_NULL(E_INVALIDARG, pData); return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); } @@ -1219,8 +1224,9 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, // Return Value: // - the equivalent TextBufferRow. const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row) noexcept + const ScreenInfoRow row) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row + firstRowIndex); } @@ -1444,6 +1450,8 @@ std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData const MoveState moveState, _Out_ int* const pAmountMoved) { + THROW_HR_IF_NULL(E_INVALIDARG, pData); + THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; @@ -1490,7 +1498,8 @@ std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaDat const MoveState moveState, _Out_ int* const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); + THROW_HR_IF_NULL(E_INVALIDARG, pData); + THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; @@ -1643,7 +1652,8 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, const MoveState moveState, _Out_ int* const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); + THROW_HR_IF_NULL(E_INVALIDARG, pData); + THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow; @@ -1733,7 +1743,8 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, const MoveState moveState, _Out_ int* const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); + THROW_HR_IF_NULL(E_INVALIDARG, pData); + THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow; @@ -2073,6 +2084,7 @@ RECT UiaTextRangeBase::_getTerminalRect() const IRawElementProviderFragment* pRawElementProviderFragment; THROW_IF_FAILED(_pProvider->QueryInterface(&pRawElementProviderFragment)); + THROW_HR_IF_NULL(E_POINTER, pRawElementProviderFragment); pRawElementProviderFragment->get_BoundingRectangle(&result); return { diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 239ba1b5469..257e576fe22 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -262,7 +262,7 @@ namespace Microsoft::Console::Types static const COORD _getScreenBufferCoords(IUiaData* pData); virtual const COORD _getScreenFontSize() const; - static const unsigned int _getTotalRows(IUiaData* pData) noexcept; + static const unsigned int _getTotalRows(IUiaData* pData); static const unsigned int _getRowWidth(IUiaData* pData); static const unsigned int _getFirstScreenInfoRowIndex() noexcept; @@ -276,10 +276,10 @@ namespace Microsoft::Console::Types static const TextBufferRow _endpointToTextBufferRow(IUiaData* pData, const Endpoint endpoint); static const ScreenInfoRow _textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row) noexcept; + const TextBufferRow row); static const TextBufferRow _screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row) noexcept; + const ScreenInfoRow row); static const Endpoint _textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row); static const ScreenInfoRow _endpointToScreenInfoRow(IUiaData* pData, @@ -298,12 +298,12 @@ namespace Microsoft::Console::Types static const Row _normalizeRow(IUiaData* pData, const Row row) noexcept; static const ViewportRow _screenInfoRowToViewportRow(IUiaData* pData, - const ScreenInfoRow row) noexcept; + const ScreenInfoRow row); static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, const SMALL_RECT viewport) noexcept; static const bool _isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row) noexcept; + const ScreenInfoRow row); static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row, const SMALL_RECT viewport) noexcept; diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index b98abc29184..3d159da8836 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -36,6 +36,7 @@ WindowUiaProviderBase::Release() IFACEMETHODIMP WindowUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface); if (riid == __uuidof(IUnknown)) { *ppInterface = static_cast(this); @@ -71,6 +72,7 @@ IFACEMETHODIMP WindowUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outp // Gets UI Automation provider options. IFACEMETHODIMP WindowUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* pOptions) { + RETURN_HR_IF_NULL(E_INVALIDARG, pOptions); RETURN_IF_FAILED(_EnsureValidHwnd()); *pOptions = ProviderOptions_ServerSideProvider; @@ -82,6 +84,7 @@ IFACEMETHODIMP WindowUiaProviderBase::get_ProviderOptions(_Out_ ProviderOptions* IFACEMETHODIMP WindowUiaProviderBase::GetPatternProvider(_In_ PATTERNID /*patternId*/, _COM_Outptr_result_maybenull_ IUnknown** ppInterface) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface); *ppInterface = nullptr; RETURN_IF_FAILED(_EnsureValidHwnd()); @@ -92,6 +95,7 @@ IFACEMETHODIMP WindowUiaProviderBase::GetPatternProvider(_In_ PATTERNID /*patter // Gets custom properties. IFACEMETHODIMP WindowUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyId, _Out_ VARIANT* pVariant) { + RETURN_HR_IF_NULL(E_INVALIDARG, pVariant); RETURN_IF_FAILED(_EnsureValidHwnd()); pVariant->vt = VT_EMPTY; @@ -148,6 +152,7 @@ IFACEMETHODIMP WindowUiaProviderBase::GetPropertyValue(_In_ PROPERTYID propertyI // supplies many properties. IFACEMETHODIMP WindowUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_result_maybenull_ IRawElementProviderSimple** ppProvider) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider); try { const HWND hwnd = GetWindowHandle(); @@ -164,6 +169,7 @@ IFACEMETHODIMP WindowUiaProviderBase::get_HostRawElementProvider(_COM_Outptr_res IFACEMETHODIMP WindowUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppRuntimeId); RETURN_IF_FAILED(_EnsureValidHwnd()); // Root defers this to host, others must implement it... *ppRuntimeId = nullptr; @@ -173,6 +179,7 @@ IFACEMETHODIMP WindowUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAF IFACEMETHODIMP WindowUiaProviderBase::get_BoundingRectangle(_Out_ UiaRect* pRect) { + RETURN_HR_IF_NULL(E_INVALIDARG, pRect); RETURN_IF_FAILED(_EnsureValidHwnd()); const IUiaWindow* const pConsoleWindow = _baseWindow; @@ -195,6 +202,7 @@ IFACEMETHODIMP WindowUiaProviderBase::get_BoundingRectangle(_Out_ UiaRect* pRect IFACEMETHODIMP WindowUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppRoots); RETURN_IF_FAILED(_EnsureValidHwnd()); *ppRoots = nullptr; @@ -203,6 +211,7 @@ IFACEMETHODIMP WindowUiaProviderBase::GetEmbeddedFragmentRoots(_Outptr_result_ma IFACEMETHODIMP WindowUiaProviderBase::get_FragmentRoot(_COM_Outptr_result_maybenull_ IRawElementProviderFragmentRoot** ppProvider) { + RETURN_HR_IF_NULL(E_INVALIDARG, ppProvider); RETURN_IF_FAILED(_EnsureValidHwnd()); *ppProvider = this; From cdfbf8f1064db75c23f417c20c20567c3054f0ad Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 08:53:54 -0700 Subject: [PATCH 092/154] C26474, don't use static_cast when an implicit cast is acceptable. --- src/types/ScreenInfoUiaProviderBase.cpp | 41 ++++++++++--------------- src/types/UiaTextRangeBase.cpp | 11 +++---- src/types/WindowUiaProviderBase.cpp | 21 ++++--------- 3 files changed, 26 insertions(+), 47 deletions(-) diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 2a8994ce2a9..99ed47b53a8 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -60,7 +60,7 @@ ScreenInfoUiaProviderBase::~ScreenInfoUiaProviderBase() } CATCH_RETURN(); - IRawElementProviderSimple* pProvider = static_cast(this); + IRawElementProviderSimple* pProvider = this; hr = UiaRaiseAutomationEvent(pProvider, id); _signalFiringMapping[id] = false; @@ -102,21 +102,12 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); - if (riid == __uuidof(IUnknown)) + if (riid == __uuidof(IUnknown) || + riid == __uuidof(IRawElementProviderSimple) || + riid == __uuidof(IRawElementProviderFragment) || + riid == __uuidof(ITextProvider)) { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderSimple)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderFragment)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(ITextProvider)) - { - *ppInterface = static_cast(this); + *ppInterface = this; } else { @@ -124,7 +115,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::QueryInterface(_In_ REFIID riid, return E_NOINTERFACE; } - (static_cast(*ppInterface))->AddRef(); + AddRef(); return S_OK; } @@ -161,7 +152,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetPatternProvider(_In_ PATTERNID patt if (patternId == UIA_TextPatternId) { - hr = this->QueryInterface(__uuidof(ITextProvider), reinterpret_cast(ppInterface)); + hr = this->QueryInterface(IID_PPV_ARGS(ppInterface)); if (FAILED(hr)) { *ppInterface = nullptr; @@ -354,7 +345,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ range = nullptr; hr = wil::ResultFromCaughtException(); } - (static_cast(pProvider))->Release(); + pProvider->Release(); if (range == nullptr) { SafeArrayDestroy(*ppRetVal); @@ -363,7 +354,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ } LONG currentIndex = 0; - hr = SafeArrayPutElement(*ppRetVal, ¤tIndex, reinterpret_cast(range)); + hr = SafeArrayPutElement(*ppRetVal, ¤tIndex, range); if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); @@ -403,7 +394,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ // fill the safe array for (LONG i = 0; i < gsl::narrow(ranges.size()); ++i) { - hr = SafeArrayPutElement(*ppRetVal, &i, reinterpret_cast(ranges.at(i))); + hr = SafeArrayPutElement(*ppRetVal, &i, ranges.at(i)); if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); @@ -490,7 +481,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben range = nullptr; hr = wil::ResultFromCaughtException(); } - (static_cast(pProvider))->Release(); + pProvider->Release(); if (range == nullptr) { @@ -500,7 +491,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben } LONG currentIndex = gsl::narrow(i); - hr = SafeArrayPutElement(*ppRetVal, ¤tIndex, reinterpret_cast(range)); + hr = SafeArrayPutElement(*ppRetVal, ¤tIndex, range); if (FAILED(hr)) { SafeArrayDestroy(*ppRetVal); @@ -534,7 +525,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromChild(_In_ IRawElementProvide *ppRetVal = nullptr; hr = wil::ResultFromCaughtException(); } - (static_cast(pProvider))->Release(); + pProvider->Release(); return hr; } @@ -563,7 +554,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::RangeFromPoint(_In_ UiaPoint point, *ppRetVal = nullptr; hr = wil::ResultFromCaughtException(); } - (static_cast(pProvider))->Release(); + pProvider->Release(); return hr; } @@ -590,7 +581,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_DocumentRange(_COM_Outptr_result_m *ppRetVal = nullptr; hr = wil::ResultFromCaughtException(); } - (static_cast(pProvider))->Release(); + pProvider->Release(); if (*ppRetVal) { diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index c29d8956b1f..c40ba607296 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -266,13 +266,10 @@ IFACEMETHODIMP UiaTextRangeBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_re // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::QueryInterface, nullptr); - if (riid == __uuidof(IUnknown)) + if (riid == __uuidof(IUnknown) || + riid == __uuidof(ITextRangeProvider)) { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(ITextRangeProvider)) - { - *ppInterface = static_cast(this); + *ppInterface = this; } else { @@ -280,7 +277,7 @@ IFACEMETHODIMP UiaTextRangeBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_re return E_NOINTERFACE; } - (static_cast(*ppInterface))->AddRef(); + AddRef(); return S_OK; } diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index 3d159da8836..ec8add2c256 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -37,21 +37,12 @@ WindowUiaProviderBase::Release() IFACEMETHODIMP WindowUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) { RETURN_HR_IF_NULL(E_INVALIDARG, ppInterface); - if (riid == __uuidof(IUnknown)) + if (riid == __uuidof(IUnknown) || + riid == __uuidof(IRawElementProviderSimple) || + riid == __uuidof(IRawElementProviderFragment) || + riid == __uuidof(IRawElementProviderFragmentRoot)) { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderSimple)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderFragment)) - { - *ppInterface = static_cast(this); - } - else if (riid == __uuidof(IRawElementProviderFragmentRoot)) - { - *ppInterface = static_cast(this); + *ppInterface = this; } else { @@ -59,7 +50,7 @@ IFACEMETHODIMP WindowUiaProviderBase::QueryInterface(_In_ REFIID riid, _COM_Outp return E_NOINTERFACE; } - (static_cast(*ppInterface))->AddRef(); + AddRef(); return S_OK; } From 230e7f43e0f0b1298ec9cfd1c513f7f7b35e5a9b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 09:15:49 -0700 Subject: [PATCH 093/154] C26466, disable dynamic_cast rule because we're not RTTI due to OS policy. Also reinstitute C6001 and C6011 because they're not actually a part of the 'core checks' and they're goodness we had before I turned them off at the beginning of this series. --- src/StaticAnalysis.ruleset | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/StaticAnalysis.ruleset b/src/StaticAnalysis.ruleset index c1f52c15f69..114c0cc5c2f 100644 --- a/src/StaticAnalysis.ruleset +++ b/src/StaticAnalysis.ruleset @@ -3,4 +3,13 @@ + + + + + + + + + From 7d4096bbbfdefda1219c8d746340c787141ac91e Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 09:40:31 -0700 Subject: [PATCH 094/154] C26485, refactor to avoid array-to-pointer decay. --- src/renderer/dx/DxRenderer.cpp | 21 ++++++++++----------- src/types/ScreenInfoUiaProviderBase.cpp | 18 ++++++++++-------- src/types/utils.cpp | 6 +++--- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index fd3bd8c925f..c45b988e21d 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -147,13 +147,12 @@ DxEngine::~DxEngine() // D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED; - const D3D_FEATURE_LEVEL FeatureLevels[] = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_1, - }; + std::array FeatureLevels; + FeatureLevels.at(0) = D3D_FEATURE_LEVEL_11_1; + FeatureLevels.at(1) = D3D_FEATURE_LEVEL_11_0; + FeatureLevels.at(2) = D3D_FEATURE_LEVEL_10_1; + FeatureLevels.at(3) = D3D_FEATURE_LEVEL_10_0; + FeatureLevels.at(4) = D3D_FEATURE_LEVEL_9_1; // Trying hardware first for maximum performance, then trying WARP (software) renderer second // in case we're running inside a downlevel VM where hardware passthrough isn't enabled like @@ -162,8 +161,8 @@ DxEngine::~DxEngine() D3D_DRIVER_TYPE_HARDWARE, nullptr, DeviceFlags, - FeatureLevels, - ARRAYSIZE(FeatureLevels), + FeatureLevels.data(), + gsl::narrow(FeatureLevels.size()), D3D11_SDK_VERSION, &_d3dDevice, nullptr, @@ -175,8 +174,8 @@ DxEngine::~DxEngine() D3D_DRIVER_TYPE_WARP, nullptr, DeviceFlags, - FeatureLevels, - ARRAYSIZE(FeatureLevels), + FeatureLevels.data(), + gsl::narrow(FeatureLevels.size()), D3D11_SDK_VERSION, &_d3dDevice, nullptr, diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 99ed47b53a8..6750cc34cd0 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -10,16 +10,14 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing; // A helper function to create a SafeArray Version of an int array of a specified length -SAFEARRAY* BuildIntSafeArray(_In_reads_(length) const int* const data, const int length) noexcept +SAFEARRAY* BuildIntSafeArray(std::basic_string_view data) noexcept { - SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, length); + SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, gsl::narrow(data.size())); if (psa != nullptr) { - const auto dataSpan = gsl::make_span(data, length); - - for (long i = 0; i < length; i++) + for (long i = 0; i < data.size(); i++) { - if (FAILED(SafeArrayPutElement(psa, &i, (void*)&(dataSpan.at(i))))) + if (FAILED(SafeArrayPutElement(psa, &i, (void*)&(data.at(i))))) { SafeArrayDestroy(psa); psa = nullptr; @@ -257,9 +255,13 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ *ppRuntimeId = nullptr; // AppendRuntimeId is a magic Number that tells UIAutomation to Append its own Runtime ID(From the HWND) - const int rId[] = { UiaAppendRuntimeId, -1 }; + std::array rId; + rId.at(0) = UiaAppendRuntimeId; + rId.at(1) = -1; + + const auto span = std::basic_string_view(rId.data(), rId.size()); // BuildIntSafeArray is a custom function to hide the SafeArray creation - *ppRuntimeId = BuildIntSafeArray(rId, 2); + *ppRuntimeId = BuildIntSafeArray(span); RETURN_IF_NULL_ALLOC(*ppRuntimeId); return S_OK; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index eb9ecabe3a4..6b8237fff41 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -29,12 +29,12 @@ short Utils::ClampToShortMax(const long value, const short min) noexcept // - a string representation of the GUID. On failure, throws E_INVALIDARG. std::wstring Utils::GuidToString(const GUID guid) { - wchar_t guid_cstr[39]; - const int written = swprintf(guid_cstr, sizeof(guid_cstr), L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + std::array guid_cstr; + const int written = swprintf(guid_cstr.data(), guid_cstr.size(), L"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); THROW_HR_IF(E_INVALIDARG, written == -1); - return std::wstring(guid_cstr); + return std::wstring(guid_cstr.data(), guid_cstr.size()); } // Method Description: From 81ab5803aa5548f66dc900f6756f85483f1af80c Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 09:44:19 -0700 Subject: [PATCH 095/154] C26473, do not cast pointer back to the same type. --- src/buffer/out/CharRow.cpp | 2 +- src/buffer/out/Row.cpp | 4 ++-- src/buffer/out/textBuffer.cpp | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/buffer/out/CharRow.cpp b/src/buffer/out/CharRow.cpp index 8173e72ad25..f10fbfe13b7 100644 --- a/src/buffer/out/CharRow.cpp +++ b/src/buffer/out/CharRow.cpp @@ -209,7 +209,7 @@ const DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) const // Note: will throw exception if column is out of bounds DbcsAttribute& CharRow::DbcsAttrAt(const size_t column) { - return const_cast(static_cast(this)->DbcsAttrAt(column)); + return _data.at(column).DbcsAttr(); } // Routine Description: diff --git a/src/buffer/out/Row.cpp b/src/buffer/out/Row.cpp index a46df44ceb5..4172374af6d 100644 --- a/src/buffer/out/Row.cpp +++ b/src/buffer/out/Row.cpp @@ -37,7 +37,7 @@ const CharRow& ROW::GetCharRow() const noexcept CharRow& ROW::GetCharRow() noexcept { - return const_cast(static_cast(this)->GetCharRow()); + return _charRow; } const ATTR_ROW& ROW::GetAttrRow() const noexcept @@ -47,7 +47,7 @@ const ATTR_ROW& ROW::GetAttrRow() const noexcept ATTR_ROW& ROW::GetAttrRow() noexcept { - return const_cast(static_cast(this)->GetAttrRow()); + return _attrRow; } SHORT ROW::GetId() const noexcept diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index bf8a3b3c94b..2533a72559b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -90,7 +90,11 @@ const ROW& TextBuffer::GetRowByOffset(const size_t index) const // - reference to the requested row. Asserts if out of bounds. ROW& TextBuffer::GetRowByOffset(const size_t index) { - return const_cast(static_cast(this)->GetRowByOffset(index)); + const size_t totalRows = TotalRowCount(); + + // Rows are stored circularly, so the index you ask for is offset by the start position and mod the total of rows. + const size_t offsetIndex = (_firstRow + index) % totalRows; + return _storage.at(offsetIndex); } // Routine Description: From d5d7cf420dbc183921de676c4a663f47439a2c60 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 10:02:18 -0700 Subject: [PATCH 096/154] C26494, uninitalized local variables --- src/renderer/dx/CustomTextRenderer.cpp | 2 +- src/types/UiaTextRangeBase.cpp | 260 +++++++++++-------------- src/types/viewport.cpp | 2 +- 3 files changed, 113 insertions(+), 151 deletions(-) diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index f47a0a2020f..70b41e72769 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -366,7 +366,7 @@ using namespace Microsoft::Console::Render; // This run is solid-color outlines, either from non-color // glyphs or from COLR glyph layers. Use Direct2D to draw them. - ID2D1Brush* layerBrush; + ID2D1Brush* layerBrush = nullptr; // The rule is "if 0xffff, use current brush." See: // https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_2/ns-dwrite_2-dwrite_color_glyph_run if (colorRun->paletteIndex == 0xFFFF) diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index c40ba607296..d01cde5fd15 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -162,7 +162,7 @@ void UiaTextRangeBase::Initialize(_In_ const UiaPoint point) // get row that point resides in const RECT windowRect = _getTerminalRect(); const SMALL_RECT viewport = _pData->GetViewport().ToInclusive(); - ScreenInfoRow row; + ScreenInfoRow row = 0; if (clientPoint.y <= windowRect.top) { row = viewport.Top; @@ -184,7 +184,7 @@ void UiaTextRangeBase::Initialize(_In_ const UiaPoint point) _degenerate = true; } -UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) noexcept: +UiaTextRangeBase::UiaTextRangeBase(const UiaTextRangeBase& a) noexcept : _cRefs{ 1 }, _pProvider{ a._pProvider }, _start{ a._start }, @@ -288,9 +288,9 @@ IFACEMETHODIMP UiaTextRangeBase::QueryInterface(_In_ REFIID riid, _COM_Outptr_re IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal) noexcept { _pData->LockConsole(); - auto Unlock = wil::scope_exit([&]() noexcept{ + auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = FALSE; @@ -327,26 +327,10 @@ IFACEMETHODIMP UiaTextRangeBase::CompareEndpoints(_In_ TextPatternRangeEndpoint } // get endpoint value that we're comparing to - Endpoint theirValue; - if (targetEndpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) - { - theirValue = range->GetStart(); - } - else - { - theirValue = range->GetEnd() + 1; - } + const Endpoint theirValue = targetEndpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start ? range->GetStart() : range->GetEnd() + 1; // get the values of our endpoint - Endpoint ourValue; - if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) - { - ourValue = _start; - } - else - { - ourValue = _end + 1; - } + const Endpoint ourValue = endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start ? _start : _end + 1; // compare them *pRetVal = std::clamp(static_cast(ourValue) - static_cast(theirValue), -1, 1); @@ -368,7 +352,7 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); ApiMsgExpandToEnclosingUnit apiMsg; apiMsg.Unit = unit; @@ -444,7 +428,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); *ppRetVal = nullptr; @@ -481,7 +465,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ { return E_OUTOFMEMORY; } - HRESULT hr; + HRESULT hr = E_UNEXPECTED; for (LONG i = 0; i < gsl::narrow(coords.size()); ++i) { hr = SafeArrayPutElement(*ppRetVal, &i, &coords.at(i)); @@ -515,7 +499,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = nullptr; @@ -549,7 +533,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal OutputDebugString(ss.str().c_str()); #endif - ScreenInfoRow currentScreenInfoRow; + ScreenInfoRow currentScreenInfoRow = 0; for (unsigned int i = 0; i < totalRowsInRange; ++i) { currentScreenInfoRow = startScreenInfoRow + i; @@ -618,7 +602,7 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit, _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; @@ -690,7 +674,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; @@ -757,7 +741,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) @@ -786,7 +770,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi #endif // get the value that we're updating to - Endpoint targetEndpointValue; + Endpoint targetEndpointValue = 0; if (targetEndpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { targetEndpointValue = range->GetStart(); @@ -813,47 +797,41 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi } } - // convert then endpoints to screen info rows/columns - ScreenInfoRow startScreenInfoRow; - Column startColumn; - ScreenInfoRow endScreenInfoRow; - Column endColumn; - ScreenInfoRow targetScreenInfoRow; - Column targetColumn; try { - startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); - startColumn = _endpointToColumn(_pData, _start); - endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); - endColumn = _endpointToColumn(_pData, _end); - targetScreenInfoRow = _endpointToScreenInfoRow(_pData, targetEndpointValue); - targetColumn = _endpointToColumn(_pData, targetEndpointValue); - } - CATCH_RETURN(); + // convert then endpoints to screen info rows/columns + const auto startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); + const auto startColumn = _endpointToColumn(_pData, _start); + const auto endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); + const auto endColumn = _endpointToColumn(_pData, _end); + const auto targetScreenInfoRow = _endpointToScreenInfoRow(_pData, targetEndpointValue); + const auto targetColumn = _endpointToColumn(_pData, targetEndpointValue); - // set endpoint value and check for crossed endpoints - bool crossedEndpoints = false; - if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) - { - _start = targetEndpointValue; - if (_compareScreenCoords(_pData, endScreenInfoRow, endColumn, targetScreenInfoRow, targetColumn) == -1) + // set endpoint value and check for crossed endpoints + bool crossedEndpoints = false; + if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { - // endpoints were crossed - _end = _start; - crossedEndpoints = true; + _start = targetEndpointValue; + if (_compareScreenCoords(_pData, endScreenInfoRow, endColumn, targetScreenInfoRow, targetColumn) == -1) + { + // endpoints were crossed + _end = _start; + crossedEndpoints = true; + } } - } - else - { - _end = targetEndpointValue; - if (_compareScreenCoords(_pData, startScreenInfoRow, startColumn, targetScreenInfoRow, targetColumn) == 1) + else { - // endpoints were crossed - _start = _end; - crossedEndpoints = true; + _end = targetEndpointValue; + if (_compareScreenCoords(_pData, startScreenInfoRow, startColumn, targetScreenInfoRow, targetColumn) == 1) + { + // endpoints were crossed + _start = _end; + crossedEndpoints = true; + } } + _degenerate = crossedEndpoints; } - _degenerate = crossedEndpoints; + CATCH_RETURN(); // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::MoveEndpointByRange, &apiMsg); @@ -865,7 +843,7 @@ IFACEMETHODIMP UiaTextRangeBase::Select() _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); if (_degenerate) { @@ -874,14 +852,10 @@ IFACEMETHODIMP UiaTextRangeBase::Select() } else { - COORD coordStart; - COORD coordEnd; - - coordStart.X = gsl::narrow(_endpointToColumn(_pData, _start)); - coordStart.Y = gsl::narrow(_endpointToScreenInfoRow(_pData, _start)); - - coordEnd.X = gsl::narrow(_endpointToColumn(_pData, _end)); - coordEnd.Y = gsl::narrow(_endpointToScreenInfoRow(_pData, _end)); + const COORD coordStart{ gsl::narrow(_endpointToColumn(_pData, _start)), + gsl::narrow(_endpointToScreenInfoRow(_pData, _start)) }; + const COORD coordEnd{ gsl::narrow(_endpointToColumn(_pData, _end)), + gsl::narrow(_endpointToScreenInfoRow(_pData, _end)) }; _pData->SelectNewRegion(coordStart, coordEnd); } @@ -912,85 +886,73 @@ IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); - - SMALL_RECT oldViewport; - unsigned int viewportHeight; - // range rows - ScreenInfoRow startScreenInfoRow; - ScreenInfoRow endScreenInfoRow; - // screen buffer rows - ScreenInfoRow topRow; - ScreenInfoRow bottomRow; + }); + try { - oldViewport = _pData->GetViewport().ToInclusive(); - viewportHeight = _getViewportHeight(oldViewport); + + const auto oldViewport = _pData->GetViewport().ToInclusive(); + const auto viewportHeight = _getViewportHeight(oldViewport); // range rows - startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); - endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); + const auto startScreenInfoRow = _endpointToScreenInfoRow(_pData, _start); + const auto endScreenInfoRow = _endpointToScreenInfoRow(_pData, _end); // screen buffer rows - topRow = _getFirstScreenInfoRowIndex(); - bottomRow = _getLastScreenInfoRowIndex(_pData); - } - CATCH_RETURN(); + const auto topRow = _getFirstScreenInfoRowIndex(); + const auto bottomRow = _getLastScreenInfoRowIndex(_pData); - SMALL_RECT newViewport = oldViewport; + SMALL_RECT newViewport = oldViewport; - // there's a bunch of +1/-1s here for setting the viewport. These - // are to account for the inclusivity of the viewport boundaries. - if (alignToTop) - { - // determine if we can align the start row to the top - if (startScreenInfoRow + viewportHeight <= bottomRow) - { - // we can align to the top - newViewport.Top = gsl::narrow(startScreenInfoRow); - newViewport.Bottom = gsl::narrow(startScreenInfoRow + viewportHeight - 1); - } - else + // there's a bunch of +1/-1s here for setting the viewport. These + // are to account for the inclusivity of the viewport boundaries. + if (alignToTop) { - // we can align to the top so we'll just move the viewport - // to the bottom of the screen buffer - newViewport.Bottom = gsl::narrow(bottomRow); - newViewport.Top = gsl::narrow(bottomRow - viewportHeight + 1); - } - } - else - { - // we need to align to the bottom - // check if we can align to the bottom - if (endScreenInfoRow >= viewportHeight) - { - // we can align to bottom - newViewport.Bottom = gsl::narrow(endScreenInfoRow); - newViewport.Top = gsl::narrow(endScreenInfoRow - viewportHeight + 1); + // determine if we can align the start row to the top + if (startScreenInfoRow + viewportHeight <= bottomRow) + { + // we can align to the top + newViewport.Top = gsl::narrow(startScreenInfoRow); + newViewport.Bottom = gsl::narrow(startScreenInfoRow + viewportHeight - 1); + } + else + { + // we can align to the top so we'll just move the viewport + // to the bottom of the screen buffer + newViewport.Bottom = gsl::narrow(bottomRow); + newViewport.Top = gsl::narrow(bottomRow - viewportHeight + 1); + } } else { - // we can't align to bottom so we'll move the viewport to - // the top of the screen buffer - newViewport.Top = gsl::narrow(topRow); - newViewport.Bottom = gsl::narrow(topRow + viewportHeight - 1); + // we need to align to the bottom + // check if we can align to the bottom + if (endScreenInfoRow >= viewportHeight) + { + // we can align to bottom + newViewport.Bottom = gsl::narrow(endScreenInfoRow); + newViewport.Top = gsl::narrow(endScreenInfoRow - viewportHeight + 1); + } + else + { + // we can't align to bottom so we'll move the viewport to + // the top of the screen buffer + newViewport.Top = gsl::narrow(topRow); + newViewport.Bottom = gsl::narrow(topRow + viewportHeight - 1); + } } - } - FAIL_FAST_IF(!(newViewport.Top >= gsl::narrow(topRow))); - FAIL_FAST_IF(!(newViewport.Bottom <= gsl::narrow(bottomRow))); - FAIL_FAST_IF(!(_getViewportHeight(oldViewport) == _getViewportHeight(newViewport))); + FAIL_FAST_IF(!(newViewport.Top >= gsl::narrow(topRow))); + FAIL_FAST_IF(!(newViewport.Bottom <= gsl::narrow(bottomRow))); + FAIL_FAST_IF(!(_getViewportHeight(oldViewport) == _getViewportHeight(newViewport))); - try - { _ChangeViewport(newViewport); + + // TODO GitHub #1914: Re-attach Tracing to UIA Tree + // tracing + /*ApiMsgScrollIntoView apiMsg; + apiMsg.AlignToTop = !!alignToTop; + Tracing::s_TraceUia(this, ApiCall::ScrollIntoView, &apiMsg);*/ } CATCH_RETURN(); - - // TODO GitHub #1914: Re-attach Tracing to UIA Tree - // tracing - /*ApiMsgScrollIntoView apiMsg; - apiMsg.AlignToTop = !!alignToTop; - Tracing::s_TraceUia(this, ApiCall::ScrollIntoView, &apiMsg);*/ - return S_OK; } @@ -1211,7 +1173,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, { const ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && - viewportRow < gsl::narrow(_getViewportHeight(viewport)); + viewportRow < gsl::narrow(_getViewportHeight(viewport)); } // Routine Description: @@ -1278,8 +1240,8 @@ void UiaTextRangeBase::_addScreenInfoRowBoundaries(IUiaData* pData, { const COORD currentFontSize = _getScreenFontSize(); - POINT topLeft; - POINT bottomRight; + POINT topLeft{ 0 }; + POINT bottomRight{ 0 }; if (_endpointToScreenInfoRow(pData, _start) == screenInfoRow) { @@ -1562,8 +1524,8 @@ std::pair UiaTextRangeBase::_moveByLine(IUiaData* pData, // we don't want to move the range if we're already in the // limiting row and trying to move off the end of the screen buffer const bool illegalMovement = (currentScreenInfoRow == moveState.LimitingRow && - ((moveCount < 0 && moveState.Increment == MovementIncrement::Backward) || - (moveCount > 0 && moveState.Increment == MovementIncrement::Forward))); + ((moveCount < 0 && moveState.Increment == MovementIncrement::Backward) || + (moveCount > 0 && moveState.Increment == MovementIncrement::Forward))); if (moveCount != 0 && !illegalMovement) { @@ -1653,8 +1615,8 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; - ScreenInfoRow currentScreenInfoRow; - Column currentColumn; + ScreenInfoRow currentScreenInfoRow = 0; + Column currentColumn = 0; // set current location vars if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) @@ -1744,8 +1706,8 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; - ScreenInfoRow currentScreenInfoRow; - Column currentColumn; + ScreenInfoRow currentScreenInfoRow = 0; + Column currentColumn = 0; // set current location vars if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) @@ -1845,8 +1807,8 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(I THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; - ScreenInfoRow currentScreenInfoRow; - Column currentColumn; + ScreenInfoRow currentScreenInfoRow = 0; + Column currentColumn = 0; bool forceDegenerate = false; Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; @@ -2005,8 +1967,8 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitDocume THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; - Endpoint start; - Endpoint end; + Endpoint start = 0; + Endpoint end = 0; bool degenerate = false; if (endpoint == TextPatternRangeEndpoint::TextPatternRangeEndpoint_Start) { diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index f0afc75bec5..c6772bfdcc6 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -450,7 +450,7 @@ bool Viewport::WalkInBoundsCircular(COORD& pos, const WalkDir dir) const noexcep // if using this same viewport with the `WalkInBounds` methods. COORD Viewport::GetWalkOrigin(const WalkDir dir) const noexcept { - COORD origin; + COORD origin{ 0 }; origin.X = dir.x == XWalk::LeftToRight ? Left() : RightInclusive(); origin.Y = dir.y == YWalk::TopToBottom ? Top() : BottomInclusive(); return origin; From bbdfdf91eb1a87591be58428fe503b6b266715eb Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 10:04:30 -0700 Subject: [PATCH 097/154] C26462, const local variables that are unchanged. --- src/renderer/dx/CustomTextRenderer.cpp | 4 ++-- src/types/UiaTextRangeBase.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index 70b41e72769..b2697abf9f2 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -44,7 +44,7 @@ using namespace Microsoft::Console::Render; { RETURN_HR_IF_NULL(E_INVALIDARG, pixelsPerDip); - DrawingContext* drawingContext = static_cast(clientDrawingContext); + const DrawingContext* drawingContext = static_cast(clientDrawingContext); RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); float dpiX, dpiY; @@ -67,7 +67,7 @@ using namespace Microsoft::Console::Render; { RETURN_HR_IF_NULL(E_INVALIDARG, transform); - DrawingContext* drawingContext = static_cast(clientDrawingContext); + const DrawingContext* drawingContext = static_cast(clientDrawingContext); RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); // Matrix structures are defined identically diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index d01cde5fd15..9fa00dd78c2 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -294,7 +294,7 @@ IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _O RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = FALSE; - UiaTextRangeBase* other = static_cast(pRange); + const UiaTextRangeBase* other = static_cast(pRange); if (other) { *pRetVal = !!(_start == other->GetStart() && @@ -320,7 +320,7 @@ IFACEMETHODIMP UiaTextRangeBase::CompareEndpoints(_In_ TextPatternRangeEndpoint *pRetVal = 0; // get the text range that we're comparing to - UiaTextRangeBase* range = static_cast(pTargetRange); + const UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) { return E_INVALIDARG; @@ -743,7 +743,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi _pData->UnlockConsole(); }); - UiaTextRangeBase* range = static_cast(pTargetRange); + const UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) { return E_INVALIDARG; From b180406b07710ba527e7218f8b77ffeee844039f Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 10:19:59 -0700 Subject: [PATCH 098/154] C26445, wstring_view byref may indicate a lifetime issue --- src/buffer/out/OutputCell.cpp | 2 +- src/buffer/out/OutputCellView.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/buffer/out/OutputCell.cpp b/src/buffer/out/OutputCell.cpp index 59dc1c1f8b2..bed14b3f1f4 100644 --- a/src/buffer/out/OutputCell.cpp +++ b/src/buffer/out/OutputCell.cpp @@ -112,6 +112,6 @@ void OutputCell::_setFromOutputCellView(const OutputCellView& cell) _textAttribute = cell.TextAttr(); _behavior = cell.TextAttrBehavior(); - const auto& view = cell.Chars(); + const auto view = cell.Chars(); _text = view; } diff --git a/src/buffer/out/OutputCellView.cpp b/src/buffer/out/OutputCellView.cpp index 37e46457d93..6380e1f7882 100644 --- a/src/buffer/out/OutputCellView.cpp +++ b/src/buffer/out/OutputCellView.cpp @@ -27,6 +27,8 @@ OutputCellView::OutputCellView(const std::wstring_view view, // - Returns reference to view over text data // Return Value: // - Reference to UTF-16 character data +// C26445 - suppressed to enable the `TextBufferTextIterator::operator->` method which needs a non-temporary memory location holding the wstring_view. +[[gsl::suppress(26445)]] const std::wstring_view& OutputCellView::Chars() const noexcept { return _view; From c956913a2857d3b2d0e51eaffc24a11588dedce1 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 10:30:06 -0700 Subject: [PATCH 099/154] C26497, use constexpr for functions that could be evaluated at compile time. --- src/renderer/dx/CustomTextLayout.cpp | 2 +- src/renderer/dx/CustomTextLayout.h | 2 +- src/renderer/dx/DxRenderer.cpp | 9 ++------- src/renderer/dx/DxRenderer.hpp | 2 +- src/types/UiaTextRangeBase.cpp | 4 ++-- src/types/UiaTextRangeBase.hpp | 2 +- src/types/inc/utils.hpp | 2 +- src/types/utils.cpp | 2 +- 8 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index f3de0eeacd3..83391f5dd3b 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -544,7 +544,7 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, // Return Value: // - An estimate of how many glyph spaces may be required in the shaping arrays // to hold the data from a string of the given length. -[[nodiscard]] UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept +[[nodiscard]] constexpr UINT32 CustomTextLayout::_EstimateGlyphCount(const UINT32 textLength) noexcept { // This formula is from https://docs.microsoft.com/en-us/windows/desktop/api/dwrite/nf-dwrite-idwritetextanalyzer-getglyphs // and is the recommended formula for estimating buffer size for glyph count. diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index 3363139b9c5..ee0bab63825 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -132,7 +132,7 @@ namespace Microsoft::Console::Render IDWriteTextRenderer* renderer, const D2D_POINT_2F origin) noexcept; - [[nodiscard]] static UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept; + [[nodiscard]] static constexpr UINT32 _EstimateGlyphCount(const UINT32 textLength) noexcept; private: const ::Microsoft::WRL::ComPtr _factory; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index c45b988e21d..9d11a6159f4 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1797,12 +1797,7 @@ float DxEngine::GetScaling() const noexcept // - color - Direct2D Color F // Return Value: // - DXGI RGBA -[[nodiscard]] DXGI_RGBA DxEngine::s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept +[[nodiscard]] constexpr DXGI_RGBA DxEngine::s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept { - DXGI_RGBA rgba; - rgba.a = color.a; - rgba.b = color.b; - rgba.g = color.g; - rgba.r = color.r; - return rgba; + return { color.r, color.g, color.b, color.a }; } diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index fb3087840fa..c4a5260bf56 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -209,6 +209,6 @@ namespace Microsoft::Console::Render [[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept; - [[nodiscard]] static DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept; + [[nodiscard]] static constexpr DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept; }; } diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 9fa00dd78c2..c551846ee5b 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -1098,8 +1098,8 @@ const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, // - viewport - the viewport to use for the conversion // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) noexcept +constexpr const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, + const SMALL_RECT viewport) noexcept { return row - viewport.Top; } diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 257e576fe22..a502e135e4c 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -299,7 +299,7 @@ namespace Microsoft::Console::Types static const ViewportRow _screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row); - static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, + static constexpr const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, const SMALL_RECT viewport) noexcept; static const bool _isScreenInfoRowInViewport(IUiaData* pData, diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index a44df865067..6fee5e5205e 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -15,7 +15,7 @@ namespace Microsoft::Console::Utils { bool IsValidHandle(const HANDLE handle) noexcept; - short ClampToShortMax(const long value, const short min) noexcept; + constexpr short ClampToShortMax(const long value, const short min) noexcept; std::wstring GuidToString(const GUID guid); GUID GuidFromString(const std::wstring wstr); diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 6b8237fff41..034e46ec48a 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -13,7 +13,7 @@ using namespace Microsoft::Console; // - min: the minimum value to clamp to // Return Value: // - The clamped value as a short. -short Utils::ClampToShortMax(const long value, const short min) noexcept +constexpr short Utils::ClampToShortMax(const long value, const short min) noexcept { return static_cast(std::clamp(value, static_cast(min), From 594dca993b7486bb870ca78a9a96d368b09ed91b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 11:18:28 -0700 Subject: [PATCH 100/154] C26429, mark gsl::not_null on places where we don't test for null (shouldn't need to, internal methods only. --- src/types/UiaTextRangeBase.cpp | 113 ++++++++++++++------------------- src/types/UiaTextRangeBase.hpp | 88 ++++++++++++------------- 2 files changed, 92 insertions(+), 109 deletions(-) diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index c551846ee5b..1ac0ed5ca46 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -974,9 +974,8 @@ IFACEMETHODIMP UiaTextRangeBase::GetChildren(_Outptr_result_maybenull_ SAFEARRAY #pragma endregion -const COORD UiaTextRangeBase::_getScreenBufferCoords(IUiaData* pData) +const COORD UiaTextRangeBase::_getScreenBufferCoords(gsl::not_null pData) { - THROW_HR_IF_NULL(E_INVALIDARG, pData); return pData->GetTextBuffer().GetSize().Dimensions(); } @@ -997,9 +996,8 @@ const COORD UiaTextRangeBase::_getScreenFontSize() const // - // Return Value: // - The number of rows -const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getTotalRows(gsl::not_null pData) noexcept { - THROW_HR_IF_NULL(E_INVALIDARG, pData); return pData->GetTextBuffer().TotalRowCount(); } @@ -1009,7 +1007,7 @@ const unsigned int UiaTextRangeBase::_getTotalRows(IUiaData* pData) // - // Return Value: // - The row width -const unsigned int UiaTextRangeBase::_getRowWidth(IUiaData* pData) +const unsigned int UiaTextRangeBase::_getRowWidth(gsl::not_null pData) { // make sure that we can't leak a 0 return std::max(static_cast(_getScreenBufferCoords(pData).X), 1u); @@ -1021,7 +1019,7 @@ const unsigned int UiaTextRangeBase::_getRowWidth(IUiaData* pData) // - endpoint - the endpoint to translate // Return Value: // - the column value -const Column UiaTextRangeBase::_endpointToColumn(IUiaData* pData, const Endpoint endpoint) +const Column UiaTextRangeBase::_endpointToColumn(gsl::not_null pData, const Endpoint endpoint) { return endpoint % _getRowWidth(pData); } @@ -1032,7 +1030,7 @@ const Column UiaTextRangeBase::_endpointToColumn(IUiaData* pData, const Endpoint // - endpoint - the endpoint to convert // Return Value: // - the text buffer row value -const TextBufferRow UiaTextRangeBase::_endpointToTextBufferRow(IUiaData* pData, +const TextBufferRow UiaTextRangeBase::_endpointToTextBufferRow(gsl::not_null pData, const Endpoint endpoint) { return endpoint / _getRowWidth(pData); @@ -1045,7 +1043,7 @@ const TextBufferRow UiaTextRangeBase::_endpointToTextBufferRow(IUiaData* pData, // - // Return Value: // - The number of rows in the range. -const unsigned int UiaTextRangeBase::_rowCountInRange(IUiaData* pData) const +const unsigned int UiaTextRangeBase::_rowCountInRange(gsl::not_null pData) const { if (_degenerate) { @@ -1069,10 +1067,9 @@ const unsigned int UiaTextRangeBase::_rowCountInRange(IUiaData* pData) const // - row - the TextBufferRow to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row) +const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(gsl::not_null pData, + const TextBufferRow row) noexcept { - THROW_HR_IF_NULL(E_INVALIDARG, pData); const int firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row - firstRowIndex); } @@ -1084,9 +1081,8 @@ const ScreenInfoRow UiaTextRangeBase::_textBufferRowToScreenInfoRow(IUiaData* pD // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent ViewportRow. -const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(IUiaData* pData, const ScreenInfoRow row) +const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(gsl::not_null pData, const ScreenInfoRow row) noexcept { - THROW_HR_IF_NULL(E_INVALIDARG, pData); const SMALL_RECT viewport = pData->GetViewport().ToInclusive(); return _screenInfoRowToViewportRow(row, viewport); } @@ -1112,7 +1108,7 @@ constexpr const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const // - the non-normalized row index // Return Value: // - the normalized row index -const Row UiaTextRangeBase::_normalizeRow(IUiaData* pData, const Row row) noexcept +const Row UiaTextRangeBase::_normalizeRow(gsl::not_null pData, const Row row) noexcept { const unsigned int totalRows = _getTotalRows(pData); return ((row + totalRows) % totalRows); @@ -1154,10 +1150,9 @@ const unsigned int UiaTextRangeBase::_getViewportWidth(const SMALL_RECT viewport // - row - the screen info row to check // Return Value: // - true if the row is within the bounds of the viewport -const bool UiaTextRangeBase::_isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row) +const bool UiaTextRangeBase::_isScreenInfoRowInViewport(gsl::not_null pData, + const ScreenInfoRow row) noexcept { - THROW_HR_IF_NULL(E_INVALIDARG, pData); return _isScreenInfoRowInViewport(row, pData->GetViewport().ToInclusive()); } @@ -1182,10 +1177,9 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent TextBufferRow. -const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row) +const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(gsl::not_null pData, + const ScreenInfoRow row) noexcept { - THROW_HR_IF_NULL(E_INVALIDARG, pData); const TextBufferRow firstRowIndex = pData->GetTextBuffer().GetFirstRowIndex(); return _normalizeRow(pData, row + firstRowIndex); } @@ -1196,7 +1190,7 @@ const TextBufferRow UiaTextRangeBase::_screenInfoRowToTextBufferRow(IUiaData* pD // - row - the TextBufferRow to convert // Return Value: // - the equivalent Endpoint, starting at the beginning of the TextBufferRow. -const Endpoint UiaTextRangeBase::_textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row) +const Endpoint UiaTextRangeBase::_textBufferRowToEndpoint(gsl::not_null pData, const TextBufferRow row) { return _getRowWidth(pData) * row; } @@ -1207,7 +1201,7 @@ const Endpoint UiaTextRangeBase::_textBufferRowToEndpoint(IUiaData* pData, const // - row - the ScreenInfoRow to convert // Return Value: // - the equivalent Endpoint. -const Endpoint UiaTextRangeBase::_screenInfoRowToEndpoint(IUiaData* pData, +const Endpoint UiaTextRangeBase::_screenInfoRowToEndpoint(gsl::not_null pData, const ScreenInfoRow row) { return _textBufferRowToEndpoint(pData, _screenInfoRowToTextBufferRow(pData, row)); @@ -1219,7 +1213,7 @@ const Endpoint UiaTextRangeBase::_screenInfoRowToEndpoint(IUiaData* pData, // - endpoint - the endpoint to convert // Return Value: // - the equivalent ScreenInfoRow. -const ScreenInfoRow UiaTextRangeBase::_endpointToScreenInfoRow(IUiaData* pData, +const ScreenInfoRow UiaTextRangeBase::_endpointToScreenInfoRow(gsl::not_null pData, const Endpoint endpoint) { return _textBufferRowToScreenInfoRow(pData, _endpointToTextBufferRow(pData, endpoint)); @@ -1234,7 +1228,7 @@ const ScreenInfoRow UiaTextRangeBase::_endpointToScreenInfoRow(IUiaData* pData, // - // Notes: // - alters coords. may throw an exception. -void UiaTextRangeBase::_addScreenInfoRowBoundaries(IUiaData* pData, +void UiaTextRangeBase::_addScreenInfoRowBoundaries(gsl::not_null pData, const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const { @@ -1302,7 +1296,7 @@ const unsigned int UiaTextRangeBase::_getFirstScreenInfoRowIndex() noexcept // - // Return Value: // - the index of the last row (0-indexed) of the screen info -const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(IUiaData* pData) noexcept +const unsigned int UiaTextRangeBase::_getLastScreenInfoRowIndex(gsl::not_null pData) noexcept { return _getTotalRows(pData) - 1; } @@ -1324,7 +1318,7 @@ const Column UiaTextRangeBase::_getFirstColumnIndex() noexcept // - // Return Value: // - the index of the last column (0-indexed) of the screen info rows -const Column UiaTextRangeBase::_getLastColumnIndex(IUiaData* pData) +const Column UiaTextRangeBase::_getLastColumnIndex(gsl::not_null pData) { return _getRowWidth(pData) - 1; } @@ -1340,7 +1334,7 @@ const Column UiaTextRangeBase::_getLastColumnIndex(IUiaData* pData) // -1 if A < B // 1 if A > B // 0 if A == B -const int UiaTextRangeBase::_compareScreenCoords(IUiaData* pData, +const int UiaTextRangeBase::_compareScreenCoords(gsl::not_null pData, const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, @@ -1389,10 +1383,10 @@ const int UiaTextRangeBase::_compareScreenCoords(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRangeBase::_moveByCharacter(IUiaData* pData, +std::pair UiaTextRangeBase::_moveByCharacter(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { if (moveState.Direction == MovementDirection::Forward) { @@ -1404,13 +1398,11 @@ std::pair UiaTextRangeBase::_moveByCharacter(IUiaData* pData } } -std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData* pData, +std::pair UiaTextRangeBase::_moveByCharacterForward(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF_NULL(E_INVALIDARG, pData); - THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; @@ -1452,13 +1444,11 @@ std::pair UiaTextRangeBase::_moveByCharacterForward(IUiaData return std::make_pair(std::move(start), std::move(end)); } -std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaData* pData, +std::pair UiaTextRangeBase::_moveByCharacterBackward(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF_NULL(E_INVALIDARG, pData); - THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = moveState.StartScreenInfoRow; @@ -1511,12 +1501,11 @@ std::pair UiaTextRangeBase::_moveByCharacterBackward(IUiaDat // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRangeBase::_moveByLine(IUiaData* pData, +std::pair UiaTextRangeBase::_moveByLine(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; Endpoint start = _screenInfoRowToEndpoint(pData, moveState.StartScreenInfoRow) + moveState.StartColumn; Endpoint end = _screenInfoRowToEndpoint(pData, moveState.EndScreenInfoRow) + moveState.EndColumn; @@ -1559,14 +1548,13 @@ std::pair UiaTextRangeBase::_moveByLine(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - a pair of endpoints of the form -std::pair UiaTextRangeBase::_moveByDocument(IUiaData* pData, +std::pair UiaTextRangeBase::_moveByDocument(gsl::not_null pData, const int /*moveCount*/, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { // We can't move by anything larger than a line, so move by document will apply and will // just report that it can't do that. - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; // We then have to return the same endpoints as what we initially had so nothing happens. @@ -1587,13 +1575,12 @@ std::pair UiaTextRangeBase::_moveByDocument(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRangeBase::_moveEndpointByUnitCharacter(IUiaData* pData, +std::tuple UiaTextRangeBase::_moveEndpointByUnitCharacter(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); if (moveState.Direction == MovementDirection::Forward) { return _moveEndpointByUnitCharacterForward(pData, moveCount, endpoint, moveState, pAmountMoved); @@ -1605,14 +1592,12 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitCharac } std::tuple -UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, +UiaTextRangeBase::_moveEndpointByUnitCharacterForward(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF_NULL(E_INVALIDARG, pData); - THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = 0; @@ -1696,14 +1681,12 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterForward(IUiaData* pData, } std::tuple -UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, +UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF_NULL(E_INVALIDARG, pData); - THROW_HR_IF_NULL(E_INVALIDARG, pAmountMoved); *pAmountMoved = 0; const int count = moveCount; ScreenInfoRow currentScreenInfoRow = 0; @@ -1798,13 +1781,12 @@ UiaTextRangeBase::_moveEndpointByUnitCharacterBackward(IUiaData* pData, // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(IUiaData* pData, +std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; int count = moveCount; ScreenInfoRow currentScreenInfoRow = 0; @@ -1958,13 +1940,12 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitLine(I // - pAmountMoved - the number of times that the return values are "moved" // Return Value: // - A tuple of elements of the form -std::tuple UiaTextRangeBase::_moveEndpointByUnitDocument(IUiaData* pData, +std::tuple UiaTextRangeBase::_moveEndpointByUnitDocument(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved) + _Out_ gsl::not_null const pAmountMoved) { - THROW_HR_IF(E_INVALIDARG, pAmountMoved == nullptr); *pAmountMoved = 0; Endpoint start = 0; @@ -2026,12 +2007,12 @@ std::tuple UiaTextRangeBase::_moveEndpointByUnitDocume return std::make_tuple(start, end, degenerate); } -COORD UiaTextRangeBase::_endpointToCoord(IUiaData* pData, const Endpoint endpoint) +COORD UiaTextRangeBase::_endpointToCoord(gsl::not_null pData, const Endpoint endpoint) { return { gsl::narrow(_endpointToColumn(pData, endpoint)), gsl::narrow(_endpointToScreenInfoRow(pData, endpoint)) }; } -Endpoint UiaTextRangeBase::_coordToEndpoint(IUiaData* pData, +Endpoint UiaTextRangeBase::_coordToEndpoint(gsl::not_null pData, const COORD coord) { return _screenInfoRowToEndpoint(pData, coord.Y) + coord.X; @@ -2039,12 +2020,14 @@ Endpoint UiaTextRangeBase::_coordToEndpoint(IUiaData* pData, RECT UiaTextRangeBase::_getTerminalRect() const { - UiaRect result; + UiaRect result{ 0 }; IRawElementProviderFragment* pRawElementProviderFragment; THROW_IF_FAILED(_pProvider->QueryInterface(&pRawElementProviderFragment)); - THROW_HR_IF_NULL(E_POINTER, pRawElementProviderFragment); - pRawElementProviderFragment->get_BoundingRectangle(&result); + if (pRawElementProviderFragment) + { + pRawElementProviderFragment->get_BoundingRectangle(&result); + } return { gsl::narrow(result.left), diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index a502e135e4c..41c6c43dae0 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -259,126 +259,126 @@ namespace Microsoft::Console::Types RECT _getTerminalRect() const; - static const COORD _getScreenBufferCoords(IUiaData* pData); + static const COORD _getScreenBufferCoords(gsl::not_null pData); virtual const COORD _getScreenFontSize() const; - static const unsigned int _getTotalRows(IUiaData* pData); - static const unsigned int _getRowWidth(IUiaData* pData); + static const unsigned int _getTotalRows(gsl::not_null pData) noexcept; + static const unsigned int _getRowWidth(gsl::not_null pData); static const unsigned int _getFirstScreenInfoRowIndex() noexcept; - static const unsigned int _getLastScreenInfoRowIndex(IUiaData* pData) noexcept; + static const unsigned int _getLastScreenInfoRowIndex(gsl::not_null pData) noexcept; static const Column _getFirstColumnIndex() noexcept; - static const Column _getLastColumnIndex(IUiaData* pData); + static const Column _getLastColumnIndex(gsl::not_null pData); - const unsigned int _rowCountInRange(IUiaData* pData) const; + const unsigned int _rowCountInRange(gsl::not_null pData) const; - static const TextBufferRow _endpointToTextBufferRow(IUiaData* pData, + static const TextBufferRow _endpointToTextBufferRow(gsl::not_null pData, const Endpoint endpoint); - static const ScreenInfoRow _textBufferRowToScreenInfoRow(IUiaData* pData, - const TextBufferRow row); + static const ScreenInfoRow _textBufferRowToScreenInfoRow(gsl::not_null pData, + const TextBufferRow row) noexcept; - static const TextBufferRow _screenInfoRowToTextBufferRow(IUiaData* pData, - const ScreenInfoRow row); - static const Endpoint _textBufferRowToEndpoint(IUiaData* pData, const TextBufferRow row); + static const TextBufferRow _screenInfoRowToTextBufferRow(gsl::not_null pData, + const ScreenInfoRow row) noexcept; + static const Endpoint _textBufferRowToEndpoint(gsl::not_null pData, const TextBufferRow row); - static const ScreenInfoRow _endpointToScreenInfoRow(IUiaData* pData, + static const ScreenInfoRow _endpointToScreenInfoRow(gsl::not_null pData, const Endpoint endpoint); - static const Endpoint _screenInfoRowToEndpoint(IUiaData* pData, + static const Endpoint _screenInfoRowToEndpoint(gsl::not_null pData, const ScreenInfoRow row); - static COORD _endpointToCoord(IUiaData* pData, + static COORD _endpointToCoord(gsl::not_null pData, const Endpoint endpoint); - static Endpoint _coordToEndpoint(IUiaData* pData, + static Endpoint _coordToEndpoint(gsl::not_null pData, const COORD coord); - static const Column _endpointToColumn(IUiaData* pData, + static const Column _endpointToColumn(gsl::not_null pData, const Endpoint endpoint); - static const Row _normalizeRow(IUiaData* pData, const Row row) noexcept; + static const Row _normalizeRow(gsl::not_null pData, const Row row) noexcept; - static const ViewportRow _screenInfoRowToViewportRow(IUiaData* pData, - const ScreenInfoRow row); + static const ViewportRow _screenInfoRowToViewportRow(gsl::not_null pData, + const ScreenInfoRow row) noexcept; static constexpr const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, const SMALL_RECT viewport) noexcept; - static const bool _isScreenInfoRowInViewport(IUiaData* pData, - const ScreenInfoRow row); + static const bool _isScreenInfoRowInViewport(gsl::not_null pData, + const ScreenInfoRow row) noexcept; static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row, const SMALL_RECT viewport) noexcept; static const unsigned int _getViewportHeight(const SMALL_RECT viewport) noexcept; static const unsigned int _getViewportWidth(const SMALL_RECT viewport) noexcept; - void _addScreenInfoRowBoundaries(IUiaData* pData, + void _addScreenInfoRowBoundaries(gsl::not_null pData, const ScreenInfoRow screenInfoRow, _Inout_ std::vector& coords) const; - static const int _compareScreenCoords(IUiaData* pData, + static const int _compareScreenCoords(gsl::not_null pData, const ScreenInfoRow rowA, const Column colA, const ScreenInfoRow rowB, const Column colB); - static std::pair _moveByCharacter(IUiaData* pData, + static std::pair _moveByCharacter(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); - static std::pair _moveByCharacterForward(IUiaData* pData, + static std::pair _moveByCharacterForward(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); - static std::pair _moveByCharacterBackward(IUiaData* pData, + static std::pair _moveByCharacterBackward(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); - static std::pair _moveByLine(IUiaData* pData, + static std::pair _moveByLine(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); - static std::pair _moveByDocument(IUiaData* pData, + static std::pair _moveByDocument(gsl::not_null pData, const int moveCount, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacter(IUiaData* pData, + _moveEndpointByUnitCharacter(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterForward(IUiaData* pData, + _moveEndpointByUnitCharacterForward(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterBackward(IUiaData* pData, + _moveEndpointByUnitCharacterBackward(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitLine(IUiaData* pData, + _moveEndpointByUnitLine(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitDocument(IUiaData* pData, + _moveEndpointByUnitDocument(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, - _Out_ int* const pAmountMoved); + _Out_ gsl::not_null const pAmountMoved); #ifdef UNIT_TESTING friend class ::UiaTextRangeTests; From 45e599368ff369e7b343a7d20f0b0c4e98f4ce96 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 11:20:27 -0700 Subject: [PATCH 101/154] C26430, not tested for nullness on all paths. I will just always check for null as a defense against a bad QI implementation. --- src/types/ScreenInfoUiaProviderBase.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 6750cc34cd0..c0e97f7bbd4 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -321,12 +321,9 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetSelection(_Outptr_result_maybenull_ IRawElementProviderSimple* pProvider; hr = this->QueryInterface(IID_PPV_ARGS(&pProvider)); - if (SUCCEEDED(hr)) + if (pProvider == nullptr) { - if (pProvider == nullptr) - { - hr = E_POINTER; - } + hr = E_POINTER; } if (FAILED(hr)) @@ -455,12 +452,9 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetVisibleRanges(_Outptr_result_mayben IRawElementProviderSimple* pProvider; HRESULT hr = this->QueryInterface(IID_PPV_ARGS(&pProvider)); - if (SUCCEEDED(hr)) + if (pProvider == nullptr) { - if (pProvider == nullptr) - { - hr = E_POINTER; - } + hr = E_POINTER; } if (FAILED(hr)) From 9678dd894cb17b838017bd3050898832dca75cdd Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 11:27:43 -0700 Subject: [PATCH 102/154] C26414, don't use smart pointers for locals --- src/types/convert.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/types/convert.cpp b/src/types/convert.cpp index 966e9f4a5bc..b42431dcd61 100644 --- a/src/types/convert.cpp +++ b/src/types/convert.cpp @@ -45,14 +45,14 @@ static const WORD leftShiftScanCode = 0x2A; THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded)); // Allocate ourselves space in a smart pointer. - std::unique_ptr pwsOut = std::make_unique(cchNeeded); - THROW_IF_NULL_ALLOC(pwsOut); + std::wstring out; + out.resize(cchNeeded); // Attempt conversion for real. - THROW_LAST_ERROR_IF(0 == MultiByteToWideChar(codePage, 0, source.data(), iSource, pwsOut.get(), iTarget)); + THROW_LAST_ERROR_IF(0 == MultiByteToWideChar(codePage, 0, source.data(), iSource, out.data(), iTarget)); // Return as a string - return std::wstring(pwsOut.get(), cchNeeded); + return out; } // Routine Description: @@ -86,17 +86,17 @@ static const WORD leftShiftScanCode = 0x2A; THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded)); // Allocate ourselves space in a smart pointer - std::unique_ptr psOut = std::make_unique(cchNeeded); - THROW_IF_NULL_ALLOC(psOut.get()); + std::string out; + out.resize(cchNeeded); // Attempt conversion for real. // clang-format off #pragma prefast(suppress: __WARNING_W2A_BEST_FIT, "WC_NO_BEST_FIT_CHARS doesn't work in many codepages. Retain old behavior.") // clang-format on - THROW_LAST_ERROR_IF(0 == WideCharToMultiByte(codepage, 0, source.data(), iSource, psOut.get(), iTarget, nullptr, nullptr)); + THROW_LAST_ERROR_IF(0 == WideCharToMultiByte(codepage, 0, source.data(), iSource, out.data(), iTarget, nullptr, nullptr)); // Return as a string - return std::string(psOut.get(), cchNeeded); + return out; } // Routine Description: From dd49c3ed51b2cccaefdf529e14d353fcd32d8750 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 13:02:09 -0700 Subject: [PATCH 103/154] C26460, use const on params that are unchanged (and remove some unnecessary span refs). --- src/renderer/dx/DxRenderer.cpp | 5 +++++ src/types/inc/utils.hpp | 12 ++++++------ src/types/ut_types/UtilsTests.cpp | 4 ++-- src/types/utils.cpp | 12 ++++++------ 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 9d11a6159f4..5aea1be31aa 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1503,6 +1503,11 @@ float DxEngine::GetScaling() const noexcept THROW_IF_FAILED(fontFace0.As(&fontFace)); + // Retrieve metrics in case the font we created was different than what was requested. + weight = font->GetWeight(); + stretch = font->GetStretch(); + style = font->GetStyle(); + // Dig the family name out at the end to return it. familyName = _GetFontFamilyName(fontFamily.Get(), localeName); } diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index 6fee5e5205e..e6f9e924018 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -24,11 +24,11 @@ namespace Microsoft::Console::Utils std::string ColorToHexString(const COLORREF color); COLORREF ColorFromHexString(const std::string wstr); - void InitializeCampbellColorTable(gsl::span& table); - void InitializeCampbellColorTableForConhost(gsl::span& table); - void SwapANSIColorOrderForConhost(gsl::span& table); - void Initialize256ColorTable(gsl::span& table); - void SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) noexcept; + void InitializeCampbellColorTable(const gsl::span table); + void InitializeCampbellColorTableForConhost(const gsl::span table); + void SwapANSIColorOrderForConhost(const gsl::span table); + void Initialize256ColorTable(const gsl::span table); + void SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept; constexpr uint16_t EndianSwap(uint16_t value) { @@ -57,5 +57,5 @@ namespace Microsoft::Console::Utils return value; } - GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span& name); + GUID CreateV5Uuid(const GUID& namespaceGuid, const gsl::span name); } diff --git a/src/types/ut_types/UtilsTests.cpp b/src/types/ut_types/UtilsTests.cpp index 57cab5c0844..06153aee2dd 100644 --- a/src/types/ut_types/UtilsTests.cpp +++ b/src/types/ut_types/UtilsTests.cpp @@ -52,11 +52,11 @@ void UtilsTests::TestSwapColorPalette() std::array consoleTable; gsl::span terminalTableView = { &terminalTable[0], gsl::narrow(terminalTable.size()) }; - gsl::span consoleTableleView = { &consoleTable[0], gsl::narrow(consoleTable.size()) }; + gsl::span consoleTableView = { &consoleTable[0], gsl::narrow(consoleTable.size()) }; // First set up the colors InitializeCampbellColorTable(terminalTableView); - InitializeCampbellColorTableForConhost(consoleTableleView); + InitializeCampbellColorTableForConhost(consoleTableView); VERIFY_ARE_EQUAL(terminalTable[0], consoleTable[0]); VERIFY_ARE_EQUAL(terminalTable[1], consoleTable[4]); diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 034e46ec48a..44d4cefc0d6 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -123,7 +123,7 @@ bool Utils::IsValidHandle(const HANDLE handle) noexcept // - table: a color table with at least 16 entries // Return Value: // - , throws if the table has less that 16 entries -void Utils::InitializeCampbellColorTable(gsl::span& table) +void Utils::InitializeCampbellColorTable(const gsl::span table) { THROW_HR_IF(E_INVALIDARG, table.size() < 16); @@ -154,7 +154,7 @@ void Utils::InitializeCampbellColorTable(gsl::span& table) // - table: a color table with at least 16 entries // Return Value: // - , throws if the table has less that 16 entries -void Utils::InitializeCampbellColorTableForConhost(gsl::span& table) +void Utils::InitializeCampbellColorTableForConhost(const gsl::span table) { THROW_HR_IF(E_INVALIDARG, table.size() < 16); InitializeCampbellColorTable(table); @@ -167,7 +167,7 @@ void Utils::InitializeCampbellColorTableForConhost(gsl::span& table) // - table: a color table with at least 16 entries // Return Value: // - , throws if the table has less that 16 entries -void Utils::SwapANSIColorOrderForConhost(gsl::span& table) +void Utils::SwapANSIColorOrderForConhost(const gsl::span table) { THROW_HR_IF(E_INVALIDARG, table.size() < 16); std::swap(table[1], table[4]); @@ -183,7 +183,7 @@ void Utils::SwapANSIColorOrderForConhost(gsl::span& table) // - table: a color table with at least 256 entries // Return Value: // - , throws if the table has less that 256 entries -void Utils::Initialize256ColorTable(gsl::span& table) +void Utils::Initialize256ColorTable(const gsl::span table) { THROW_HR_IF(E_INVALIDARG, table.size() < 256); @@ -454,7 +454,7 @@ void Utils::Initialize256ColorTable(gsl::span& table) // - newAlpha: the new value to use as the alpha for all the entries in that table. // Return Value: // - -void Utils::SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) noexcept +void Utils::SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept { const auto shiftedAlpha = newAlpha << 24; for (auto& color : table) @@ -473,7 +473,7 @@ void Utils::SetColorTableAlpha(gsl::span& table, const BYTE newAlpha) // - name: Bytes comprising the name (in a namespace-specific format) // Return Value: // - a new stable v5 UUID -GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const gsl::span& name) +GUID Utils::CreateV5Uuid(const GUID& namespaceGuid, const gsl::span name) { // v5 uuid generation happens over values in network byte order, so let's enforce that auto correctEndianNamespaceGuid{ EndianSwap(namespaceGuid) }; From d8bc94f13c984246944d4ff1224833310fd66003 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 13:30:03 -0700 Subject: [PATCH 104/154] forgot all return paths to _FillRectangle. --- src/renderer/dx/CustomTextRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index b2697abf9f2..f9e5f1a35b5 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -174,6 +174,8 @@ using namespace Microsoft::Console::Render; const D2D1_RECT_F rect = D2D1::RectF(x, y, x + width, y + thickness); drawingContext->renderTarget->FillRectangle(&rect, brush); + + return S_OK; } // Routine Description: From 49ff36bfc331ac54980df9875bb1d0278e867450 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 28 Aug 2019 01:49:56 +0000 Subject: [PATCH 105/154] Reflect inbox changes in 8c63dff [Git2Git] Git Train: Merge of building/rs_onecore_dep_uxp/190820-1847 into official/rs_onecore_dep_uxp Retrieved from https://microsoft.visualstudio.com os OS official/rs_onecore_dep_uxp 73e964d4046c37df3030970cae1ae32e83103fb5 (cherry picked from commit 8c63dff982093db1af7e2bb46b49af884dfec0c5) --- src/host/ft_host/API_PolicyTests.cpp | 1 + src/tsf/ConsoleTSF.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/host/ft_host/API_PolicyTests.cpp b/src/host/ft_host/API_PolicyTests.cpp index c70e02fd403..89f2bef1c4e 100644 --- a/src/host/ft_host/API_PolicyTests.cpp +++ b/src/host/ft_host/API_PolicyTests.cpp @@ -15,6 +15,7 @@ class PolicyTests #ifdef __INSIDE_WINDOWS BEGIN_TEST_METHOD(WrongWayVerbsUAP) TEST_METHOD_PROPERTY(L"RunAs", L"UAP") + TEST_METHOD_PROPERTY(L"UAP:AppxManifest", L"MUA") END_TEST_METHOD(); #endif diff --git a/src/tsf/ConsoleTSF.cpp b/src/tsf/ConsoleTSF.cpp index a47c63d6b75..17d3a687108 100644 --- a/src/tsf/ConsoleTSF.cpp +++ b/src/tsf/ConsoleTSF.cpp @@ -39,7 +39,7 @@ const GUID GUID_APPLICATION = { 0x626761ad, 0x78d2, 0x44d2, { 0xbe, 0x8b, 0x75, hr = ::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&_spITfThreadMgr)); Init_CheckResult(); - hr = _spITfThreadMgr->Activate(&_tid); + hr = _spITfThreadMgr->ActivateEx(&_tid, TF_TMAE_CONSOLE); Init_CheckResult(); // Create Cicero document manager and input context. From 2d3f2858940c2e975f7ede90d3088a0c7f4481b8 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 13:45:16 -0700 Subject: [PATCH 106/154] C26432, rule-of-five (if you define one of destruct/copy/move, then define them all) --- src/buffer/out/textBufferCellIterator.hpp | 2 -- src/renderer/base/lib/base.vcxproj | 1 + src/renderer/base/lib/base.vcxproj.filters | 3 +++ src/renderer/dx/DxRenderer.hpp | 4 ++++ src/renderer/inc/IRenderEngine.hpp | 8 ++++++++ src/renderer/inc/IRenderTarget.hpp | 7 +++++++ src/renderer/inc/RenderEngineBase.hpp | 8 +++++++- src/types/IBaseData.h | 7 +++++++ src/types/IUiaData.h | 7 +++++++ src/types/ScreenInfoUiaProviderBase.cpp | 4 ---- src/types/ScreenInfoUiaProviderBase.h | 6 +++++- src/types/UiaTextRangeBase.hpp | 3 +++ src/types/inc/CodepointWidthDetector.hpp | 1 + 13 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/buffer/out/textBufferCellIterator.hpp b/src/buffer/out/textBufferCellIterator.hpp index 68c6e1531a5..d647bd70152 100644 --- a/src/buffer/out/textBufferCellIterator.hpp +++ b/src/buffer/out/textBufferCellIterator.hpp @@ -28,8 +28,6 @@ class TextBufferCellIterator TextBufferCellIterator(const TextBuffer& buffer, COORD pos); TextBufferCellIterator(const TextBuffer& buffer, COORD pos, const Microsoft::Console::Types::Viewport limits); - ~TextBufferCellIterator() = default; - operator bool() const noexcept; bool operator==(const TextBufferCellIterator& it) const; diff --git a/src/renderer/base/lib/base.vcxproj b/src/renderer/base/lib/base.vcxproj index 7ba34072f2a..03f238e9ad6 100644 --- a/src/renderer/base/lib/base.vcxproj +++ b/src/renderer/base/lib/base.vcxproj @@ -22,6 +22,7 @@ + diff --git a/src/renderer/base/lib/base.vcxproj.filters b/src/renderer/base/lib/base.vcxproj.filters index 00f0bb1e6dd..300c6dfc5d4 100644 --- a/src/renderer/base/lib/base.vcxproj.filters +++ b/src/renderer/base/lib/base.vcxproj.filters @@ -80,6 +80,9 @@ Header Files\inc + + Header Files\inc + diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index c4a5260bf56..bd5af7c60f6 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -32,6 +32,10 @@ namespace Microsoft::Console::Render public: DxEngine(); virtual ~DxEngine() override; + DxEngine(const DxEngine&) = default; + DxEngine(DxEngine&&) = default; + DxEngine& operator=(const DxEngine&) = default; + DxEngine& operator=(DxEngine&&) = default; // Used to release device resources so that another instance of // conhost can render to the screen (i.e. only one DirectX diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 2399ab6a3f2..d172607b3ca 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -64,6 +64,14 @@ namespace Microsoft::Console::Render virtual ~IRenderEngine() = 0; + protected: + IRenderEngine() = default; + IRenderEngine(const IRenderEngine&) = default; + IRenderEngine(IRenderEngine&&) = default; + IRenderEngine& operator=(const IRenderEngine&) = default; + IRenderEngine& operator=(IRenderEngine&&) = default; + public: + [[nodiscard]] virtual HRESULT StartPaint() noexcept = 0; [[nodiscard]] virtual HRESULT EndPaint() noexcept = 0; [[nodiscard]] virtual HRESULT Present() noexcept = 0; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp index 1d1223b12b1..ffc83f6e371 100644 --- a/src/renderer/inc/IRenderTarget.hpp +++ b/src/renderer/inc/IRenderTarget.hpp @@ -24,6 +24,13 @@ namespace Microsoft::Console::Render { public: virtual ~IRenderTarget() = 0; + protected: + IRenderTarget() = default; + IRenderTarget(const IRenderTarget&) = default; + IRenderTarget(IRenderTarget&&) = default; + IRenderTarget& operator=(const IRenderTarget&) = default; + IRenderTarget& operator=(IRenderTarget&&) = default; + public: virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; virtual void TriggerRedraw(const COORD* const pcoord) = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index dad43c5c51a..66f0ee1d594 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -24,8 +24,14 @@ namespace Microsoft::Console::Render class RenderEngineBase : public IRenderEngine { public: + ~RenderEngineBase() = 0; + protected: RenderEngineBase(); - virtual ~RenderEngineBase() = 0; + RenderEngineBase(const RenderEngineBase&) = default; + RenderEngineBase(RenderEngineBase&&) = default; + RenderEngineBase& operator=(const RenderEngineBase&) = default; + RenderEngineBase& operator=(RenderEngineBase&&) = default; + public: [[nodiscard]] HRESULT InvalidateTitle(const std::wstring& proposedTitle) noexcept override; diff --git a/src/types/IBaseData.h b/src/types/IBaseData.h index e82bfa8c170..cabcaf9a773 100644 --- a/src/types/IBaseData.h +++ b/src/types/IBaseData.h @@ -24,6 +24,13 @@ namespace Microsoft::Console::Types { public: virtual ~IBaseData() = 0; + protected: + IBaseData() = default; + IBaseData(const IBaseData&) = default; + IBaseData(IBaseData&&) = default; + IBaseData& operator=(const IBaseData&) = default; + IBaseData& operator=(IBaseData&&) = default; + public: virtual Microsoft::Console::Types::Viewport GetViewport() noexcept = 0; virtual const TextBuffer& GetTextBuffer() noexcept = 0; virtual const FontInfo& GetFontInfo() noexcept = 0; diff --git a/src/types/IUiaData.h b/src/types/IUiaData.h index a087789172d..d28046d0c96 100644 --- a/src/types/IUiaData.h +++ b/src/types/IUiaData.h @@ -24,6 +24,13 @@ namespace Microsoft::Console::Types { public: virtual ~IUiaData() = 0; + protected: + IUiaData() = default; + IUiaData(const IUiaData&) = default; + IUiaData(IUiaData&&) = default; + IUiaData& operator=(const IUiaData&) = default; + IUiaData& operator=(IUiaData&&) = default; + public: virtual const bool IsSelectionActive() const = 0; virtual void ClearSelection() = 0; diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index c0e97f7bbd4..3e201d30731 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -38,10 +38,6 @@ ScreenInfoUiaProviderBase::ScreenInfoUiaProviderBase(_In_ IUiaData* pData) : //Tracing::s_TraceUia(nullptr, ApiCall::Constructor, nullptr); } -ScreenInfoUiaProviderBase::~ScreenInfoUiaProviderBase() -{ -} - [[nodiscard]] HRESULT ScreenInfoUiaProviderBase::Signal(_In_ EVENTID id) { HRESULT hr = S_OK; diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h index 3f7f63ad2ee..7aebf5a56e6 100644 --- a/src/types/ScreenInfoUiaProviderBase.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -38,7 +38,11 @@ namespace Microsoft::Console::Types { public: ScreenInfoUiaProviderBase(_In_ IUiaData* pData); - virtual ~ScreenInfoUiaProviderBase(); + ScreenInfoUiaProviderBase(const ScreenInfoUiaProviderBase&) = default; + ScreenInfoUiaProviderBase(ScreenInfoUiaProviderBase&&) = default; + ScreenInfoUiaProviderBase& operator=(const ScreenInfoUiaProviderBase&) = default; + ScreenInfoUiaProviderBase& operator=(ScreenInfoUiaProviderBase&&) = default; + virtual ~ScreenInfoUiaProviderBase() = default; [[nodiscard]] HRESULT Signal(_In_ EVENTID id); diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 41c6c43dae0..3028dd7f0f5 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -137,6 +137,9 @@ namespace Microsoft::Console::Types }; public: + UiaTextRangeBase(UiaTextRangeBase&&) = default; + UiaTextRangeBase& operator=(const UiaTextRangeBase&) = default; + UiaTextRangeBase& operator=(UiaTextRangeBase&&) = default; virtual ~UiaTextRangeBase() = default; const IdType GetId() const noexcept; diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 22d9605c381..9bc06a01d23 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -28,6 +28,7 @@ class CodepointWidthDetector final CodepointWidthDetector(CodepointWidthDetector&&) = delete; ~CodepointWidthDetector() = default; CodepointWidthDetector& operator=(const CodepointWidthDetector&) = delete; + CodepointWidthDetector& operator=(CodepointWidthDetector&&) = delete; CodepointWidth GetWidth(const std::wstring_view glyph) const; bool IsWide(const std::wstring_view glyph) const; From 3bbd8f4c97d494f89f6b7083403785ec8e241db8 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:14:07 -0700 Subject: [PATCH 107/154] C26443, overriding destructors shouldn't declare virtual nor override. --- src/renderer/dx/DxRenderer.hpp | 2 +- src/types/IUiaData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index bd5af7c60f6..a76c7dcf037 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -31,7 +31,7 @@ namespace Microsoft::Console::Render { public: DxEngine(); - virtual ~DxEngine() override; + ~DxEngine(); DxEngine(const DxEngine&) = default; DxEngine(DxEngine&&) = default; DxEngine& operator=(const DxEngine&) = default; diff --git a/src/types/IUiaData.h b/src/types/IUiaData.h index d28046d0c96..7b2ef08b1b1 100644 --- a/src/types/IUiaData.h +++ b/src/types/IUiaData.h @@ -23,7 +23,7 @@ namespace Microsoft::Console::Types class IUiaData : public IBaseData { public: - virtual ~IUiaData() = 0; + ~IUiaData() = 0; protected: IUiaData() = default; IUiaData(const IUiaData&) = default; From b78d9176ae89d182a62ee01d80422d92da0b108b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:22:02 -0700 Subject: [PATCH 108/154] C26434, do not hide base class methods. Overriding this one because it's going to require design changes that need a future todo. --- src/buffer/out/textBufferTextIterator.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/buffer/out/textBufferTextIterator.cpp b/src/buffer/out/textBufferTextIterator.cpp index b8a17d11368..7e7ed7acd24 100644 --- a/src/buffer/out/textBufferTextIterator.cpp +++ b/src/buffer/out/textBufferTextIterator.cpp @@ -25,6 +25,7 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data +[[gsl::suppress(26434)]] const std::wstring_view TextBufferTextIterator::operator*() const noexcept { return _view.Chars(); @@ -34,6 +35,7 @@ const std::wstring_view TextBufferTextIterator::operator*() const noexcept // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data +[[gsl::suppress(26434)]] const std::wstring_view* TextBufferTextIterator::operator->() const noexcept { return &_view.Chars(); From b87f8f907043e8e9fe4943af442bf4e4f00e6c91 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:30:40 -0700 Subject: [PATCH 109/154] C26426, global initializers calling non-constexpr. Suppress for default settings as changing to wstring_view cascades through the entire codebase (non-trivial, string_views aren't guaranteed as Z terminated.) --- src/inc/DefaultSettings.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 99ae30aed78..2a16dd88ead 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -26,6 +26,9 @@ constexpr COLORREF DEFAULT_BACKGROUND_WITH_ALPHA = OPACITY_OPAQUE | DEFAULT_BACK constexpr COLORREF POWERSHELL_BLUE = RGB(1, 36, 86); constexpr short DEFAULT_HISTORY_SIZE = 9001; + +#pragma warning(push) +#pragma warning(disable:26426) const std::wstring DEFAULT_FONT_FACE{ L"Consolas" }; constexpr int DEFAULT_FONT_SIZE = 10; @@ -39,3 +42,4 @@ constexpr COLORREF DEFAULT_CURSOR_COLOR = COLOR_WHITE; constexpr COLORREF DEFAULT_CURSOR_HEIGHT = 25; const std::wstring DEFAULT_WORD_DELIMITERS{ L" ./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}~?\u2502" }; +#pragma warning(pop) From 072bbfd09de467cf7921828ae6afae2378e62511 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:32:44 -0700 Subject: [PATCH 110/154] C26426, global initializer calls non-constexpr. This needs further consideration. I brifely tried to turn GlyphWidth into a singleton class but it cascaded into interesting far corners of the code because IsGlyphFullWidth was liberally used everywhere for a long time. I'm punting here to a future work item. --- src/types/GlyphWidth.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/GlyphWidth.cpp b/src/types/GlyphWidth.cpp index 8d60d507e2d..5efbaba2d9a 100644 --- a/src/types/GlyphWidth.cpp +++ b/src/types/GlyphWidth.cpp @@ -5,6 +5,7 @@ #include "inc/CodepointWidthDetector.hpp" #include "inc/GlyphWidth.hpp" +#pragma warning(suppress: 26426) static CodepointWidthDetector widthDetector; // Function Description: From 5d60d69e86803df399cb6f61a0406438ac188bda Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:33:00 -0700 Subject: [PATCH 111/154] C26426, global initializer calls non-constexpr. This is an easy move to wstring_view. --- src/renderer/dx/DxRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 5aea1be31aa..361592c0e7c 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -15,7 +15,7 @@ #pragma hdrstop static constexpr float POINTS_PER_INCH = 72.0f; -static std::wstring FALLBACK_FONT_FACE = L"Consolas"; +static std::wstring_view FALLBACK_FONT_FACE = L"Consolas"; static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; using namespace Microsoft::Console::Render; From c7f0a3439d4de40353390a028a103e95da96bb0e Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:39:23 -0700 Subject: [PATCH 112/154] C26490, don't reinterpret_cast. It looks like the buffer can easily be char. Also use brace initialization per feedback. --- src/types/UTF8OutPipeReader.cpp | 10 +++++----- src/types/inc/UTF8OutPipeReader.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/types/UTF8OutPipeReader.cpp b/src/types/UTF8OutPipeReader.cpp index 85b60b30293..7568a44db13 100644 --- a/src/types/UTF8OutPipeReader.cpp +++ b/src/types/UTF8OutPipeReader.cpp @@ -7,10 +7,10 @@ #include UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : - _outPipe{ outPipe } + _outPipe{ outPipe }, + _buffer{ 0 }, + _utf8Partials{ 0 } { - _buffer.fill(0); - _utf8Partials.fill(0); } // Method Description: @@ -33,7 +33,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : // in case of early escaping _buffer.at(0) = 0; - strView = std::string_view{ reinterpret_cast(_buffer.at(0)), 0 }; + strView = std::string_view{ &_buffer.at(0), 0 }; // copy UTF-8 code units that were remaining from the previously read chunk (if any) if (_dwPartialsLen != 0) @@ -95,6 +95,6 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : } // give back a view of the part of the buffer that contains complete code points only - strView = std::string_view{ reinterpret_cast(_buffer.at(0)), dwRead }; + strView = std::string_view{ &_buffer.at(0), dwRead }; return S_OK; } diff --git a/src/types/inc/UTF8OutPipeReader.hpp b/src/types/inc/UTF8OutPipeReader.hpp index fb0da7eaa6c..46bf507d6df 100644 --- a/src/types/inc/UTF8OutPipeReader.hpp +++ b/src/types/inc/UTF8OutPipeReader.hpp @@ -62,7 +62,7 @@ class UTF8OutPipeReader final }; HANDLE _outPipe; // non-owning reference to a pipe. - std::array _buffer; // buffer for the chunk read. - std::array _utf8Partials; // buffer for code units of a partial UTF-8 code point that have to be cached + std::array _buffer; // buffer for the chunk read. + std::array _utf8Partials; // buffer for code units of a partial UTF-8 code point that have to be cached DWORD _dwPartialsLen{}; // number of cached UTF-8 code units }; From cd144e98c64411c113ae085d97d88a6567e44674 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:52:00 -0700 Subject: [PATCH 113/154] C26436, destructor definition required for class with virtual methods. --- src/types/WindowUiaProviderBase.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/types/WindowUiaProviderBase.hpp b/src/types/WindowUiaProviderBase.hpp index 7b84dee8caa..2a4634a6961 100644 --- a/src/types/WindowUiaProviderBase.hpp +++ b/src/types/WindowUiaProviderBase.hpp @@ -31,6 +31,14 @@ namespace Microsoft::Console::Types public IRawElementProviderFragment, public IRawElementProviderFragmentRoot { + public: + virtual ~WindowUiaProviderBase() = default; + protected: + WindowUiaProviderBase() = default; + WindowUiaProviderBase(const WindowUiaProviderBase&) = default; + WindowUiaProviderBase(WindowUiaProviderBase&&) = default; + WindowUiaProviderBase& operator=(const WindowUiaProviderBase&) = default; + WindowUiaProviderBase& operator=(WindowUiaProviderBase&&) = default; public: [[nodiscard]] virtual HRESULT Signal(_In_ EVENTID id) = 0; [[nodiscard]] virtual HRESULT SetTextAreaFocus() = 0; From e14a59a1b6282ce28c30f0c1be6c3887fed5561d Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 14:57:14 -0700 Subject: [PATCH 114/154] C26455, default constructor may not throw. Mark `noexcept`. (Trivial cases.) --- src/buffer/out/OutputCell.cpp | 2 +- src/buffer/out/OutputCell.hpp | 2 +- src/buffer/out/OutputCellRect.cpp | 2 +- src/buffer/out/OutputCellRect.hpp | 2 +- src/buffer/out/UnicodeStorage.hpp | 2 +- src/renderer/dx/CustomTextLayout.h | 4 ++-- src/types/inc/CodepointWidthDetector.hpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/buffer/out/OutputCell.cpp b/src/buffer/out/OutputCell.cpp index bed14b3f1f4..456202fb23a 100644 --- a/src/buffer/out/OutputCell.cpp +++ b/src/buffer/out/OutputCell.cpp @@ -11,7 +11,7 @@ static constexpr TextAttribute InvalidTextAttribute{ INVALID_COLOR, INVALID_COLOR }; -OutputCell::OutputCell() : +OutputCell::OutputCell() noexcept : _text{}, _dbcsAttribute{}, _textAttribute{ InvalidTextAttribute }, diff --git a/src/buffer/out/OutputCell.hpp b/src/buffer/out/OutputCell.hpp index 3d100bab6c6..df64e0b461e 100644 --- a/src/buffer/out/OutputCell.hpp +++ b/src/buffer/out/OutputCell.hpp @@ -34,7 +34,7 @@ class InvalidCharInfoConversionException : public std::exception class OutputCell final { public: - OutputCell(); + OutputCell() noexcept; OutputCell(const std::wstring_view charData, const DbcsAttribute dbcsAttribute, diff --git a/src/buffer/out/OutputCellRect.cpp b/src/buffer/out/OutputCellRect.cpp index aec19e9fb3d..90385094465 100644 --- a/src/buffer/out/OutputCellRect.cpp +++ b/src/buffer/out/OutputCellRect.cpp @@ -7,7 +7,7 @@ // Routine Description: // - Constucts an empty in-memory region for holding output buffer cell data. -OutputCellRect::OutputCellRect() : +OutputCellRect::OutputCellRect() noexcept : _rows(0), _cols(0) { diff --git a/src/buffer/out/OutputCellRect.hpp b/src/buffer/out/OutputCellRect.hpp index ae73232505d..ebdb4883e35 100644 --- a/src/buffer/out/OutputCellRect.hpp +++ b/src/buffer/out/OutputCellRect.hpp @@ -29,7 +29,7 @@ Revision History: class OutputCellRect final { public: - OutputCellRect(); + OutputCellRect() noexcept; OutputCellRect(const size_t rows, const size_t cols); gsl::span GetRow(const size_t row); diff --git a/src/buffer/out/UnicodeStorage.hpp b/src/buffer/out/UnicodeStorage.hpp index 88669f79259..0f2a629bd40 100644 --- a/src/buffer/out/UnicodeStorage.hpp +++ b/src/buffer/out/UnicodeStorage.hpp @@ -47,7 +47,7 @@ class UnicodeStorage final using key_type = typename COORD; using mapped_type = typename std::vector; - UnicodeStorage(); + UnicodeStorage() noexcept; const mapped_type& GetText(const key_type key) const; diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index ee0bab63825..73fa3a07c6c 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -68,7 +68,7 @@ namespace Microsoft::Console::Render // A single contiguous run of characters containing the same analysis results. struct Run { - Run() : + Run() noexcept : textStart(), textLength(), glyphStart(), @@ -108,7 +108,7 @@ namespace Microsoft::Console::Render // Single text analysis run, which points to the next run. struct LinkedRun : Run { - LinkedRun() : + LinkedRun() noexcept : nextRunIndex(0) { } diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 9bc06a01d23..2b1edb00aaf 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -23,7 +23,7 @@ static_assert(sizeof(unsigned int) == sizeof(wchar_t) * 2, class CodepointWidthDetector final { public: - CodepointWidthDetector() = default; + CodepointWidthDetector() noexcept = default; CodepointWidthDetector(const CodepointWidthDetector&) = delete; CodepointWidthDetector(CodepointWidthDetector&&) = delete; ~CodepointWidthDetector() = default; From 87f5852a72dfefc22553bee5dba8011a94df6181 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:03:54 -0700 Subject: [PATCH 115/154] Define actual constructor for CodepointWidthDetector as default isn't cutting it. --- src/types/CodepointWidthDetector.cpp | 9 +++++++++ src/types/inc/CodepointWidthDetector.hpp | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index de3f6e6d3a0..aea44fe9643 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -310,6 +310,15 @@ namespace }; } +// Routine Description: +// - Constructs an instance of the CodepointWidthDetector class +CodepointWidthDetector::CodepointWidthDetector() noexcept : + _fallbackCache{}, + _pfnFallbackMethod{} +{ + +} + // Routine Description: // - returns the width type of codepoint by searching the map generated from the unicode spec // Arguments: diff --git a/src/types/inc/CodepointWidthDetector.hpp b/src/types/inc/CodepointWidthDetector.hpp index 2b1edb00aaf..0dc7ac8b966 100644 --- a/src/types/inc/CodepointWidthDetector.hpp +++ b/src/types/inc/CodepointWidthDetector.hpp @@ -23,7 +23,7 @@ static_assert(sizeof(unsigned int) == sizeof(wchar_t) * 2, class CodepointWidthDetector final { public: - CodepointWidthDetector() noexcept = default; + CodepointWidthDetector() noexcept; CodepointWidthDetector(const CodepointWidthDetector&) = delete; CodepointWidthDetector(CodepointWidthDetector&&) = delete; ~CodepointWidthDetector() = default; From b2c093fa2fa81940d4bcdc58498a2d00e4222241 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:04:42 -0700 Subject: [PATCH 116/154] C26455, default constructor may not throw, mark as nothrow (another trivial one) --- src/buffer/out/UnicodeStorage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/buffer/out/UnicodeStorage.cpp b/src/buffer/out/UnicodeStorage.cpp index ae71f0f5380..41f85adcff6 100644 --- a/src/buffer/out/UnicodeStorage.cpp +++ b/src/buffer/out/UnicodeStorage.cpp @@ -4,7 +4,7 @@ #include "precomp.h" #include "UnicodeStorage.hpp" -UnicodeStorage::UnicodeStorage() : +UnicodeStorage::UnicodeStorage() noexcept : _map{} { } From 3a0da642764e52e94107e5742a7d16dff5af3525 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:08:48 -0700 Subject: [PATCH 117/154] C26490, no reinterpret_cast. Suppress on OutputCellIterator because fixing it will make trouble in the Windows build if we're not careful thanks to non-differentiation of wchar_t and DWORD. --- src/buffer/out/OutputCellIterator.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index ff33b2c091a..a709c1c3cea 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -116,6 +116,10 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const // razzle cannot distinguish between a std::wstring_view and a std::basic_string_view // NOTE: This one internally casts to wchar_t because Razzle sees WORD and wchar_t as the same type // despite that Visual Studio build can tell the difference. +#pragma warning(push) +#pragma warning(suppress:26490) +// Suppresses reinterpret_cast. We're only doing this because Windows doesn't understand the type difference between wchar_t and DWORD. +// It is not worth trying to separate that out further or risking performance over this particular warning here. OutputCellIterator::OutputCellIterator(const std::basic_string_view legacyAttrs, const bool /*unused*/) noexcept : _mode(Mode::LegacyAttr), _currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))), @@ -126,6 +130,7 @@ OutputCellIterator::OutputCellIterator(const std::basic_string_view legacy _fillLimit(0) { } +#pragma warning(pop) // Routine Description: // - This is an iterator over legacy cell data. We will use the unicode text and the legacy color attribute. From 244fb72fee6226a37fae94af294cdc6b2d3446a9 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:09:30 -0700 Subject: [PATCH 118/154] C26490, no reinterpret_cast. Just use the actual struct and copy instead of relying on the wink/nudge fact they're defined the same way. --- src/renderer/dx/CustomTextRenderer.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index f9e5f1a35b5..b071f69e195 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -70,8 +70,17 @@ using namespace Microsoft::Console::Render; const DrawingContext* drawingContext = static_cast(clientDrawingContext); RETURN_HR_IF_NULL(E_INVALIDARG, drawingContext); - // Matrix structures are defined identically - drawingContext->renderTarget->GetTransform(reinterpret_cast(transform)); + // Retrieve as D2D1 matrix then copy into DWRITE matrix. + D2D1_MATRIX_3X2_F d2d1Matrix{ 0 }; + drawingContext->renderTarget->GetTransform(&d2d1Matrix); + + transform->dx = d2d1Matrix.dx; + transform->dy = d2d1Matrix.dy; + transform->m11 = d2d1Matrix.m11; + transform->m12 = d2d1Matrix.m12; + transform->m21 = d2d1Matrix.m21; + transform->m22 = d2d1Matrix.m22; + return S_OK; } #pragma endregion From 41f209f6d3f51f49a16cbe3e7960de3e88849df5 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:10:33 -0700 Subject: [PATCH 119/154] C26440, default constructors should be noexcept. --- src/types/UTF8OutPipeReader.cpp | 2 +- src/types/inc/UTF8OutPipeReader.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/UTF8OutPipeReader.cpp b/src/types/UTF8OutPipeReader.cpp index 7568a44db13..c35530b8b62 100644 --- a/src/types/UTF8OutPipeReader.cpp +++ b/src/types/UTF8OutPipeReader.cpp @@ -6,7 +6,7 @@ #include #include -UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) : +UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) noexcept : _outPipe{ outPipe }, _buffer{ 0 }, _utf8Partials{ 0 } diff --git a/src/types/inc/UTF8OutPipeReader.hpp b/src/types/inc/UTF8OutPipeReader.hpp index 46bf507d6df..3f0d84f79f6 100644 --- a/src/types/inc/UTF8OutPipeReader.hpp +++ b/src/types/inc/UTF8OutPipeReader.hpp @@ -27,7 +27,7 @@ Author(s): class UTF8OutPipeReader final { public: - UTF8OutPipeReader(HANDLE outPipe); + UTF8OutPipeReader(HANDLE outPipe) noexcept; [[nodiscard]] HRESULT Read(_Out_ std::string_view& strView); private: From 93aa9455e2773b85e5bfd56e2d81a3bdb98b0c45 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:14:44 -0700 Subject: [PATCH 120/154] C26429, test for nullness or mark as not_null (and a few cascading warnings. --- src/renderer/dx/DxRenderer.cpp | 4 +--- src/renderer/dx/DxRenderer.hpp | 2 +- src/types/WindowUiaProviderBase.cpp | 13 +++++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 361592c0e7c..17d0a0976d8 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1545,11 +1545,9 @@ float DxEngine::GetScaling() const noexcept // - If fallback occurred, this is updated to what we retrieved instead. // Return Value: // - Localized string name of the font family -[[nodiscard]] std::wstring DxEngine::_GetFontFamilyName(IDWriteFontFamily* const fontFamily, +[[nodiscard]] std::wstring DxEngine::_GetFontFamilyName(gsl::not_null const fontFamily, std::wstring& localeName) const { - THROW_HR_IF_NULL(E_INVALIDARG, fontFamily); - // See: https://docs.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontcollection Microsoft::WRL::ComPtr familyNames; THROW_IF_FAILED(fontFamily->GetFamilyNames(&familyNames)); diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index a76c7dcf037..2f238ad9080 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -197,7 +197,7 @@ namespace Microsoft::Console::Render [[nodiscard]] std::wstring _GetLocaleName() const; - [[nodiscard]] std::wstring _GetFontFamilyName(IDWriteFontFamily* const fontFamily, + [[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null const fontFamily, std::wstring& localeName) const; [[nodiscard]] HRESULT _GetProposedFont(const FontInfoDesired& desired, diff --git a/src/types/WindowUiaProviderBase.cpp b/src/types/WindowUiaProviderBase.cpp index ec8add2c256..63735e34b6a 100644 --- a/src/types/WindowUiaProviderBase.cpp +++ b/src/types/WindowUiaProviderBase.cpp @@ -214,10 +214,15 @@ IFACEMETHODIMP WindowUiaProviderBase::get_FragmentRoot(_COM_Outptr_result_mayben HWND WindowUiaProviderBase::GetWindowHandle() const { - IUiaWindow* const pConsoleWindow = _baseWindow; - THROW_HR_IF_NULL(E_POINTER, pConsoleWindow); - - return pConsoleWindow->GetWindowHandle(); + const IUiaWindow* const pConsoleWindow = _baseWindow; + if (pConsoleWindow) + { + return pConsoleWindow->GetWindowHandle(); + } + else + { + return nullptr; + } } [[nodiscard]] HRESULT WindowUiaProviderBase::_EnsureValidHwnd() const From ae25a32913e3a0f8a5945705f08bf47e3d12307b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:18:01 -0700 Subject: [PATCH 121/154] C26497, you can mark this thing as constexpr. --- src/types/inc/utils.hpp | 2 +- src/types/utils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index e6f9e924018..11cc46b3b69 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -28,7 +28,7 @@ namespace Microsoft::Console::Utils void InitializeCampbellColorTableForConhost(const gsl::span table); void SwapANSIColorOrderForConhost(const gsl::span table); void Initialize256ColorTable(const gsl::span table); - void SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept; + constexpr void SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept; constexpr uint16_t EndianSwap(uint16_t value) { diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 44d4cefc0d6..536cd40d25d 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -454,7 +454,7 @@ void Utils::Initialize256ColorTable(const gsl::span table) // - newAlpha: the new value to use as the alpha for all the entries in that table. // Return Value: // - -void Utils::SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept +constexpr void Utils::SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept { const auto shiftedAlpha = newAlpha << 24; for (auto& color : table) From 01bd77003c228846f106ad74fbddf5b1925e8d03 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:23:44 -0700 Subject: [PATCH 122/154] C26429, mark as not_null if not testing for nullness. --- src/renderer/dx/CustomTextLayout.cpp | 18 ++++++++---------- src/renderer/dx/CustomTextLayout.h | 8 ++++---- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 83391f5dd3b..de883e84215 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -20,16 +20,16 @@ using namespace Microsoft::Console::Render; // - font - The DirectWrite font face to use while calculating layout (by default, will fallback if necessary) // - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume. // - width - The count of pixels available per column (the expected pixel width of every column) -CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, - IDWriteTextAnalyzer1* const analyzer, - IDWriteTextFormat* const format, - IDWriteFontFace1* const font, +CustomTextLayout::CustomTextLayout(gsl::not_null const factory, + gsl::not_null const analyzer, + gsl::not_null const format, + gsl::not_null const font, std::basic_string_view const clusters, size_t const width) : - _factory{ factory }, - _analyzer{ analyzer }, - _format{ format }, - _font{ font }, + _factory{ factory.get() }, + _analyzer{ analyzer.get() }, + _format{ format.get() }, + _font{ font.get() }, _localeName{}, _numberSubstitution{}, _readingDirection{ DWRITE_READING_DIRECTION_LEFT_TO_RIGHT }, @@ -38,8 +38,6 @@ CustomTextLayout::CustomTextLayout(IDWriteFactory1* const factory, _runIndex{ 0 }, _width{ width } { - THROW_HR_IF_NULL(E_INVALIDARG, format); - // Fetch the locale name out once now from the format _localeName.resize(gsl::narrow_cast(format->GetLocaleNameLength()) + 1); // +1 for null THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow(_localeName.size()))); diff --git a/src/renderer/dx/CustomTextLayout.h b/src/renderer/dx/CustomTextLayout.h index 73fa3a07c6c..598431b8b74 100644 --- a/src/renderer/dx/CustomTextLayout.h +++ b/src/renderer/dx/CustomTextLayout.h @@ -19,10 +19,10 @@ namespace Microsoft::Console::Render public: // Based on the Windows 7 SDK sample at https://github.com/pauldotknopf/WindowsSDK7-Samples/tree/master/multimedia/DirectWrite/CustomLayout - CustomTextLayout(IDWriteFactory1* const factory, - IDWriteTextAnalyzer1* const analyzer, - IDWriteTextFormat* const format, - IDWriteFontFace1* const font, + CustomTextLayout(gsl::not_null const factory, + gsl::not_null const analyzer, + gsl::not_null const format, + gsl::not_null const font, const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters, size_t const width); From 23b4a466f544edc5ee13f25c5b4575298b91863b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:41:37 -0700 Subject: [PATCH 123/154] C26429, C26481, don't use pointer arithmetic, test for nullness. Also eliminated completely unused GetTextRaw. Left todo behind for pointers as iterator boundaries in CharRowCellReference to fix later. --- src/buffer/out/CharRow.cpp | 27 ++----------------------- src/buffer/out/CharRow.hpp | 3 --- src/buffer/out/CharRowCellReference.cpp | 4 ++++ 3 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src/buffer/out/CharRow.cpp b/src/buffer/out/CharRow.cpp index f10fbfe13b7..8ac4ea217da 100644 --- a/src/buffer/out/CharRow.cpp +++ b/src/buffer/out/CharRow.cpp @@ -250,29 +250,6 @@ CharRow::reference CharRow::GlyphAt(const size_t column) return { *this, column }; } -// Routine Description: -// - returns string containing text data exactly how it's stored internally, including doubling of -// leading/trailing cells. -// Arguments: -// - none -// Return Value: -// - text stored in char row -// - Note: will throw exception if out of memory -std::wstring CharRow::GetTextRaw() const -{ - std::wstring wstr; - wstr.reserve(_data.size()); - for (size_t i = 0; i < _data.size(); ++i) - { - const auto glyph = GlyphAt(i); - for (auto it = glyph.begin(); it != glyph.end(); ++it) - { - wstr.push_back(*it); - } - } - return wstr; -} - std::wstring CharRow::GetText() const { std::wstring wstr; @@ -283,9 +260,9 @@ std::wstring CharRow::GetText() const const auto glyph = GlyphAt(i); if (!DbcsAttrAt(i).IsTrailing()) { - for (auto it = glyph.begin(); it != glyph.end(); ++it) + for (const auto wch : glyph) { - wstr.push_back(*it); + wstr.push_back(wch); } } } diff --git a/src/buffer/out/CharRow.hpp b/src/buffer/out/CharRow.hpp index b7f33a9eb52..e1b802d065f 100644 --- a/src/buffer/out/CharRow.hpp +++ b/src/buffer/out/CharRow.hpp @@ -64,9 +64,6 @@ class CharRow final void ClearGlyph(const size_t column); std::wstring GetText() const; - // other functions implemented at the template class level - std::wstring GetTextRaw() const; - // working with glyphs const reference GlyphAt(const size_t column) const; reference GlyphAt(const size_t column); diff --git a/src/buffer/out/CharRowCellReference.cpp b/src/buffer/out/CharRowCellReference.cpp index 1f320756bd0..caf6979fbe4 100644 --- a/src/buffer/out/CharRowCellReference.cpp +++ b/src/buffer/out/CharRowCellReference.cpp @@ -91,6 +91,9 @@ CharRowCellReference::const_iterator CharRowCellReference::begin() const // - get read-only iterator to the end of the glyph data // Return Value: // - end iterator of the glyph data +#pragma warning(push) +#pragma warning(disable:26481) +// TODO: eliminate using pointers raw as begin/end markers in this class CharRowCellReference::const_iterator CharRowCellReference::end() const { if (_cellData().DbcsAttr().IsGlyphStored()) @@ -103,6 +106,7 @@ CharRowCellReference::const_iterator CharRowCellReference::end() const return &_cellData().Char() + 1; } } +#pragma warning(pop) bool operator==(const CharRowCellReference& ref, const std::vector& glyph) { From 4204733c3471bb6492a02706516af483a6219294 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 15:48:02 -0700 Subject: [PATCH 124/154] C26481, don't use pointer arithmetic. Convert to measuring string within known limit and using view. --- src/buffer/out/textBuffer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 2533a72559b..cf37993f4b1 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1063,6 +1063,10 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo { try { + // TODO: the font name needs to be passed and stored around as an actual bounded type, not an implicit bounds on LF_FACESIZE + const auto faceLength = wcsnlen_s(fontFaceName, LF_FACESIZE); + const std::wstring_view faceNameView{ fontFaceName, faceLength }; + std::ostringstream htmlBuilder; // First we have to add some standard @@ -1088,12 +1092,9 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo htmlBuilder << ";"; htmlBuilder << "font-family:"; - if (fontFaceName[0] != '\0') - { - htmlBuilder << "'"; - htmlBuilder << ConvertToA(CP_UTF8, fontFaceName); - htmlBuilder << "',"; - } + htmlBuilder << "'"; + htmlBuilder << ConvertToA(CP_UTF8, faceNameView); + htmlBuilder << "',"; // even with different font, add monospace as fallback htmlBuilder << "monospace;"; From 6735311fc9754e60fc32adfaff86ea97e255f2f5 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 3 Sep 2019 16:18:19 -0700 Subject: [PATCH 125/154] Suppress last two errors (C26455 default constructor throw in DxEngine because it's due for refactoring soon anyway & C26444 custom construction/destruction on OutputCellIterator because I can't see what's going on and it needs more investigation and shouldn't hold this up). Also run codeformat. --- src/buffer/out/CharRowCell.cpp | 4 ++-- src/buffer/out/CharRowCellReference.cpp | 2 +- src/buffer/out/OutputCellIterator.cpp | 2 +- src/buffer/out/OutputCellView.cpp | 5 ++--- src/buffer/out/cursor.cpp | 2 +- src/buffer/out/textBuffer.cpp | 2 ++ src/buffer/out/textBufferTextIterator.cpp | 8 +++----- src/inc/DefaultSettings.h | 2 +- src/renderer/dx/DxRenderer.cpp | 19 +++++++++-------- src/renderer/inc/IRenderEngine.hpp | 2 +- src/renderer/inc/IRenderTarget.hpp | 3 ++- src/renderer/inc/RenderEngineBase.hpp | 3 ++- src/types/CodepointWidthDetector.cpp | 1 - src/types/GlyphWidth.cpp | 2 +- src/types/IBaseData.h | 2 ++ src/types/IUiaData.h | 3 ++- src/types/UiaTextRangeBase.cpp | 25 +++++++++++------------ src/types/UiaTextRangeBase.hpp | 2 +- src/types/WindowUiaProviderBase.hpp | 2 ++ 19 files changed, 48 insertions(+), 43 deletions(-) diff --git a/src/buffer/out/CharRowCell.cpp b/src/buffer/out/CharRowCell.cpp index 38da6a1720e..d8cdeff429c 100644 --- a/src/buffer/out/CharRowCell.cpp +++ b/src/buffer/out/CharRowCell.cpp @@ -8,13 +8,13 @@ // default glyph value, used for reseting the character data portion of a cell static constexpr wchar_t DefaultValue = UNICODE_SPACE; -CharRowCell::CharRowCell() noexcept: +CharRowCell::CharRowCell() noexcept : _wch{ DefaultValue }, _attr{} { } -CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept: +CharRowCell::CharRowCell(const wchar_t wch, const DbcsAttribute attr) noexcept : _wch{ wch }, _attr{ attr } { diff --git a/src/buffer/out/CharRowCellReference.cpp b/src/buffer/out/CharRowCellReference.cpp index caf6979fbe4..41357320f52 100644 --- a/src/buffer/out/CharRowCellReference.cpp +++ b/src/buffer/out/CharRowCellReference.cpp @@ -92,7 +92,7 @@ CharRowCellReference::const_iterator CharRowCellReference::begin() const // Return Value: // - end iterator of the glyph data #pragma warning(push) -#pragma warning(disable:26481) +#pragma warning(disable : 26481) // TODO: eliminate using pointers raw as begin/end markers in this class CharRowCellReference::const_iterator CharRowCellReference::end() const { diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index a709c1c3cea..ebc7e5e0045 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -117,7 +117,7 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const // NOTE: This one internally casts to wchar_t because Razzle sees WORD and wchar_t as the same type // despite that Visual Studio build can tell the difference. #pragma warning(push) -#pragma warning(suppress:26490) +#pragma warning(suppress : 26490) // Suppresses reinterpret_cast. We're only doing this because Windows doesn't understand the type difference between wchar_t and DWORD. // It is not worth trying to separate that out further or risking performance over this particular warning here. OutputCellIterator::OutputCellIterator(const std::basic_string_view legacyAttrs, const bool /*unused*/) noexcept : diff --git a/src/buffer/out/OutputCellView.cpp b/src/buffer/out/OutputCellView.cpp index 6380e1f7882..57a94498893 100644 --- a/src/buffer/out/OutputCellView.cpp +++ b/src/buffer/out/OutputCellView.cpp @@ -15,7 +15,7 @@ OutputCellView::OutputCellView(const std::wstring_view view, const DbcsAttribute dbcsAttr, const TextAttribute textAttr, - const TextAttributeBehavior behavior) noexcept: + const TextAttributeBehavior behavior) noexcept : _view(view), _dbcsAttr(dbcsAttr), _textAttr(textAttr), @@ -28,8 +28,7 @@ OutputCellView::OutputCellView(const std::wstring_view view, // Return Value: // - Reference to UTF-16 character data // C26445 - suppressed to enable the `TextBufferTextIterator::operator->` method which needs a non-temporary memory location holding the wstring_view. -[[gsl::suppress(26445)]] -const std::wstring_view& OutputCellView::Chars() const noexcept +[[gsl::suppress(26445)]] const std::wstring_view& OutputCellView::Chars() const noexcept { return _view; } diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 73624784752..0b2f461be4d 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -11,7 +11,7 @@ // - Constructor to set default properties for Cursor // Arguments: // - ulSize - The height of the cursor within this buffer -Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept: +Cursor::Cursor(const ULONG ulSize, TextBuffer& parentBuffer) noexcept : _parentBuffer{ parentBuffer }, _cPosition{ 0 }, _fHasMoved(false), diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index cf37993f4b1..d61d25d0bc6 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1002,6 +1002,8 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe selectionBkAttr.push_back(CellBkAttr); } } +#pragma warning(suppress : 26444) + // TODO: figure out why there's custom construction/destruction happening here it++; } diff --git a/src/buffer/out/textBufferTextIterator.cpp b/src/buffer/out/textBufferTextIterator.cpp index 7e7ed7acd24..c0cc3ad0882 100644 --- a/src/buffer/out/textBufferTextIterator.cpp +++ b/src/buffer/out/textBufferTextIterator.cpp @@ -16,7 +16,7 @@ using namespace Microsoft::Console::Types; // - Narrows the view of a cell iterator into a text only iterator. // Arguments: // - A cell iterator -TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) noexcept: +TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cellIt) noexcept : TextBufferCellIterator(cellIt) { } @@ -25,8 +25,7 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data -[[gsl::suppress(26434)]] -const std::wstring_view TextBufferTextIterator::operator*() const noexcept +[[gsl::suppress(26434)]] const std::wstring_view TextBufferTextIterator::operator*() const noexcept { return _view.Chars(); } @@ -35,8 +34,7 @@ const std::wstring_view TextBufferTextIterator::operator*() const noexcept // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data -[[gsl::suppress(26434)]] -const std::wstring_view* TextBufferTextIterator::operator->() const noexcept +[[gsl::suppress(26434)]] const std::wstring_view* TextBufferTextIterator::operator->() const noexcept { return &_view.Chars(); } diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 2a16dd88ead..22dd95d0f49 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -28,7 +28,7 @@ constexpr COLORREF POWERSHELL_BLUE = RGB(1, 36, 86); constexpr short DEFAULT_HISTORY_SIZE = 9001; #pragma warning(push) -#pragma warning(disable:26426) +#pragma warning(disable : 26426) const std::wstring DEFAULT_FONT_FACE{ L"Consolas" }; constexpr int DEFAULT_FONT_SIZE = 10; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 17d0a0976d8..982e666a8de 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -24,6 +24,8 @@ using namespace Microsoft::Console::Types; // Routine Description: // - Constructs a DirectX-based renderer for console text // which primarily uses DirectWrite on a Direct2D surface +#pragma warning(suppress : 26455) +// TODO: The default constructor should not throw. DxEngine::DxEngine() : RenderEngineBase(), _isInvalidUsed{ false }, @@ -268,7 +270,7 @@ DxEngine::~DxEngine() freeOnFail.release(); // don't need to release if we made it to the bottom and everything was good. // Notify that swap chain changed. - + if (_pfn) { try @@ -393,7 +395,7 @@ void DxEngine::_ReleaseDeviceResources() noexcept gsl::narrow(stringLength), _dwriteTextFormat.Get(), gsl::narrow(_displaySizePixels.cx), - _glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow( _displaySizePixels.cy), + _glyphCell.cy != 0 ? _glyphCell.cy : gsl::narrow(_displaySizePixels.cy), ppTextLayout); } @@ -749,7 +751,7 @@ void DxEngine::_InvalidOr(RECT rc) noexcept // First, set up a complete clear of all device resources if something goes terribly wrong. auto resetDeviceResourcesOnFailure = wil::scope_exit([&]() noexcept { _ReleaseDeviceResources(); - }); + }); // Now let go of a few of the device resources that get in the way of resizing buffers in the swap chain _dxgiSurface.Reset(); @@ -1244,17 +1246,16 @@ enum class CursorPaintType [[nodiscard]] HRESULT DxEngine::UpdateFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& fiFontInfo) noexcept { RETURN_IF_FAILED(_GetProposedFont(pfiFontInfoDesired, - fiFontInfo, - _dpi, - _dwriteTextFormat, - _dwriteTextAnalyzer, - _dwriteFontFace)); + fiFontInfo, + _dpi, + _dwriteTextFormat, + _dwriteTextAnalyzer, + _dwriteFontFace)); try { const auto size = fiFontInfo.GetSize(); - _glyphCell.cx = size.X; _glyphCell.cy = size.Y; } diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index d172607b3ca..2d321381318 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -70,8 +70,8 @@ namespace Microsoft::Console::Render IRenderEngine(IRenderEngine&&) = default; IRenderEngine& operator=(const IRenderEngine&) = default; IRenderEngine& operator=(IRenderEngine&&) = default; - public: + public: [[nodiscard]] virtual HRESULT StartPaint() noexcept = 0; [[nodiscard]] virtual HRESULT EndPaint() noexcept = 0; [[nodiscard]] virtual HRESULT Present() noexcept = 0; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp index ffc83f6e371..4fbba7fad41 100644 --- a/src/renderer/inc/IRenderTarget.hpp +++ b/src/renderer/inc/IRenderTarget.hpp @@ -24,14 +24,15 @@ namespace Microsoft::Console::Render { public: virtual ~IRenderTarget() = 0; + protected: IRenderTarget() = default; IRenderTarget(const IRenderTarget&) = default; IRenderTarget(IRenderTarget&&) = default; IRenderTarget& operator=(const IRenderTarget&) = default; IRenderTarget& operator=(IRenderTarget&&) = default; - public: + public: virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) = 0; virtual void TriggerRedraw(const COORD* const pcoord) = 0; virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 66f0ee1d594..eae934bc775 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -25,14 +25,15 @@ namespace Microsoft::Console::Render { public: ~RenderEngineBase() = 0; + protected: RenderEngineBase(); RenderEngineBase(const RenderEngineBase&) = default; RenderEngineBase(RenderEngineBase&&) = default; RenderEngineBase& operator=(const RenderEngineBase&) = default; RenderEngineBase& operator=(RenderEngineBase&&) = default; - public: + public: [[nodiscard]] HRESULT InvalidateTitle(const std::wstring& proposedTitle) noexcept override; [[nodiscard]] HRESULT UpdateTitle(const std::wstring& newTitle) noexcept override; diff --git a/src/types/CodepointWidthDetector.cpp b/src/types/CodepointWidthDetector.cpp index aea44fe9643..eb29a8af488 100644 --- a/src/types/CodepointWidthDetector.cpp +++ b/src/types/CodepointWidthDetector.cpp @@ -316,7 +316,6 @@ CodepointWidthDetector::CodepointWidthDetector() noexcept : _fallbackCache{}, _pfnFallbackMethod{} { - } // Routine Description: diff --git a/src/types/GlyphWidth.cpp b/src/types/GlyphWidth.cpp index 5efbaba2d9a..2a7d5113e6c 100644 --- a/src/types/GlyphWidth.cpp +++ b/src/types/GlyphWidth.cpp @@ -5,7 +5,7 @@ #include "inc/CodepointWidthDetector.hpp" #include "inc/GlyphWidth.hpp" -#pragma warning(suppress: 26426) +#pragma warning(suppress : 26426) static CodepointWidthDetector widthDetector; // Function Description: diff --git a/src/types/IBaseData.h b/src/types/IBaseData.h index cabcaf9a773..453dbae7711 100644 --- a/src/types/IBaseData.h +++ b/src/types/IBaseData.h @@ -24,12 +24,14 @@ namespace Microsoft::Console::Types { public: virtual ~IBaseData() = 0; + protected: IBaseData() = default; IBaseData(const IBaseData&) = default; IBaseData(IBaseData&&) = default; IBaseData& operator=(const IBaseData&) = default; IBaseData& operator=(IBaseData&&) = default; + public: virtual Microsoft::Console::Types::Viewport GetViewport() noexcept = 0; virtual const TextBuffer& GetTextBuffer() noexcept = 0; diff --git a/src/types/IUiaData.h b/src/types/IUiaData.h index 7b2ef08b1b1..6fbb2f513ef 100644 --- a/src/types/IUiaData.h +++ b/src/types/IUiaData.h @@ -24,14 +24,15 @@ namespace Microsoft::Console::Types { public: ~IUiaData() = 0; + protected: IUiaData() = default; IUiaData(const IUiaData&) = default; IUiaData(IUiaData&&) = default; IUiaData& operator=(const IUiaData&) = default; IUiaData& operator=(IUiaData&&) = default; - public: + public: virtual const bool IsSelectionActive() const = 0; virtual void ClearSelection() = 0; virtual void SelectNewRegion(const COORD coordStart, const COORD coordEnd) = 0; diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 1ac0ed5ca46..bf0418e9b73 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -290,7 +290,7 @@ IFACEMETHODIMP UiaTextRangeBase::Compare(_In_opt_ ITextRangeProvider* pRange, _O _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = FALSE; @@ -352,7 +352,7 @@ IFACEMETHODIMP UiaTextRangeBase::ExpandToEnclosingUnit(_In_ TextUnit unit) _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); ApiMsgExpandToEnclosingUnit apiMsg; apiMsg.Unit = unit; @@ -428,7 +428,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetBoundingRectangles(_Outptr_result_maybenull_ _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, ppRetVal == nullptr); *ppRetVal = nullptr; @@ -499,7 +499,7 @@ IFACEMETHODIMP UiaTextRangeBase::GetText(_In_ int maxLength, _Out_ BSTR* pRetVal _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = nullptr; @@ -602,7 +602,7 @@ IFACEMETHODIMP UiaTextRangeBase::Move(_In_ TextUnit unit, _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; @@ -674,7 +674,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByUnit(_In_ TextPatternRangeEndpoin _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); RETURN_HR_IF(E_INVALIDARG, pRetVal == nullptr); *pRetVal = 0; @@ -741,7 +741,7 @@ IFACEMETHODIMP UiaTextRangeBase::MoveEndpointByRange(_In_ TextPatternRangeEndpoi _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); const UiaTextRangeBase* range = static_cast(pTargetRange); if (range == nullptr) @@ -843,7 +843,7 @@ IFACEMETHODIMP UiaTextRangeBase::Select() _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); if (_degenerate) { @@ -886,11 +886,10 @@ IFACEMETHODIMP UiaTextRangeBase::ScrollIntoView(_In_ BOOL alignToTop) _pData->LockConsole(); auto Unlock = wil::scope_exit([&]() noexcept { _pData->UnlockConsole(); - }); + }); try { - const auto oldViewport = _pData->GetViewport().ToInclusive(); const auto viewportHeight = _getViewportHeight(oldViewport); // range rows @@ -1168,7 +1167,7 @@ const bool UiaTextRangeBase::_isScreenInfoRowInViewport(const ScreenInfoRow row, { const ViewportRow viewportRow = _screenInfoRowToViewportRow(row, viewport); return viewportRow >= 0 && - viewportRow < gsl::narrow(_getViewportHeight(viewport)); + viewportRow < gsl::narrow(_getViewportHeight(viewport)); } // Routine Description: @@ -1513,8 +1512,8 @@ std::pair UiaTextRangeBase::_moveByLine(gsl::not_null 0 && moveState.Increment == MovementIncrement::Forward))); + ((moveCount < 0 && moveState.Increment == MovementIncrement::Backward) || + (moveCount > 0 && moveState.Increment == MovementIncrement::Forward))); if (moveCount != 0 && !illegalMovement) { diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 3028dd7f0f5..aaba89e7911 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -303,7 +303,7 @@ namespace Microsoft::Console::Types static const ViewportRow _screenInfoRowToViewportRow(gsl::not_null pData, const ScreenInfoRow row) noexcept; static constexpr const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) noexcept; + const SMALL_RECT viewport) noexcept; static const bool _isScreenInfoRowInViewport(gsl::not_null pData, const ScreenInfoRow row) noexcept; diff --git a/src/types/WindowUiaProviderBase.hpp b/src/types/WindowUiaProviderBase.hpp index 2a4634a6961..3cd68b1a2fa 100644 --- a/src/types/WindowUiaProviderBase.hpp +++ b/src/types/WindowUiaProviderBase.hpp @@ -33,12 +33,14 @@ namespace Microsoft::Console::Types { public: virtual ~WindowUiaProviderBase() = default; + protected: WindowUiaProviderBase() = default; WindowUiaProviderBase(const WindowUiaProviderBase&) = default; WindowUiaProviderBase(WindowUiaProviderBase&&) = default; WindowUiaProviderBase& operator=(const WindowUiaProviderBase&) = default; WindowUiaProviderBase& operator=(WindowUiaProviderBase&&) = default; + public: [[nodiscard]] virtual HRESULT Signal(_In_ EVENTID id) = 0; [[nodiscard]] virtual HRESULT SetTextAreaFocus() = 0; From 7d9534bfa88844315be1eee8ac721b842f0d4bd9 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 4 Sep 2019 10:59:18 -0700 Subject: [PATCH 126/154] constexprs have to go into the headers or other usages can't find them. Imagine that. --- .../ut_app/TerminalApp.Unit.Tests.manifest | 1 + src/types/UiaTextRangeBase.cpp | 13 -------- src/types/UiaTextRangeBase.hpp | 14 +++++++-- src/types/inc/utils.hpp | 31 +++++++++++++++++-- src/types/utils.cpp | 30 ------------------ 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest b/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest index ef9516047af..a447bc8fdc2 100644 --- a/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest +++ b/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest @@ -9,6 +9,7 @@ + diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index bf0418e9b73..929a8c8e612 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -1086,19 +1086,6 @@ const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(gsl::not_null pData, const ScreenInfoRow row) noexcept; - static constexpr const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) noexcept; + // Routine Description: + // - Converts a ScreenInfoRow to a ViewportRow. + // Arguments: + // - row - the ScreenInfoRow to convert + // - viewport - the viewport to use for the conversion + // Return Value: + // - the equivalent ViewportRow. + static constexpr const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, + const SMALL_RECT viewport) noexcept + { + return row - viewport.Top; + } static const bool _isScreenInfoRowInViewport(gsl::not_null pData, const ScreenInfoRow row) noexcept; diff --git a/src/types/inc/utils.hpp b/src/types/inc/utils.hpp index 11cc46b3b69..ff1c8dd1781 100644 --- a/src/types/inc/utils.hpp +++ b/src/types/inc/utils.hpp @@ -15,7 +15,19 @@ namespace Microsoft::Console::Utils { bool IsValidHandle(const HANDLE handle) noexcept; - constexpr short ClampToShortMax(const long value, const short min) noexcept; + // Function Description: + // - Clamps a long in between `min` and `SHRT_MAX` + // Arguments: + // - value: the value to clamp + // - min: the minimum value to clamp to + // Return Value: + // - The clamped value as a short. + constexpr short ClampToShortMax(const long value, const short min) noexcept + { + return static_cast(std::clamp(value, + static_cast(min), + static_cast(SHRT_MAX))); + } std::wstring GuidToString(const GUID guid); GUID GuidFromString(const std::wstring wstr); @@ -28,7 +40,22 @@ namespace Microsoft::Console::Utils void InitializeCampbellColorTableForConhost(const gsl::span table); void SwapANSIColorOrderForConhost(const gsl::span table); void Initialize256ColorTable(const gsl::span table); - constexpr void SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept; + + // Function Description: + // - Fill the alpha byte of the colors in a given color table with the given value. + // Arguments: + // - table: a color table + // - newAlpha: the new value to use as the alpha for all the entries in that table. + // Return Value: + // - + constexpr void SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept + { + const auto shiftedAlpha = newAlpha << 24; + for (auto& color : table) + { + WI_UpdateFlagsInMask(color, 0xff000000, shiftedAlpha); + } + } constexpr uint16_t EndianSwap(uint16_t value) { diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 536cd40d25d..1a2dd178877 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -6,20 +6,6 @@ using namespace Microsoft::Console; -// Function Description: -// - Clamps a long in between `min` and `SHRT_MAX` -// Arguments: -// - value: the value to clamp -// - min: the minimum value to clamp to -// Return Value: -// - The clamped value as a short. -constexpr short Utils::ClampToShortMax(const long value, const short min) noexcept -{ - return static_cast(std::clamp(value, - static_cast(min), - static_cast(SHRT_MAX))); -} - // Function Description: // - Creates a String representation of a guid, in the format // "{12345678-ABCD-EF12-3456-7890ABCDEF12}" @@ -447,22 +433,6 @@ void Utils::Initialize256ColorTable(const gsl::span table) // clang-format on } -// Function Description: -// - Fill the alpha byte of the colors in a given color table with the given value. -// Arguments: -// - table: a color table -// - newAlpha: the new value to use as the alpha for all the entries in that table. -// Return Value: -// - -constexpr void Utils::SetColorTableAlpha(const gsl::span table, const BYTE newAlpha) noexcept -{ - const auto shiftedAlpha = newAlpha << 24; - for (auto& color : table) - { - WI_UpdateFlagsInMask(color, 0xff000000, shiftedAlpha); - } -} - // Function Description: // - Generate a Version 5 UUID (specified in RFC4122 4.3) // v5 UUIDs are stable given the same namespace and "name". From 21067a7629d37d714309ed2c10bbe0c70565fe89 Mon Sep 17 00:00:00 2001 From: Rich Turner Date: Wed, 4 Sep 2019 11:21:39 -0700 Subject: [PATCH 127/154] Fixes #1918 - Added docs for image/icon settings & paths (#2545) * Fixes #1918 - Added docs for image/icon settings & paths * Described URI Schemes & their use * Added guidance re. background images * Added notes re. icons (inc. sizing) * Added example JSON & screenshot of background & icon --- doc/cascadia/SettingsSchema.md | 47 +++++++++++++++++- .../custom-icon-and-background-image.jpg | Bin 0 -> 138399 bytes 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 doc/images/custom-icon-and-background-image.jpg diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index 2d5d32bf8c1..c32c5cb3026 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -43,7 +43,7 @@ Properties listed below are specific to each unique profile. | `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Colors use hex color format: `"#rrggbb"`. Ordering is as follows: `[black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]` | | `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. | | `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. | -| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. | +| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. See [Images and Icons](./#images_and_icons) below for help on specifying your own icons | | `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` | | `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. | @@ -133,3 +133,48 @@ Bindings listed below are per the implementation in `src/cascadia/TerminalApp/Ap - moveFocusUp - moveFocusDown +## Background Images and Icons +Some Terminal settings allow you to specify custom background images and icons. It is recommended that custom images and icons are stored in system-provided folders and are referred to using the correct [URI Schemes](https://docs.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes). URI Schemes provide a way to reference files independent of their physical paths (which may change in the future). + +The most useful URI schemes to remember when customizing background images and icons are: + +| URI Scheme | Corresponding Physical Path | Use / description | +| --- | --- | ---| +| `ms-appdata:///Local/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\` | Per-machine files | +| `ms-appdata:///Roaming/` | `%localappdata%\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\RoamingState\` | Common files | + +> ⚠ Note: Do not rely on file references using the `ms-appx` URI Scheme (i.e. icons). These files are considered an internal implementation detail and may change name/location or may be omitted in the future. + +### Icons +Terminal displays icons for each of your profiles which Terminal generates for any built-in shells - PowerShell Core, PowerShell, and any installed Linux/WSL distros. Each profile refers to a stock icon via the `ms-appx` URI Scheme. + +> ⚠ Note: Do not rely on the files referenced by the `ms-appx` URI Scheme - they are considered an internal implementation detail and may change name/location or may be omitted in the future. + +You can refer to you own icons if you wish, e.g.: + +```json + "icon" : "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\icon-ubuntu-32.png", +``` + +> 👉 Tip: Icons should be sized to 32x32px in an appropriate raster image format (e.g. .PNG, .GIF, or .ICO) to avoid having to scale your icons during runtime (causing a noticeable delay and loss of quality.) + +### Custom Background Images +You can apply a background image to each of your profiles, allowing you to configure/brand/style each of your profiles independently from one another if you wish. + +To do so, specify your preferred `backgroundImage`, position it using `backgroundImageAlignment`, set its opacity with `backgroundImageOpacity`, and/or specify how your image fill the available space using `backgroundImageStretchMode`. + +For example: +```json + "backgroundImage": "C:\\Users\\richturn\\OneDrive\\WindowsTerminal\\bg-ubuntu-256.png", + "backgroundImageAlignment": "bottomRight", + "backgroundImageOpacity": 0.1, + "backgroundImageStretchMode": "none" +``` + +> 👉 Tip: You can easily roam your collection of images and icons across all your machines by storing your icons and images in OneDrive (as shown above). + +With these settings, your Terminal's Ubuntu profile would look similar to this: + +![Custom icon and background image](../images/custom-icon-and-background-image.jpg) + + diff --git a/doc/images/custom-icon-and-background-image.jpg b/doc/images/custom-icon-and-background-image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..540a8c7c7e2d3c7e4aa3bc6ba0320c5efb800b0d GIT binary patch literal 138399 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�MXe-7oQ_R zf;Tw)CLuZR%bnftwccJ1O|R}&NZl{wahFW0J1z}GRXDaV;Aqo8;X)%j9=vxv0+Ut7 zy0O8ZXg*Viex8tX;C*!<;*K(qgciE|D{F$aczCw5`5F~;{0upX2kqZ|uQ&v|z`pFag=Xjd z37rYqZljFFyc*S4KY1<>%K@}+x6rN&Gmk53)24*)1ld>TB2y^Uf5uG1RUJ zNLqhX4Aa@=TD-FQ>59TeE9#H*45s?NbexFS@=l7A_Mhm={D<0?oW93qbL1PB4t{BU z@#0RQwgEi;l8xTnkw&g}kfknprA1@{Ztbzyd}|G}q=-3=V3`H z^L>smiR$AMdqBnVs8Tf~_UC*m2M!f8D9wAo)!EPUsa|2IgQyMb5-Po2oR@?~3(XDc zGJDcQv_0()MukHZM^}>?Tap7*teeM<*_&>s`Pf`;8CM27Ryd3L^TKYn3;Mm7{#?-3 zt5DD51A+~VKJxWQ%C+WSU3`=G zK_K;0O4GBIsZnBLg=2bU*HT3}B0mBeZhkvgha97q%Z@v)wKdyg*p@ok!6;q$SB+$~ zyK?1cRNnB4g6zWrnLwT$fi{ah{Fjd(g1+r6h1n&z9$$vgj5n9?S!uYa+siDh>Pht| z%2Xw&+}_VSwb>Awc;!A3rhGSxt14%VK^&h|b66?s4XxUm016EG9~9Z4%qVbTk5|?c{A1|RhITAsTE5;_nhWy-rBhbPZ|))Fv3UO ze~R~_lwLB*2RUXNWaRJWGglP3E(_`4sD~wpoy^NLXFvN`FTv&MJhHhw%NbLm z{?*UYplRrJqS;nIU#0#<398#<>3JG9rnsfXz&%z-sqW&GylhA`BTJ-luFF&EqkoVR@>xp3WNjJp)u~RP8oTBz^DX%F&*2lfQ=>ZK2y@CLJSFGIuMd zm%ns6Xwy;vixO6BUy=fF15#e^VU-? zlKfoqW!PobB*>)f*A#9qZgp7Myx^~HHm3LGIK^=KV*9R7lzxWg`CYpFC$`nk{(x(} znZqr{!n#U^)#)vR!h#33eki2JncszLt7;!dbL->7GqLys6D;`yOxIig){=THv*5k$ z^UtsNyq!9~Tn+CP7!W$zPUAY@d0HoB=W*a)nsHJx@ba&nA1_!~jQLegMzqxk?4VZE zx>wddb4aixpbl)SY#mVBtmooZ6kRL+g#)VeBO3|avG7>>|8M}a|Npo~DIWfhlZ2>d zo3xE3Tn#(WjYI^wcz~j+@Znp!>gt{-KTSGcLDSc*b$ODD>nn2;3A)S}xZpry z65yc#XfF&v+>?z!+sJoZ(Vc+14oEIX#PR#4X7Zg^Q& zi8?_zIxFXADog5$@p}xDr-ALwQGw7z;LDidb9zOfMvroZ>M7` z@dyGE+hK*J>m+{B(Zx+flyP$jRewCG%!5EAd%GoVOq|k2x{TJ+9{Ucx3F9eS9pnuj z1XqGP5ElmBmiT-PCYp5R_g=5h=%+S+{2c7f+~LMRU~Y%fAE9E=S7kCSd9>1>|EU^B z1`ga7Si6(t0i_lhDvnIKO-`PT=QqBWb{`o7;1xkJ!Ga{yrUq=z_$e&52-;h5E5dG8sRK#x)d)WS7jy;YjjYldflm3S?@yOqQrSA#m0Ha3v*FCuV<_J+?7TnS z91mS^!b(J<-7b-QaiX2{LK zIHOV#n7Ru3-4gNs?;vv*C#Tm6;#5cRK-c|EFfs$Iuy@2m)-z5sL}x5!$Jxe}2|3)3 zX)h4OQvJP8KRQIYPB?z_!&+JmSA%kJeZO5>J(?TGA^ch72k*qLHZ+kFhL zmNKBXO5hp-cl$kzjOh<=D2N%^an_+8kEQ*LpXlNjs2S(!WT#wji9dbxByDI`Aw6nS zXWbWU0oXe(Nks1M*^lN=?B&$ehkdWFOWMFdo*rqueADNypzL9l@Ekjuycvy>1YTf48_7f0RJ$jIZ_hIU(m>^;TEaDIxi$R-+*2 zwz=QW_0SDV?w0HY%f{zf>_gQ`{ub81CMGsSiVpS*j6mtK2Ps(DjWvGE`|sbxZhiLl zX>4#RmiG?^=u$jnBlg@=V5%Jdt=8_xk{iPqXhr#BZ3Y05oz>}U z!H#Z{4>((20}#Cc(}&pvRF@oku|qx~?P)mM@5vWU-p`VgTNPFgj>-T6-q2Ehi;v&O zGZgYlYGx4~{Bf_5ce)IpmPX{?E*4c#FO`6CyXZ9fG)kCnVoEsBn0uS1m)w6nNn=o= z6FgE19Ryp{HDu+=3|sXV{N8_(c`I@C<*22rk_=En;= zpE5t(d6cUQQ8o51n+4e5<;T8U?O0qE61w0uZ6U@pTPovb*-|@NPOdL0x#KDuh!U5* zDMM6yLCtynGOh=toiB4Nacx>hsmjBlU&AfMv$u68spy8g@3ZA7ulv^?8D`%Sc~l3dwDpepi)QTw8cNu0$8gO5JRkm{iAxgs(QZP12u zi~bd?=9l?&9~Xyyoy>XI?cAIY*`$^;Y2$R?rqLhfRB$uz8_dmXgRif!FN&2{f$qaa zg~E;(?sZBHSDvk;x}j*pPijjOS?zOz%Jr0%YgIrVS7U%z+%7;vwA zGOf3E{rNf1Kd-XBICF}N--ucYUmX+dJ~Fv3$IQh)w)0Z@wVm6mWpStjlh*!hV8h%l zvBe=cPRR_DbuCUcNMb<&bkT zs#Ba?93CB4|0K>;X4$115w>*qnsUd?k2b4<#uL=E7Ts}}ORyoYv7kW%Gg+9y_-qMq zFHhv6yP6vBuOiQh&@Kp~bY_0aV&g#2h4Hi94YLWZ-d@{XNZF_L^t4fcPeJpxwg^Ld zpH;bmVU_p}>x}VOb$P<*y%vnDk%7~JV^U+KX@GTAX8DEOBMMUDi><#5Bbo3@#N&v~ zzQ5BVB@Hw6>OiRJU0;%almoriZ>E6Hwx4nO5`&&kY7-3#W?uYSrk#HK@%!?x?7%yM zyU0Kr;srP@$JK!W*31f+kWaGS00zI?$hw@W^~EkS#cqX7K~`+8xNyFL+(KD`A$@Naf4N8Jy;X_urZPG^YdYM6}4C0DxS;{U=#WJHXNg;`f>V4}r)4)sF6>q~6f z@Gi|Jn~v!9&uN!C_HU)Tm2Bd#$gpRW{*tT}x|yD$zv5C!^i@6`?ou|^cTAIzSFW!| zq-7k}rfhqXHEFh+lXCej=Vpo~_FOC|cIFb|s=r~%`2DHls=#=8L?C~ijHR>bsqG0S z=x}_u|1I{XarqHj9Iwy)I6u>TlV_h*x$el`K2@2pZp_pU=g*bbd#WelHC%aQDLKwB zJX%G5BL?rs7f0$nE1Sw$=cFt1+f><7g{)*~q|EHu|6beFx0>*V&+|pMcZZpKTD-|j zhV}Mo->HYxBEl@oh@TI!RZb?}*Gs?0H3(+<=;Up~x@N~_N|hiMUo z_lKIQRBZ8g$ng|mfEA^RKaD5{2aS8FfNcQ6Xou$y_F@KSuDf<&_mzVQ zK9^DOYi5F==FSZwABnA!{M{QF31))(FBF$Pck=X$SVTsHjB2YI6g%IfhO6YHh;~1C zP4o7Fa^8CXzT;_?lqM_2y1fdyIrjBAbfZURB;T1@@Z;n_jqsF!zvjichd89+H?RjksI>2b68wY`?vVzUCDBh5B60*qCIV$eOH3tpf& z8({$Fh-}3XP&z@H?Gzr|M4UyB=~(HQZ}{8U=k8r;=kGk9i49G(aUmK4N?&l3F;O!3 zH-ZN6N3%roKSZhFP#v`m)mdo{dCG5`?zoCwg78*elE#<+q^BJWpu&n;-=`4dCrfT+ zI?V5X7pvV0?F#ZIssdTvY=YwU@NWp3#K&eNmyxPzM9e?;fYDSBuMMpv>E3q}!RuEm zMNuUp{`{Pz0xPV?KTu}C$C5vj)Ugcs?>L9;TDL+(yLu4;;FIS*?`y&yi^Q<>NJc&J zSovVxAP!k)JM>KeFNA@rk?qJZQ|Q%TQMfrleX-PLqQlcA4!dWH>xEv0xUg!NPC5UO zY7Cl>Vi}uBv6s`mn6XllsMe86dHM1@CJ=#fvcCOTvLAjE-@}wabaCyDMV7^ zT}k50o!sA1g}i1ug6am#>yz4&iy_aA5?imwkNsr_RT{6%v&aDIqzS;G0G};k>wL6#JIJ!8Q5) zM5H68#8-8;xSQc#FO8sgEpSytbT)Q3GbmSAXf4-&(Meg;`(^64n^hwzvua%CZT@<{ zPF^Tnz6V(fQMm2(#bm8}*|2uRRj{V|m1Xh!uvqs>2S@vE)*2@12|7N}f}vnqKcXc; zrv&cuc5a-HPJO?#V#d0t-`&P%wqAV+;Si=V87{EVc+*u z!J@YiDtBqqCOe$zl5fI}m=~Ilq%~1p?82ML69hHOT2q3DsH!JM+EDS@hKPQ#+K5&h zitCAXS#}f7`1AT^>snUGy5Q&$Ys{4$%HF!+_mu(AWRWCc_)!U=gE{+IwNaYWQ6bliA3Z=3j9`JsIx?yey0fXic$rqszq3J>q~lFL=< zHB^cO+k>MhGFWZ6JN9u+Kv!0Ee&(vthpV&?#6aa-8#dxd2DlE+b$`BN+=BgE(X6?o zX=Sy^$vuCC4QT4J)Gs^bSNRu7Pfjtii+Un=GrPf8l_kM(x3F*g=ylmS?WKzKD6if= z!KJpMYzU?})S`ZSzY>^z7B1H~+Vo(c#n|%gkEY<}`x7?Oce0#PNAiv)am$e@JbCU& z7TIjiMQhvTy!7)vQgS7=#dd;i8CRR&>3dy(A=uI_>eQ{9dGZ+VjT?JkUTN)Y`qD1; zUHBEtb=9N4i~AXVPYxJG&2uIeV3A#cuLW$78LHHDXH;W)YWus#sHVhK|K!J>Bw3%K z7FW^uAKWHZ{Gu;=_Fi%0in)0L-h9y9vXm1g$*vpC{28n5(Qc+z-~{+BCho>DX!E#D z%;h{ix5I|SP(9D*vWPcU08UaO5!cwQj(CA<>s(p!MNzhbbUq$Wyw9Jz;BWA%7ho$op8HAAOTNS9Oky$>W{wOucLl{8zWqJzo0I--?uGQ6t*moXNNl)#_!R z`2fTzDf5gSA zfH;@}e(ai| z26Z1<`Sn$;%L~ERgY`cM_IC}M5dfPdIS#Il;{mkm<&4NqUAk>BuS8kPT`L#K%?}09 zW=D+Tj?bAaxn8hMU(tK?nL73gbsIpYP&Ls$k1>z{43liZ=eHUVBr#D`JhOm>M5ic} zag#eD{B@uD3tnGSxmxhwJcZCCp~siIAN-Ps1?wy&>~s_c4}yL`cOK#@@y-!#s+V!^ zK~=g9W|NK$VR`i~=l2|P)V#f6Ywd{x^-Uw_UTU)%S`Ei@6KxU^KF})Yc3dvOmqMRd zO1g?0Y%f(>oOZThBUC#q{oIB>{HpaQNt50${+TVRNcs2YdXUU`_OfON>KzgbmAC@;!_`GR#STUQ+YTpL-A9%_*9vQNH9gYhRumt|$9jvXxJ)A! z3g)r8U``U8{JrHmqU}KwBk?Io7AM@^9Kenfyq{0HH562)WE`eH`7$PH_=UCxM?wSB zNCp+LTJ^S{)bEWWA~F_zVH(c`efymge&g~u$oYQ6hwK-+E&g@0DHvX+cW&v)(7U83 zmTjt$oAB)Ynme0bU*luvBsP@o&sFpK%`^Yj z>mdOgB=(6OZdPzPvXd;;uZ2yBgfpY&9Hrn=?(_w>uz>Y;JfXvFpHQqmnr&%WoCx!DjRIqA6IOU4odhml zr0(-*JXcCGA*_G%o2PJGZNgy-LMcabD6cUvc0boonvct_L!G&U#Uc4Mp);ye8kNI}P_CrKjytB8yHX~vJrJN`R>%5w z=~Fw+Nns;mf+hR_pDavIWR*UT8N_KRjverVuA1yUz#KK@93-5U#`X>4ZY>X9XQS{^ z{_t})W4pc#cA_HaMJ`lAM?n+hEqk~HF1S=pExeZm+?4-;fFu@hZcyhAP5;M!P9et{rI##~~4 zSMD^M?Rrx{N>ka_h2@K}Gl?VhUzz9&w~)9k5E%{t5x&vir*@uKlGO7NpsgPo3 z9hKMpUPUYWhH6H{jxWq!M>tH!eb#wKvdoX|b8P=pl%hcXmdQ0``t@Y#!1#TDW4G#6 zsph?shj-zt2^LW)m4HwD(U#WFa5X`{bx_zn6yKd01}I(dO-B$t*ohI;%`YSs6t5!Z z_{wZ`(sBUu^fJEJnJh*a5u^x_Blf0su(@SC(Zd*LG5d91Y< zr6IUcXYs2jD%!=g)&24#m6N5GV8BIVkCh~f?G)~2uc=V;>g@2QPe?>J9-{jE^i@%j zc#ou^tZ~&?TT!c%jv@bqm%UmPVaDH3Rk&I<#H~ST#6Yy5+iL&9jJu z_Z5y{=zC0C@W}2HX|#PxfmQ6WO!DZcE1>M9|8sHn2jph9>Qa@ot#t@b5f_psWB9s* zGwjo3GN$Rx$U87OSoe%7o4dKB;yCT+YUkmt@6EO!|F2k;0B;tNtlEJpTS}HKz~mK1 zW%2GkaJSJq9YgfGrUB0p=Qq#T=-MAzimqeVsp3=4r!Jm9EcW^7*;D#LT51Jo%v?#1 z)pIdVNL0e)e65{lr=-vzO<}?X^Ns=Zo z_!6}#f7rp7WQbw$o5aWU8AIu<=ghvRs;_TWpYmbHbq(7=6=CRhYkST^L?pM70cKWr zjomz(r-t|1!=GJxqHNr(yQgAIC1PMFbF1U|$xLm+H(Uo^d*sL(v}F7_U_IO|FYI}i zdbAiWF|4(8tfeI%h_YV7j527pX@Q2)f zyO5x~0As1izVcQIX8sb~E*m-ve}Q`htIBv>QxQM=!#24Lt7OaP(%)pa3}*LBYlBW< zQcmYBCc7E>1yb#TXI<6_Oh~y4O0Nec_+pLAEwjwY#dfk4P65dpy~jvCx)Zi#0Btu{ z_-!*_J>qw$T?ME8)f42L04%ssomeQ<()N?Kw;OXLCXD~YZIkYnhv)#4McTWs_0NAir}< z;?@CTQ4Ow`5DVPq_M46pw`C#*thuqVptm*v7A@qj#2HnO%RpfoQDGW0aN19yK6GE! zGs`7^2}2xTIs*XiVaT4HWr`L$q-W%zD|lbb$sF!8*u z{h^TlNqpgqs%-v99GD6h^3SMxGp@j`XQ%;Co5P~y;V_UmrnsDBF$2^&47La*u>om0 ztjZFOk#nY}TYyXOwgiP9K}wf_vhC+n#CpQeSd)2@wMwwaG986&B8-BZf&p|dqeRdi zxCuM~JxzOo_~#Ddl^#U<*tRM!0g5~g`zKFbFzcW`!8Diy&}i5qa9)A`B*^VX{%_6V z+@f>bgMk!VB7A-CB(F*UpU$_BcY#GCS2+B$0X@XKkf`$pc>V( znR}q7B}EAP7ecN{IT&L7uf5t=ycjjWcK~s;DLg9KGYJT5FM-k=h@M6sG3o$3y$Dj` zCg5icJQk_~C!q6SirHgKA}pM(bkvmWX*YrTO+)H$c(hF#Vqw&@|#dRij3bKA@L?jln3A<{xw?a0NE& zX8J}x;5s&sqBINRzwP|zxsoCaoreBT62$+}7CPheuQu@LSWS|Xsl`Qw;q7Vg901mg zbJA4BORt@la;z-uoKfXoJ)^oRO39f7k*-Qm%oxjnLT~j@NJw1H{bP`L0x)?6gHMkz zb=j1!KY(#02DJGRpan<)1_hj%auGrLp}#}&BqzK??1TbzWllKy^)K8&27o*^(c=lH zZ##fi16|)sM|zApOp2{GV%UVZ3Qe8T_Io#T%c|0m-6v_+a9u)^PFNnJ~gZTixNW6-)*b79;XaJ z0yk0=AS#Bk?dNyDE(cC)`YYxBNs7*1ee{3H!*{OG1=s`u$ItvvXHeykvyv&&;3 zgfoyu_m#r5*#S$Rsbho&Ak}cA>s};D3ysGg8h8)$UY}o}vAjb+p#+*7CK?W-T_xye zH?9gMi)X%(>zm}f_lp^qV+zJc6Fg5phq(z3$bwQ4&7ejXfc~$w@XoGsk%X_n@LC^T(qmKLq}1;)0(|^--^d)+v+av-&YO2s`)AX#vd+H^%v-Fpia_<&)n* zYU`gGz_b_GVwcgWp({N^(JiaX{=#draf;f;&Z<#4XaeJKg@P79wbx&=n%pmKP zQPc0w(7z4=r>a&8Esnjp2)KDX?~71R7)dd=dc5$mr?S0+{nakcS2sfMPA$albeZAi z0rVFeZrKpK!p(#%G{@NdsHh$LCaoq^^;JeU_HC!YWnSRw(VNxIDUzPKftqRSL31r} zAT>?V)&+tCLwb?3aS*C0*O!CNn|0Z%bl{RpV>9X-iuiED0=NjoQP-lUBbcwEw!AXq zx0csY#4+#mP!14KROKYa(cGU|wo0LVEyiVP?mt_=cd3Xn2Fgw2bhp!&o z{?!{L)Te6*LxWWq%Rjoir@|uQIE83)IotxkO|dYJAOZWs@}K_?V{aMMX4t*^($W@a zky4;Iw8g!+Lkq==OK`Uk++9j>hXN&7aVJ=CDQ?9j5ZoOSH0XQs{?CUqXYbiF`&%+| z&*YgWdG4%xt>1NB3xZV9myqWVfIXq*KJK zEo5?!q@Q&Py#rSO5B+|jD0#}5OLYpDOGX`mwh;Q_GZI^c{>8Huw(vYelR-m@k`a@7 z?Oee;ldb*kjFq))lFIAs!$QJDUkeg}Cd8=v)MZSS@k}3uaUs~hg60p{{ojeB!Uv)j zEyASO*w|@pB9L5Jw#u8q8EtTt@;Ut$P;!-1Tk!ad+JV$7(0J)LlWKYxPw{4p_R4VS zM&pGdB$uLgLpv^jM1>Zj*uXMd^>Qr0@i-^vsKLq0{Q*)TH)$@$@y}T}jEa$oIwG{p zy>L%Lcg(fRhOw-%cy@DIp(x0wI6K>#*O$XOME8C7atF6|lJv4Yd&~sV25Tw0bn0hK z!$;-;9&LvAD|08KNd1$;HRaHIQ33;|(Ndif**IPE@~JF%vWB`=?wO%a5q(x6glsL{ zl4IG=3Cy(6B`6xWUwV3s`+f~*@Ox3o)IuzGBxGs*Qa%0ex8(;ONGV|nYIhGFsFHy#H`H0H+*U}lu%kudiJIx{Vu zH<+vozTBY^9V~43FdgV^b)%9tXn%1>F6_vfT*XKlPXuqi%*gNCZV`=&9GBhBi(hoK zft+Hi$Jx`q@8Ujrf{CMTSFGvjqg9sUJL*`PC@j+u{5^)!p9Z>3M$Hw$E!#rBHBwc9 zSPsy@>u5kcc>k;_4oO31z2J}MZ!&MI$alK=YsYt3GtSsfq1(t+DRXtlZcN-hE1g1g zaj^CDj_^aQU`k7q8_MFU+bpLpg4bi#mTUCh%btarZ0+;(^l z92&v*tE{nb^UyC%(J+3wF12A-af@Nf@!~anlfs zQI^0vR>CqSMTFtV+ho_>ohGHa5NnIrCv9jE@Dm=Vk(73AnykC-!%-MK_=k$m(XmfK zxHI8^g_STfKlX@D83S394QQpJ@XB|;iLN&*S-8_hqjHX%m2Xc%|LUX4yX2UiF1~^> z;;Yo`9fr=HlVwMsqTtFFZXh}fB$lkiZHR}=uSiy zH>`;NgxKn(5A#Z!k!_1nb`?_{8$r_OGyQmvvG6Ja$2<{kM>~8+1s2)mYJxPwEHqVV zm|$2hgT(8P%d-1pND_yI;Wk}vq>o+meuv&+0iIFbZ&_EyF5|7jjZUZ{jOo}uL-fmx)qO52S^i4+T2dPquCh}Q)9;OV_D@?ouDRF;4BC9On^+q>bXls5_#PRS$| zEkWO1^kQ9k?A*p_`z`s+UPC`hSYCxXFp&afR=9k*J-BU?vZrgf`!1Di*cJXT%yF;R zIZzVOPPnB?s(T2|4HNrVZkGmUi0YWH=6jS-D5z<~B_8Abltk>80q`?({cA(7p|B1i zmtIE&epKGzPGS)!fKN^*bq9UcFH7`7bMStfU+zzGsst!41_u z$w(2b$ac;3wKo%`z^8Z9Fm?1$oIS+eL0{{q;!-3%Ilas}QQCpw%@%r2`mA7s3){PC z@QI-gVG<(7ReGd9P~=<#gsY@jFpQ1OmgLJ3o7UGJm*eo)Bc<0>2hbHUsX(I%g#;d| zqKC+YPCQG38F4b61A8s(&hVCjYj8K#Gt013 z4SrYB7wZV@3A+n}HiH<-GIqyJ7Cs&x`3RIQ72)mVtHEBhIhG)*Ypa861D$(`KUM*P z&rL4@eI44E{i0OAy^JImI+f8!+H`kCb-qAYCm;#>I|-X>r~JSA_<;%z-_;Sff~RHB z-Nsk+WGla@Lb8xxj9uG5&l|*qH%y4Vd)1zX5$WMqcp67$LG{2!C$UU9P3SWrv>gkq zoozGW_1bU|s-V~F&S6&6|H$uNFkJoyZ#2~zduZFHv}opm19I4kpSKwx`aRi5Qo&ZX zz`f8Rbj-YVk4chSLkOz~!)84tn60D^#fJ2~GZGN$BtJ^3u64nsFoaQ9CiZ*K7jT!$ zuYX(?raBk?gR3mqdv^^jlSnAAZ33jL7gk@-r=74z*OvKEzQ*Y(uP2(sP*~RPVtIA{hE76M3@#2M#&U2|eg%_A#$gZkD>s(Z;C~GGQJ#Q|Z zOu0s(=DGYCE0Auy)JD-8sE-v@P9>Jh7jk}8^a(1#7Tq-1m3+ijpM1YlN_-2N=n(%) zsZi}!V+L1o`8sTz>bj6YASJaYeyIC(LVxV|TO+3wkLP9shb*_@95&aN60ztuD({0w z_*NHa<<&E{+&xok_maP zZ0_}=u=p(L?nDq*Zv9_{TeKOUn=&$1q&Z>pRee=eM8vVhy%-NL)Cq3ny;bdI+=7tX zjOgukpj-!(!rb_NW*0c!)PXJ%PV8EO-pLU%ud-Z=0@&*oSsW*F zAC%FCFy3p|g$aZ3;t-O~R2%!=udP~ra;EUY_~0!tG6?Quj5O(MJEG(wI=E40Q@T!Q z8Xp9?P5J@O^c;x}8)gY_PiR-of5kKDO?oi5vjkb!u*tc6`rNXNR=OlicLt!gN=)D& z5evf&Z3sp@{~Z_q*+_P>&9o{?NBqr}_(^(bJK;vq4+`yBjcf_nJ-|4b6c-ZYn)ids zgS)p@Jj(GFA`-c6!@swoc}+D@;e_rg1p6+_2#JkN#k3ZYbxS=S zy%QlfIXGyrBO=AHa)~d z6n~s-(3|Cjee+SB@EPS~G>~rc_5}yC^()>V|N7zzgdr4-q=c`>p=X|tD;(BU9gqOR zt6A@a2qd|E2_Yq(U|(SHnX+l@V5lWeVS zakA++ncS@Orh0uR*F7Vb@6Uq}FM-QIw-d+2e~oRpm5~+tg@5{%C~_-TG8+1W7Y3mb zwCpknWbpQ5pweOVg8oSkoPaO33``^+UV8%9R_v~=sQ;UFj;Pv2()OH@cxUIgKP{3f zRT~_*7*{4c^oGZO9gro!`mjw=;)CQ>X_<#AKndO%*(Hr_0Qr_gGx(ZMo?J(|zqugF zU@0kv#uH$}VW+CIeLu2shJvu(-j@dNmdk{toVD_RLM``Cuq|L?2Q1&qt1+~l2`Q+A z=uFuNm!}7B$<0@DR_86nO%1*EFM&PlQf%2WiS!f+4=dF@ppk5Cjeksk#C#$g1v#aA z9*dQYS}fq^D$*&JN9#QJkNXRfnz3wO^h&jtDaf)Zs^v{sxql znFaOLXIx!->Kcsz{a?Zre2;~NFoxso#vAAqQ zgi0s*I_E+W2R`z!BJ2^c1XuO6G6;u{U06w+p=S8WIjV)jcY=-INpfKjRDtvx0YaNp zAklFJh?_?j+fBpVS=F)ou3kFkd{L$!u7`@hjJAg0pGOqq-tp3P&R*x-YSWI&3eKw{ zi?=BwuBB5Xx;oDW$&TjBzH$_%aap$KDpADCaAL~T9~95i!R+XKX)%mWje?xFwQ}|nM-!O0z_Vk#tH@U zZG`ozI1=aSWLa+6FtCdOWtCYZjDf|sj?3Cm>!0AF;2J+r%p&K-`vnapXR*d(FB_;u zqOayAt|t*WD{HEphI(9)Aif;uaN)|WPS)5;F8`q3JRnO6yJl`DGFwP!yrHaIBdzn; z@>kKn4Qk<~c1ZO=T1rp}T#rvfY5Sk$MCP>%BY@T`=hxAR-T0PTYirz^{k(*Rl}p;+ zhgOQ4n%iq_Ii>=scm5+|Mc5U5g;_1I4o>U&1o^6j`flVPo|%x+n~1@U*w}$lcH_G45c$ zB;!C#F)de$-vGTH6ZRZVt#T&KR93!2*4ivO zd{v;NZ(NIqvu{5fCd=*C_Nq!QfP9Lf*4%%fRnA!SrD;&Mw-Bz3nAq;#JW=%3FMX3J z#3GF-L2#A6wi8gJ;-!N}78PLROk*{!4A_{4M_fndD0VU?xTvE%oX{%hUScPBr&9t! z4;Y5M=v`O`v~GpzgM|iU*^WMDT0(hz$%M6>8~$vkq7T0pZW5NW#oO>Hs8#JRxdSzP=`5&cz$rg1t~u|~V?#!~a8cquDo44(@fHmDqc3hFM9 zR5HEIhO$;-609C&^JNpSUGtM&9eFf&-5j(2+!o~f53?tEKIxFHZ%l9mg}&AzSQ=*}>pdNjTbpcrlT5#%3>w4*qjvxOIhB30g zBj*rU*>a1TGQ|;24L|p~rU|Ev^O6T*8x^Er&l_5jpdRV<(srKKhIgt(wur-| zm8y>R^9Gjz`nXI_VXE~Fw_vu5>gEU8e{}H!W-T`3sr=P2uersO+9sThq6l>Sp^uYb ziG#p{YJuc>S|{5KdlyKQ_Gc$^v&>{5%f4^I5B?;sI%qxq&NHj=i3>3i-Y&Xzi^MUAyb@hePEpsv$07u_hE)&?eXJTfcDWtC zipYG>pOnCSf~~!K_=+4q>ULf+SPg`&5+dHm@p;wd6TE&5zh2fWAJ`D!^VHcEdiGa6 zoczOB<zP0bLw{aW_r@cZB9cEhlK;)H6W{%#PJo0}n?c#C zSgBT$KL2+#m^p@FIPDaOz2Ai+X^*yN2tpeiSQ!Mpf`p(|^zoga67;)T+B&200_O=X z=bDo?iG&mWJx=#MTYjDtYk3${Z*vS@gG$CquIDJ+^TQVvHgYi!1qIO7oJGj@d5=X5 zJ2}Ztoono}CUpU60Od~INONK02ztw4d+(qpocnvu|6wj*ec5z>lb04B3{hsqy$Av& zXjcoH!)ABkvXOD|ZffiwBVgUv2pjEt-`MBnvO%F;Hn|HF|;D`l!M8H5w+6VS37cqy$F@vipD@&d7v02BRrgNg1P|` zq$s663TYq!Zqo;C=$zR$9~igYPNMnbs<7}EVu&=}KqS*H!Zs6nPk67<+|Hn~!Iq!x z-9QVHO*3tPa!J%Z;`=1z^?qaB%Ae*H&%!B~a?;WU%x}dc^hfA_|8jiMWy+$cK@B1dAOJi;?3Q2s=^gNiQ z_ns%HTOg41K@cGug&YJRva?|cPn2krer1;mT9ktId}UFaQh4Ywn|*@$w!a4gb}id9-|i5A{#+$wP?WzMQxw;yK93=Rm>2)S;2rEEcoa5J!y0N( z!|(-Ecq=K+wST?mID`XwM_H;pWRUi*-ud(WN`}fMw`7eBfL(7LCtkWlsyv}vWeg8m z+{+ccAe&r5Z#s1Sir;<&K`D!garQq5D!GtUCa%0p4Ecav9wwEY_a*S_t&7_xe?;h| zD&B)DA}F+-!6_f8&lTBF!Tbpd24+ujRg?OWTMFCbf7Y_VTn!fb4@Q&-ZI3wJMsDwl z!NzKRsS!w1f4Os5O!NhZ&!KmpA*D8p}G11OECFJTR_NJ#Zn{0EpF= z4I@XVD1CPtdc;%B#C3qwrkpkK`VQ1p1~TuOENi!N7o)1 zRp#1lKqk`*B8=yOT0gDV9q$+HoiW60{ual@oKt-X0_QP?fuB1EJ!-qq(VvX7O_aWf zA;iLXCOz1EeV}Xj`n=DjX}Y3aDMzm3GutpNJ?5R3gqr4M6>-=IEsjTW?jXHg&rys!91dc1C7VG+%4}3i%WtveB(^CoxQGnb z{K=64@ok7h1O7?(6TRw&HNsaMofdJm_V!u$_%6{G=gZ;zHFar>BE` z39(}Z-fep6vYCBjh5f|~w^O~gd34rWvGEqZl;c{W_+~cR1m$<5d|J15 z=$}aLjCBu{HYGqWY5gzuAv9YX-&r_%#^~$VZZlg{vH)QZ{?h}wkI_L55_4@D4s}<@ z?SUkh+7g)iRzcrA7?J*;WjF)3nJ#7VKk}m?=8zt0iV+rpxi0 zEo=sP6Qi1`3RPU7%lk1liN6HX(BmfmX8tX0=KcI>4-lL88H8^%awCPtaQaKP0Uh0o zSakS7A>t-M%<0bp8bG$q;hn2IaaCP(@W8Q1$i3DMhVV2o$drG-tXZg>VS-FZ74DB! zZSG*%Odpik+$3@VWdYrhD`SEx;3$jHlGKJuVg3#=T&Jym`b^o^#H!|> z4LBi~H{n2g`zv`bE`=GTH{alV&vPXk;;bBbiMc#nV{AAJ;KODx`NvUBXC=qoU^>%{ z*2@Bkt2{2VA_;ql*yPtUcxvzO=N6~Y?**tR%-s1x=OT`jEy-8FugO2{{a}3)#-8cr zEb_W}M14xU7p$)GZ*2CL4?75Ldb`u~7%=uf;8uIdW>SCT!3Dvk*1w8Y-^#W`}~=xmJK=oKNzD~hDH5% zxyF7~unzQ6pX*!FR9KH0LaQU2Y|-h_5Sj-9H-&q&NfPc0PV27$Z`79;mj`e1QptFP zZg8JLk9ki2!LVDQ+p>l@AqlkNEoa5lY15dpts;h!ljy-4Mq3T4thm zfA78ggA2h|ivhn?#}{wRf4C5`oxP3kzk68Dnwkr8qJ`fW zq5x=w!ay5!R;j$PJ3aF~dF7{xZF0X$RwDFEPybO`u*UL>t*` ze?@3QktUkZ29!yqayjbNs(7Y5XxK(?J54e@dAih`)>$mpG|{t@HgIJPtydZY^ikY| zNbsyKta%sc1{mmff+&H-X}7~1uE`2Sd_2j44^{*!-eedX{#Zz-O}SpKs7AE4J;CNt z1uZeKHDT%a3X-Ofyhop*koeh}K3NGf*{&;C;*USV&=3gp_5r(3=j*hs&b4s8~Y zPIMLegP+;A*0gpKgszOJy=^un+K^pp4uI|^4MUVs)d|O+P-XDfD2Dh|=CrrR7`RI~ zrmqzK4l-;^z$v$Eeu}ZL;v!1=0+f_zmipg%Cydl4emg4fsjM}sjK(aCCJ%=Hb(;ZM{dhQcMRlZJ>6#^M3P}`1>w1VQu6m!MW3#uCNfwEtSa4(ZvG;}fp1l!mBlTqO z+peckHi+mm{?G)yf7#jMLq(~0%i_L)Auh`sjZva?1wmaWJawYEtI05?norvt_nj~C zVqK05r$k?MaV0Zf_p9^Je;t_7`9Nhloi^agWx$2$0Mz56iry}(WlSyq;S$|mOVH8S z%<8D|?Z;=~#r}_9570PUwdu}>UqN@}?MbEMGJwynsjKWctVHAk0^mITfs$ibp{@Sl zN!~^#*E@sTG%V$e>U7VQa=PDX6HWS$acu~lFr>wXaf}(l_-=Kg=X$<+#tw#ep7uqW zmF0>32d@c1e{!GMg(831$w>OQilpd@xmpUsGC0^SU-5~1?O%qCb;^1PQThxqc#|q?WSJ2A zk5$3kUHs!|AR!7S1%=Vga{ifdv^?KVR_F|>@Uw2e*-TAKaeGz!j%Nw3yL3{otD-%N z&uiirQbsWfX8cWU1q?6!D`fC%k=dD5$(K5qq4|*>U*t_jnMvA{Gv^>T z=k`q7;aq&xzo!Q~6{iV8X+v`eku;eb;%*x>1!WBqj4)ccdx7Rs`sj5&KLhSCmk>OHLV>*6SltA#(tc?(p@L4PjdWe=&k zIdhO)4>wChJYl^7ke;xLxT;;VPVMggBK{ACPq_|2X$59b6+d7UWC%l_$*m|>tHfc8 zVph2CJN=8=OVh?`Ld7vI%ehfRN-+uuhx6sJw!S##Rw{GxCI{@L!5dkh3I3k{V0gK= zbxp2)?o|@0kgERVFsxQkb>)@I2pJZa`1ZtvXw2RSBC)%ZZ_TK>eAF&y<_<=%LJLSw6!9S`~gIN!&=AxOQ@a@|cSVbPygcUVfhTT^P=59p+1+~}m< zXsv~aOjoe}_L<$;R^(ezjDC%WCNa?>_~aef*Bfpek9rV$5*2SN|l00){GKJLfdcj3CoSj3v1gaoQS1wH@E1<9l z05SreR;^qr8DOeHUl#ltO-*p|z+fqVB}=tOGMZ(zVE9tRcockkv)gzGhcJ3m1@vUl zzA-~2b^AF1B|J3YrG?8>0DivITl%GXhZX-(eK)umug}o^&+mbT?$B=58c*eG#T8KkNA&PJy*6sb6Jutu?6$f_Ll7jmrp{A}QpX>DKnNe0u=MQ`te zjcv8z%u6{;{m;ck4Rv8QS}3L8QUoVx|H8WvDF@ts<6fn4jMQ zDcG%pR>S$oIJfIX+^4ZmGa}AhT0E}~PGpNVP=9LthTiW=G6Xv{FjIDKeQIOq@z(z> zf&$|gL<2(W>J(o)!6o9>QVl%4MtiB62!CzyQc+g1x+L&3Zjq9ePK8-lK&i8k)NU&t9Z{h_55Emrbj;aeCWPb?(hFHriuo5j)oCo7?X=V$kmp7k2v!;mg-@2A^qy_mXq0C zz|d#ngLWB&pRWFFMb9>1IdU69J>T(hd|AeMWRt@8h6p%m3^zVB%qMhC>4xA=<+PO7 z<!YTGi{NY6{ zVz4(irjB)xe)^DtPtm_a9N4K#!P|;=tOezhWGB*VE`a?b=EW+3NsQ{l|tbl zReNZ$+f@l-p$QS^ApO+1Z7&MLHTalg<8jOQ&g<@rK@&w-4QhBJo3@`~w_|-R;-sV- z;_bPo+zids0yZk~S{37J`4Xg1x56@`d$d#a6eeV=Kn_(m;_O)OteUCH<0;5YRy9-7 zB}MC96WDGXiqno}{uvQ%UY8Z1qNiM7+ozVPtQ?5%h zp^x9w-gnxzEbRm=f3s=61pNIw73eypk;YN)#n7G1HcX|0%m+8j2#X+sJ~nr}{dVKf zF01ug+}kBwRgO^69YJ%^fWZNa5&m$ z-$s(wixoRsAUcj;n5rPF878cv{$uKVDZ8S`%TigOf*2N4%FqbIYw7gg%^+DI93@SE zf<{PW&1sFZ%=Qb}%m@y2s@ro|5!gH!Ydf0f=KDRl5AsK~rM)T_!J$&<%gQ$~6wdLrR5S_FPFI6A!) zpCAvaudr}lDA(*zaARYag3wyG1Z2%W4J)2QMfcOCKuS{%bfn6MqtJBmvd!yN-VZoP7V8K;@<{8-{ZoBGe6>w(&ZZr=sxN>qv{N7SRc zcy)Fb;VV}U_;+H!l=c#$pZ%tWhuS47U~u`?NEyS(t(N`cj#v52SAbeLvpyS0Iqs8# z`Sd$U){-9`pj`6!lIS=gjR<=427pt*Re)FmWmIzvR`yh{S&g@one0z}b+=SBq(6M2 zq*dJzJ>}E5XuYJ~6#Z{uRj+dDHgnD^^<(WAd+WdC_Hl<)9i^a8{5l)NZ{^M*=DF_% za`3ZBWZVi1Sd3QHqwQFi*h~zZtZGlxkk5tui{mMTP!8>zu>6tpv z)cov9fi>MUxTp!EBzSQ#)N-TAgV^NoeUrS7==-?e+f6zDB!bnSYNdSqYoN?)G9$Bv zE&8$MONz@db(M*4uri*l5%1XOVx!2O3%^>mf&>3c#=;5iL3esjLsP zo++^!Zh@{VPxe?;Y8a0BErgp%>+W{npYvcTnm%Vog1^$>zfr>sKKUw!&x}ETStum- zx>}8FNFYm#h-FX=sj|iEA@exnpIy;k)$A9Na2tRYM4-j?8It{U=_Heri0hClZ-`Mn zVlaYl7{ZSybtvjbDeAF&p?FN9F6XL%5dfOce8SndcZ94yX0&TbG)g}D-pm>{3Ujvp z?&Nulm$V~@XjPtX+j9ERI-d!$akOa#ACj1Ge#T*!J>J5&J_h}}_?ExbY$X08IuRW^ z`|y8|>BXb?&{0@e)TRIR{zFUj(7{2-AlPis-P^MCo|k=wvO-1vKZ;v6J+ZyF_?9=k zNo4JlKXFeK>4!GjSt{{y++9^EeIdXAkeJ@f#?=)&i_W1!me3d~+7Th&3e%eR>&rmo zf$k8twe}%vWkGC+lm*-Hpl?(#N2nIv@9;gXTGUzb%Ko*o3(o=PcU#``O`|~sIsc8# zv1A#lsTVGhS7Iyj&?pjET)}22$CTDzKQ!UH*B`h=twcy#9<4fgi74lH9?#9arP%?N zqa)J28yz$TU~02@t)Y&29?mTqs|%Z*{%Gzm$9uG&mQ0L$qbtG~6CqH>u=m?P!yb0V zeC(n(7+pF$CLW`oBV&)J=wTq2d>8~J9UzDS=Sy#P5e&Z&2^$`}2$&!G0GWYzx<@am zArej$T#CGDo}fVl3bAo@RQq;*-1=D!yZHfwy24ES{uX4q6%APtHs8O z?XM*NKt4SF;cB>?zchNuq`-M%IoA8hf+{5R5HwxI6+mYouUooDLrq-{Qx{Im>A~Gz&Q^K4i0PBvd69vL8%{??*k(C8rlLW zzJo74y^OIQ-h-muLKE4*wS9JaTOUEjgF7e2GDgv|9Z5ptY&tNq@xzT$eZG&T9eDat zzc2YmKMiY#oT&nIXg}}PKaaVUUl^?XPes~1s}9$uteo#{v-nHCdiRMP*)8cP z$6w19DPPSK+Q8pk2EU`JJxuaHxJZ?CEV_~%WjdLp$dV6Ue_jgF3FQTza(}@~#OvZNzhK1TSS@>&T~_?bT*mt+ z?GdNs(RI)x#oU?Ye=vxjx+^dtuD z5sv=CTN7f~PsBtKGy1Jln!Pa=rSkBUrGKz4OK*-hL--C#+p_ldVE0+kJ&+T9OC&Q(hWvgzTaHj56cz5INmkAZg&hT3zk{ zAriCyiSV&fOqnz^klLGd$#9-OPHCfD&Wmr_Bt{1szv%G_>ou}<$dWQ+Br(tHEq+&D zF5)|F2;$!=IXa5WbQQEpKi~P@@Zk%BGp&nYl?=+kN~z%ZU63fCtaHr0XLaK}pI$n5 zwV~5i4f;O#J`D!FkRW-2Nj?W`0S4J3+x<4f>X6h4!Gk}>#)9jc;}tkITH1jF!$px! zuRnNb>{s^E2NQ&g`Bu=zdlbfzXnTNy%R8{n^#3O=`hSVA#+*i*`ZVgBggiZ0Mw-t^ z&s^bIs3E^je@M})i9e+lVP4nE_^DALJ1wuv%vt%o!1TE*hM<@r`YHUc^lpq&37RQ4Y5HT8A&oNWshWjOHEV!;DE6(EDfJ*aj16V8p&GtM(8-0u>V5i{ zuA3`8*AT-3r5xkukQ!-9De&=08q$ncU_*s~(T$b>1QS|`8T>%Ewe+Z|+-9V|3WS0T zOo((<6FAT1rT*58=zmdvwQJO^ndS57q^VK3A z-kR*U{l4`~|2e>zSQq{;_MASBT(tfRRYIu53zU=2>__Ba$b%X6W8CcOr`py8V??|C z$DA1zzpO%Q8y(zu0eQs+M@$C>8P6`nPk(aQ14jYMLT?sYTr=~8qg=hT&|4&%#ITWO zxN7DodwUoAx%-*x!a9ccTByWxr7eP=fpn-8Q1wA=V}^${*j~_>e&%F!kUfPhqafLI zN`Q`1yD@Eeso8YC>+ewjQ(xy>m>5U-K@QbiVL~rBWnteL$X#8<(C3n&v*QT=;5k5C z`z_;lKH*Ny7={m?<~;J+w*EgDlIrN7G^t_}Y>v@d!ltT^FT(zV;oFk7X9`mz;G-fO zs^GOubautEYjH~MF2L`wWT+d z-yR{!&984wwl!aGYT6v*7zl`V zAG)*QzZ7Hl$Ju-}#dk);0Sh~E!kt!av!8cq8ON&THKy?}NGHMO+EGQ?)TYbba2f|~ z2Y4ZDHfevh8?6^1ntHp)7S;&rw*!jbPTSCW?=?em(DQ;g$<5Ut_G0P?G_J39uamwT zxZAICP(&`Cppts-O{pZNVHR2PZ-NlkNdsCHL^!5HY+=0n$o+cuQ5>_zuQ?G3zT3M9 zIYT^(F4C`5QdNGteEd@Q(wMyCrhyFiavq+Q1^+ z1KVhCmuJvsP~Mr#dz9)OQuT`CYqB#%1ADhID;o_?nR$B|BeY7yrQiEXfRz!N`+;J% z;np*Hvs68gaA6=}i~rvZ^Ka^fPucA!B-GNSo#wfT(V*G^(&+q9Km11)FXZ1p zg@0S8+VSPfcbL^b5gvSTUJjN0%Pd68&qB7YVdaLCBa!;)1heSFF0M<;Ls^cV*lzZ) z)LZQC;<~Us65CffDqfo3En{C}khGq?%1*G?iNq8VXWsL)eb%jDZ=KZXF}|8z`X=&l z$6K7WefbKlf7SlLd&M2O#J^D!RE1^@Am}#iB?!=x@}Mc~Lj4A;e16dSOK@6hnqA|S z?c8-5^?RrehL%|qZmBE%j~MEgx5qBy#4Uv*r%f@w%mnuv;Sk=Z~x#A@ayL8CNP z48am~h#~U5)X!9dZ%pChWIB_Z;aK^B|4bTWtM2(8}_XpE;Z!Y2=v-rHI) z(p?Trce!=@Ys^CS7;@-6#oR)gEBhnytI=HuF4cDfj72} z?LevNVNhDw*dDx+w%}PqYI)pE`+>5BANGLOGylj(?`}8IPt@ovjHct%d{o6IQ*Qvp zlH4@fFn!an&7-BZNj9EVV3v)}kp8=cUVTWIc~~&tv-9w_^3JwuW00#99cZ+S$+p)9 z=*ba#bEIvHk^jFLJny%YBPIJKuxGd~srleWz}Av97~HtXI{p25yXA2TC=?1~!u{;r z=T5Ko^2j8fhPO6Ut*#b2Z3>;O=HR5Sk5CBv6b-Vn3YVYd8v4s?8E@diBwXIw)qh5H zRIwR03nj~ZB6_ob#(&Z{)ahPErDD`8_1_LuKjs>^1F@A;t84A&Wa6DFgh%(H5R zn^>V}Wi=rDav>St7Y4Aomwk|#I;%Wu=Y5}0@R*<%qkZn4O+iKnKIFY`O41H=&TuFX zRPU%k&kmG&)~KH7yhY`W2)@pl#jiLyRnHmEk=-_><2oh^@UR#!TziVLrsV&;bSi<}gvl-de-4Ny|+*cNp* zgnd!a=NXVJo<527ou5xYhW1O)Oa|5RQF1VjtJaL#JeD~X_BG|>2>QhAJ!{+5vE;aE zs9sBJrz+_xmJ zAu`uyHMoK0cvYw}-fQz42~SZbCQh>$WcFl zZAy~nKC?^DC6}OhV6AVQ-mZHO1N-xw2%Lw6rI25ZdFK?|E1`~LXU^I=n4&q;3=Sw{ z!uim(*uJaVbo+8KD%PeUs_|1U%OM;qB9POc4N+X=fyDe<<+hPwkCjp;A?Z0tI|R!> zCCJR2`J<2FOAX0eYKdH_@40Je_r?OVd!QXqQvT&zf6-qSWqXnhbV|UD30AfV&Yu26 z_SbD->A_d+a+^6#_*Fp#42@Zhhm}=VVt~bYof_>Ms5l1dqSxZuf)-bQSM?+Tr97i? z-kwsk6r(^lewwlT*J8TaJ97#&T%e-0#dhWJcrE^Yja-`in3^e2Q7z^Q(6wv$(7rRJ4C{ng?th9oy7e46QPst8R&D~$5*gqdY|-1YAvuOl8k zV2?qnXcR!d<6rJ(#zf@4f{foF^Z#uivbMO>lXwAo>;9j1sWeKL^dkmDpYj7Hcq?kg z*?zBx&!*TeUx_g$WvIfLWk}S{T251H^4%KOj((%C#eDnSKzLZq)m25=4A}$dhXMHY zqI!th(2}o~ff}2kA|*Y|oYbagDvzuMg{)`@w*t$`Oe)ry*t>=F*4ieY6?wSc68jQEMNN!6a`N~zv zWtUv50&ONOFVqT{cD0fIzJlmbXFC(T_IJ`iojRPW6=0-JVvc9%{%3ETSOp zkkrc;wh{E}BG$#^BhV9Ymbbj5XD98BYpF^1xYD&{V)nNt%ULJgZK%u^K7fbnKuZ2N zJLPAlYwk=|P4(TP9^5geMZP~Dx1Kur6ni^qMA96jGeicNtfg2OP?fGmMIq+5{<`e9 zW>K`-Q3Q4r^aok^Q8AFzDGrm93t4(tNq&b(V&7gE8;NwnlhX!`twPcaVpFj=LHgtK ztw06ImSTel$JNw4>vA8bo)&H_iiO#FlT0wI_UF(M+2OyJJ{k#p$wtbE%N_j&;GpO9 z{Yrf6M7K!0@7gt?_d_>n#_n^^lerNq)9FXY~at8t#<`8ziPLN$KdxI8Lrls;j!oL8~46EF?OUS zV3)DNgUqi^BbLhYxb0k6tA_=32M$|RcfeK7k zY~qlDdiXVw&&qJoZHqZ5DeDXU-|Hg4TF;~^FDaJz!4h)z>W>#yWufD%aBAe2{&lL| z#BEMFeT>)&58@WG?()dhO%JixSmG3=-+nI*0Eckw!8nV_7+Azp6Juj9BKwk3)V|01 z#@pq;ax5(D>x-XTNBm&?|BA7WMo=p)gIz;W)ptb#K$3R zLsw*{mso-Uz_ZY@T|)l7s<6f0ZDwdOJ$8vjsu>E?aytf;J(KcjJjYYdap((XO=gr) zuo|_D4l5o+`sqwXWhObTr-c=JS6pXJ^9AUv0(Bf1w?^J(ef}lGB766$`+@D$DU_Db zv4M-oh^gOTQH&$V!uBJ;EYd^%$Ch}V3wlhi6$s7LD7=%A>^tq%NfqIs>rC(Os!uD( z8{7!`sgGZ~DSs8FS++E6H%w%hD?Nd++r=lrVeI{80}ThEN8fsIl#Y_gQQnOEB8?XD$fgVaAI9D?s;%~0`-Rd%3k8Zhw75fY50v7?-HQ}0 z?hptq1&S9hQXGO)B)F8~?(V@oK|-)RZ=V0&dz?Mac|V*l83Wc}jj_11?lsq(*Y&%| zh(lO>wWlE!nok^w4m(+=KP(vX*9&l7bU1g*FfRv)w3_!A2grofb6qabq?27i?ilTh zTmy5%<&YWoQcfrWn=T!g)M@3R4Knku4)M|3lbYQjWbJj0U}O$R75*D_0wx$oCrV|U z?pt>DY`wJ{Q)hkHTm>-4)exxX$qW7p`MU=d=xQUce?BO4ivw{m1w?`xf!DuV?+HM& zR0A2YpdiZQQxm0DU}RIUe~Gzb~>Sgz#L*;(#es4FQNH5{@Q`q-{*XcX=b zAA2yfq|c%YTk8c1^Uo4LRoIk3mb@99GO7{VOxUEIh$3;3OGVDFtr&;ID(6a=`{VDVfnKCFS$& zX_+I*tLDT8R8?FFd_!Ca^RAW^bTzi1r$;P@^0^f6wX1)zK0lMT;<>Wclv)`g3dQYJ zig}A$+!DN?F|vl>#_?w>d{k8~RRcw($r>~e z?^aFK3FEgj9gOwQ(wzlh{^fOOV__iO<+{i@ufwQX1AYb@xHw=j`sXNZ?N5`KRJ~vJ z%=1;A7Lvyt#O$dmpOsP2fwdYlI_QTI=oFOdx_KO1;_MGel1EJ0r zGq=6&zH38Zj}3cXDY_O41>b9{0k?p!aaMQ=HA*m;T9`Y8?PAxI!$&eBH~+TA>3f&Dy8Y z_uEjv9>C(|R!3QL@+MBi`OT(jRb_3hUBA2HXsmtm;KV!UYgfW8vOaw_2B)pz=~hFN znf|bS=DxZR1{Aw>P{H`3uDVh3&>-)v1JoTmKhw%l# zX8J~>L6}>C`N$@S(4r428LY9bkgS9ZE7;&!H38yFZsUyOK5HZZf+uM}3tQO@`n6+bnsa>RRoiFgV z?OqPlE&ZTW_TV@J#JcB2G7o`Q9@t?UQBq1lmHudC`FOwSidc>cOIv%In3Z7$qM$xD z86tNmOK%C?ShPXpq30P;b++Cv{CRR1_*)@drpkl7-d?!Pk&v@D{f9bj72WEmRd)XR zo&9?-8IbRuB4j#5r~_sm84*G&Je&JUD5%Qx?@4QMc@aWnS~;D9xJ{XSS!`6ZrGy>B zd`&!$;1W^x7*pM4$G60{Wts5cSg_I!ouKfq@10+`$#je2fpBqE(1=}4)Kh+fq!|&= zL${0yNpe$LOy*%tCmr|v58{tWK#F^<4IZT-kO zE8xaVC6}L-3G?mOUKpmAJi0I9g1q0ZIjMrhK*1*~o8R4pC&-=X-$3TG4B($q6INIo zJK`mkd8W@Cz9k2I>hdGsb8SAfFol5-#E+#R;!A6m{y1CLzvCB7gK1D~A#Z+9FYjxc zxz>MiWBb0UQJg}#{qj%UR~f4xgzk^%PN7xTw@&UlBn9|E8tD>+oOOCh0Q&Vft7?I!V`jhNH30btN zXp$yr@i=^!o?hzpMYnrzLM!@fiU{d~vGsH@m;se$@*{g?Xqso3nwlVU1z9I!JZQ&M z-E;F$=GT8d(|SsT$xfn>4m>WI0lBNdedj9P8WAhL8e$0;b zKD20IIJtpsD?jE*@xlCbU^UUqjq_4UjxNqM=1$tdF=lmkxR)!H?q8#q49wJ6v8>`9 zuoBS^D|aLHOvd5nq>UEuILmWIZF40`pmQX5R;?a$&L!gs>V+c)ZD zvMT&-RBERoF684UK=I_>o8^>srlus*jP`*i8&=uO?ids|ZT@fS2J$fA!GJ#|^WZIM zw|;}^mEZ)~=EjKon_cHJ51})w7Kyi9Nlt5*;U5$s8Tfu>- zAN9*ur}(97f6C8fK6;TljkL$o4u&xaVeUdSP4?E`{DYzSpEh?S>^I-L7L62oE$e1o z3GBL$F!orIp>o#uQ$A~kJpBZl5_%IKqw*amTp?il1y{+1X9n6Kw6R_WOnk8@vL2o> z%;@X#CLLma4$NH7og9c_K!rnrt#0a{;EYlen|Q7Q_TG~&n<{BY?uvYmFC&V^P4Gi2 zz0bhfK(Z)9Tu8o=Kf|9c!kzf@J`1v1xJFq3rKz9~qY%G+!G-fSvbBnq&dF!DO(lda z{|orE<;Oo548UNd8Z1V(b%yuhX*%`TuVgisyaV6ir^J}Ov46Z7OdddJwsr0{TR$CW zAcrM&t1($GbI(n#yAWmRwk{MRyiH#n=h$tZGWZ z-C|sU9#CPpPj4)1SEMb+ZaBu5~T?VQeKI&iw&CY}o|MM7^yVSjo^456GH6b9* z_8Mqw;(KJ2l-OiO=Y(*3;;K@oxOJLH+*CT#>k0#FjE7K)MPC$Te>la4O}MRT(@mGk zq=bOd(KL-f-S4Kwt;U7qy2W$#W87T+XMT6*iC$W`aHV19Lc_4YDI3@1ongA5vZ{9K z{9Z@l&|W}RmakWv$9S-_#&W>B&&HoC-gr`3eQw)ma%KLF*Y`8pq9i*(T3~GGx@5Y0U+4j?7Oqe< zjLlqfMVJQnPxV`u9aAT13^}Y1kMX^qt~*d<&A@ZX=ys*SpU>p2ej1^lBfBP08EE+4 z!>Dv6&C_7c=_+`tLuy0-JTheBoVsSBV-P&w3J_vivk>GG9t}@V9gU1uNPMUZeE^@o zye=z??ww5++FLnWackPmtFRM~U@R;%X(-w-WL=+M>s$`Re-lS*AHA2@H%f)>TxQd< z;VD~3T0)^IAKjg7F-UV#NX{p!X#4UxyVM0cY2+;=_^G-ptNl^ftKT(GG_P+guO9)ZIHx<> zgzenHy_(5`PEzEVCc1Jb5hDB!cdYPVCa-k^Hde8Ai>AH~&id_$BG%|M1A?*y203&FiC7#{;l=M zs9IPy)-ko)smHx!$Q&9`+c$6mzgKh2AwjCsCvP7UzVdg|hlzgUqb83BT?HFk2n9qX z<{8<_Uwoa!2thc9UJ=U90h~6L@cu9~QbeG5k%ovEb^EQg$jEELrw5&clR%m>)1fVC z?7hFxCD*R$RPrM^=FZP68n<+7p;EXpn@y+c_hhKiOt+JpO}Cd1pjjz>hMtMoWpLDk zX|?O&CLxSwM*Ft8QJT~Yea(}~n zkm&A%JX|EV^e}KfKd-OkvFn6~C_Hl>tC3VjH8k%l1y%O}=2FO%1ltKF+_1Nk=P^Ho z9yOiAIJFOdvhFkw!uKJ>8WX;5EE8z|I=No?b*Tz9xjOa>$_uR9;0YY)BV4{js39M> zHpm`u;S7D!nmrPh(IE^EfQXrXwXLDE{JJ?I8u6{1dFa|(K?SzuTw+C`(FTGe3tbKB*c*FdtESPS2} zFI|TxalWJGa%3K}(B5ua&2T_H=ucNH#k$~Z`y1#4sgnic92@N9F1QU3c~y(qWZY@} z6XGNJdnGYSt61f@YZI+6qU^G_T!LJIqB=Pre_yZQ;l&-p*)~sduyYU_y=hz7!KtPJ zkVMDqADT;_ zmp`cT{UELKsXYNsh9e_Sz!Onm7;2upIzFjSQLpKNxat?bAE63pz8d z4J)bf(BWhIG~{}*IkRkDSwAiZPU&mvbsA*LCIc?g#sFrKzEx#h=?WR+)Bm01d=Og@ z^gh+tw)f*_FF~SsLyS)?LjK_G)28o)y%{_WRHqvQnWv5U=!I~ks=KJR&2s#68?}zX zM9CP+;lF^0M8?4>x;q(Qe6dpcm1{-^upzqrUsdXFW<^)qyLSjw&$L!Zf4GwdbCtmu zPFXg)!efdYWr$((Ex{#@sJ9IZFD3ShX&_BYH`ZLiiQ$Sr#MhCxjcg-*7}`k3d*#FR zC$Iv4-|fF%<^s;tYv-Bia&o4R{KlL%ZZM+uob7mtnTFhDN#&;5hN)7d^}%8fZ0dof zl&53*HVIpxDJMJ~>*bN}kB~#E{m_>TxTwj0Fo=Ofuze%sr>z?DV}rYpV(B*H29w^Y zEYfHd^CV$K+@}$`ZIn;ry-Y(eA1m5*w-N|f{cU=wu~W-Tls50j2vrQGUBXsgjGvCK z8w=pCWQ5VyO69O)4Zj!~)1B|yUgx{Ji}}~o3kd%b^5mUuh+cJ`$1NJFhTSRmI)s#D zQJ;9IKuf&OER<;C(0G*55;9yPWssWBynrvSoDLn_1M6PEaORtfTjW^)yf}w?XFsjO zn@M?TX7%w(EHpr>2VfF$(jwqXh?^mSV>6WTgfz2EK#A#8d! z9GlxI;I-bF^~G6Ak^azE{89;Fe2v}#$qyuaQ!+>Q)!U{&*SgSVO5(>QhN{pV4*}U= zSC(|TDzMnC{HDY+KG!DqL{YJc5KL&7?8wK$h-j&H$-SZs`icUl944X_M?*DQiNIU; zA?gd{Suod7h7N(f`=qV5iA~6?+hDwI&P~W{D39DZbBW zP0D=}=Z_1jX=S+juL42}T|g9wAA8d&1QU(_=N;_-#|N3FiC9w%;PT(OEISAfOObRm zfD|EX_@r5TQaF?$FcX{eOY-~ntGl_u=WJxzy)pCc?t~o<%Q*q8&1jVF1KaMQPT?o7 z!DA7*2j~H6()$4wlqkO{GrZ`d~k+63E~mx7u(8# zrA1?}UB+%2R2PDe)?dNsawCQMBl?dGKOem;a=So-+_5b4K3a_^&|8oo*>}9}$*0c= zBd;D6-zP@d2zuKMs9sDg6Yqa$gWILe|4wa=*Hf4 zDqc`(WuAnTOuN#1&wSp$#1>gwu8eNq4-GU(?W}o&E}9_mx%O>zh@ch$|gn+6~@g2?71xTuA7*qhkS*3h3#ftnKG{K zm4UTi=&G9Il*a>}KsJn>k@HkSBVvl1ekLo=7g3DucW9Vz?rQWN>S*RcIzBpE*Pv;> z=37rqr3s>t?BvvK3pCM&?wyXQxfnsfi>PkrxHjNKeH^+$TV*-JH`c&PhI*3rnpoy{m(%GI*db||wjk;NH{=@q%4MwA**#upt^LaJXyt7k#}6@lm|?UV3rf>n0;g|xSQ%M8o1tf!Iu z?6T4|MAO+)vf{+&Op%UI3y?fZdo^}DKQ&%bp;+^~_-r6;ea!e4QlEa)B$_~B5HFC! zbmVV8q`7o)|J-xrembsl|M8{_zxa4D&B__?jFQo)$L6vm=AubG2;txdnh>O1nwc)4 z`lUP-4)~Ls~wb) zW*0#@3~v?5|9i@i*x3!H}u6R|=gtus(ON|T8k4E2#g}tQJw+~~& zzx4FCO&4{hr5X_)=ryhE>S$>fYi;mIfdh1VM4x+lw!Yc{t*5xy1I{~?tum@ch@RR@ zXL!B$!{+FmtW`oCE2Tb^bL^88YNSYqvz_dY`KgPtExWE?0dvx%koKu>A`6v{u0Vdg zLHA;pD(L)!jpc|gyy^K96`t9YU9=xCu8gfzsyCmL!)1uD`os5-QKYQAj$*#z=Q-em~)(wQjN?$Ii|4%A#y%MjJz z)tPHk@35zHv0w5PD9q8wp$=zxmyFQaYXehoIIWee*TuKVMETO*#4#7xMF@XFk;*>f zz9~5L{mE$N7x&ggQ>IKOhY?^{&{3~(l%gBnstdA`87$A(RZThWIl3~0I5w8`i3Om4 z>!%!KP7yZDuYkJf7rdP!Tj^s+c^%dIF!qYI5gmO{K0rmQ`R}!8SlZAlRo!O$?xPS0 zQ8N_-u|cRzg}kz-=RNxcB3b?ME?5@R&$(y)>zM_?$!5g+I;hN99UYrRKyib-B?MQSMk=g z8S1y*HO1FRU^e8r)>hScTt6SaCn&5@J(K$V|5ci?|1b4fVXo?b1(2Z~2xZ4i{O<@K z9?@dQd|Vx*>}=D^+U?6S5&ZTAQjIUwzv|U7wkZ$wo|d+3C#W_(@k*Yq%h@z*H0bw; zo^0se7w$GHO@HkvL8NP&7`>CWOEf&H=%MGDQTye+AC}C{2^F5+K69g7b*r5K$EfF0 z>58i;;kjLFV)E*7m+eSY-ea{l1DmqiUb0GS!pViV%cQCx(q8y35jN0|0wCRB2y^GZ zcbjhy%ik}{Z)MSWTov*U|GrHlm;;8>+Rq)c=WwAT2w5J>ae4uZhFUYOLT{`WB#Ic7 zZ75A6CbW+&)A8WAYUeM}04Hf5tDP0@MgK0&{gJw5e@sOl)mCHz*TT_0h%)L8LL` zM-eB7A(`tvH5Ul}9UHIlFN)YbSUh?XIh3tz5kIKiHA&H|(c;kB>7KJyqX zO@A{~R)|BR;5C>(JV1(}B>eOFV;v{sy%L?XEcK|8_FUVnzhnzp!7MiuKLf9LiJP<_ zQVZ(~Ti4+`avbrSvPB%ipSzCTlC#Pu)Z{M2#MIoO(C_3^Rh9}r9=!e)E|50_v))+DRW+DP@CiWAw%&vXo5$lE zIbX#fbkXAkM}N$A+)h4tWhQ=nI{x(1Q4TNMnu-UtXe|&xq(~OE>1%;WCC=DRFEI+A z)#oN40N4BQacCfRzq$DNlrJ>48a;abHA~>=RU^u>)01v@6hnueuubR$aY@r>S*p8U zDFPlyEm1EDVLe-2;X=7&K!!H8n5=VxU#ik->P|cY@Bh?rcmB-Q{*4%yjIpZ9@C~;njXE8l{VkVriZ{H?+&rfDEZ?jjC zH5!`J%@6q8ONUH!iar*%2YA3P|4_8LbX?a&Uej5t2&=4%jdi3}rVE5>)1GtCJfrOX z{6u!I0!g#!78~;VfepUU&qHvjnV^OQxq-*3`n&`yr+Idlm-}`~-&MYk#dH1i_d)Dhliyh8|(&gIB@rB1lQcrEzg z4xNkaVeWKSKlnT9-&|y3sU&4t8wk;NeQtMb2h6*LzhiFdljrE16sOjZ`ohBngT@=` z#&p433zuFuW(RV;_zzG~9Cne^?$b0RXlv}O4R+s~`-=SWO-xUcF?Zv%qMs%riVmC5 zrC9uW48yuQvQl($@i4jkh>cu8g-E~o7C{{A|B;f6+5I^aB=A|}$pQ$_`wOt8aMPPh z$lBslBUiL>n0D&LJWrym+IaG}R%Q1$=ek)e=7FXO2DBj;ud%FVEZ^+dR|1qy*YjAR zomdzZt31&_W+0jCI!D=Uhw($4?$i0n4b-7svSj1XPw`T#9oC-1)cQOARpPQ^`B~d& z<+k@X@Eybglad)OOUo@`QS9+arq~Uv$|P6pP4Bw$_SH5jq~Kp+&xg)e z!c~S`$l1zpMd{;IIt)ccj}MDWgV%XD{m(a}>#^U)(nC^Z`4qA}jtyUL#&bpIdn~q) zxNJrzyJImn?>G{R+fD?6-3O?Kis=Rg&xV^+i^X3q{<~pwir?Pe=@35&5uT$jL8n6_ z**arfpn#Qyzb5*t#45CzK1)k9E_RITHqnpfk=-Fw?F)R~fEP#&m=f#aHCGf3y@4!E z@TCP9Es3zxY1!Qd#C8yV1#H%##9%m)%+mmSH(ci_$a;dHrjMgj;Yb2A*?Le8en^Ol zvtQ}NTM)Nm*DVn3wS!;k|OUhkh|wgrCH~|xr~03 z>9pae$vU$I#!P06J#A^$pdVBZ9{Sg&VkhU+Vl0GIw*{Y2hdcJ`oe`ZB&uHVbx80n6 z?N-B+9l1b$sh~}zRhzoeoLub53XKhu7~QPaws0l(J`=RdEm0J_vXHM;MvH35B3#Fd zh83PW0HuQW5{u^36xS-5IC+^80+Rh+27$L8j(&{$Tb4K<`Bz}2ZL!F0R(+yZSz5n- zeahhm7M?K|t-UtW{xZT}m(NXBSvVvqx$Zu#bFtSN-1K`_R-6s64lb`f{_zYLvv@1# z8`jUhvI@Tj4=^$0N^zZT&%_v7ekKsk8A#8LlWiCJDy%J(ywW6F z`m1Yk%Dfj8%z72SemO9Ed{#Qw8eRPu;=SEWbysL7c)KuhIQsA4!{cAkZD+z8?aEVbe(|H0sVOoSN@pgkd$5Vk)Pgb%{Vx%rFWCk zSq}cCHUAu{f*z3z&(8!T)BK>YECtb_A1EAyaBQ!iC|oRJ4s;aaXi9f z0JTZi8m8IpS^19L-PxM&?V-J#Qp zb+k%L&|Ai2gn#(JPs%lrR;TZz!DHMvTe=)$dQjMgOz)VzlSI8RM8lL``kNv;8fK)7 zp?Gwx3?%+az4Q$Avp$l=U*~B?);Ra@Kq5+SY!p)yMd>l!HL#VBOVBw_Gzk93$Pht) z5F&Qwxn=-B?A z2O28UNr##Y5okwV-wUHg7B+||P-HdVyFT3qxsfBy!LddggUk5$prRVZL<_O#G|3+Y zF8v%KxDOy?MH%}4!Vd3>k`Y)_M-w02)0YRnga{rsN?1Vr?&Tem(Jc0(`5O{Qdmw93t^)^2tnhZYoUz&ygr(cExCOf1jnS0>dCj6rNz%~oiwJ$iz^ot^>0gsfrng$*0e6dPm zJObNB)f)S`%kvMuw#6z#x@~7^K6&6+2qEw4=K>}7neZ0XE?b)6Img(lpk}Cnd2jKV ztt0xmB4Jp?XC3J0fokySx z)$FUmLN07IeFeTjODwSt?(`AK&R%FL0>8J%{7!$;Ey!-ZTgO$&@KB zKQf%>!=LM2IB|O@bEVozENsz#_B5u+!F=g9j5f0ametql8?%XMKqkPklxC&a9urE@ z;!?(`h6U|4<=8hw*aQomGFctEQ;t?)Cj2VRC(y&An^ zSL8X=pf8!hzl8EGgp)AF4V|g${@i>hXg&O?T&}TK(j{frT38#idd0V>NA}p%F370P zDBzgxT7zfx<<$rOnrX{r8pJoa(g_3>c)86NUu{JWvi~&Y{SO8|ce?KO*MUxavx-^7 zaIPA?FDLCB$pwI>J!spFDr6Ew1kt2Jw&`q?9;a&xtvx`Pe+VMy?ZB{m?U{xpp@SG3 zl<{cQO+pD*d3Hb)_$=2QnR9Q_gbW8G2h3X`0ld5=Ev35@ zP`u=qBA2QKlq;dwfwMc_<}XP<-t$e~F!8#4=!&{9gZ$dzUmjv<5Cy=mX~U>CVO)JE ziPa?)zdR)uz)zG@{X;X8WZ*)XJUWXV0s}$%tn)aPSTCZJKiCB9~h{fOV}x5aWW*#-`b@ zgtvtkdlvVRpk8bA3RAFl&>m@3#rx!)j~BAJUZ`~S?@xR9IEiIn_?LTXt|*exL2inl zxA5t+p6`ihJ^!1NO7p~$>eRyV^101dYy}A}u94c-&Yuq=eOYHki8xuesb4$gF03+=UBW%n!$m{ru#KEYG6@FbtD`sO=vv`|~+iBuS8$ z67je=@~Wi!;bot&=g%9n6SHj@{-!48t^ZP>QS`%g#1NaX{QFZ*7Y%f&`tWSIWl#tM zbMRD_V4sZfhhfdJEb;4MwDfYJ+5vMD*ut|>EdQ42D}d8u_2sGBqv7pU4F*~>0Qhgs zfK((}Gq7JX>EpeQjhuk1ZB_EPY$i~z8~q(icCD(_+NGA|qpz;PkW$V;d&bRD_fCz_c&&|JNTY z&^elA-#|07mUpj;5Kljw*o<_S1z<(*NK6)8COHX2`g+N6yPH`lXp+lOR_;A-BcYAO zttXmzy@3C2gg-W+Lj!BC$G6(RWHDN)yoqmsH6`SYa=ttA^PF+ikeKkwhrbG*i#vL* zUgmEX|D1JMpHnU&X%Iu`d2@^pAKwWgQ2lt^?qfO0$))Y35d%zHbH%jRvSA~B8#bOB z%;ayclp2xlyhm5PwwO@uRO~RZgN7*eCvN8wx-qEhw>GEWR9y5AMMM|7;D~$9a=E_?;5%`(pNdiyzk zOAf9f=0BV@Sd5Ev0rrtL9i9Gj~F8K)*LZ)<*UKB^&7x-)tD1rn*6qjKT3w2UrA zy4-X%ZM3x!?KNqLj$bv)_vSo7ar<-tYMaD1?C%)^9wHHxDBfHXPjkyfNqN#4`H9i; zK9Iy&PrENYADO~1It{{hx!&o}{PVJ=M)`gS4pHW(iQ{JR@vN98DcXV=XIF=Yx#>V3 z?AdFoh#|f`s|9=xp@5rb?l%NVn(YDA={fsJF)@95&P`HXTsd*B+|cMLrxJ;Vn!*l~ z_~XVpobYX23&s{Ivm4TL_He^bQjZ(*X2R~xe*x5Rst$}RLqRTf1!LA9cB_w2u zI5QOjcpjbrm)3m(Hij{?92u2Ktc#-;T^kKVLsmzG(yHi60(h?>%cRxjl# zHl@%E!;QM0?A!R;k_*GkyeIyJ@f&gGp^`9vaP{+k4xy(-JGQuqUJV{PSUXIVH>E60 zO@aU3sUM(Eo}XG^GTa^K-l6_woIrx<72p$7=w1v#LFbSC|LZ;RzuSZV(>$!G$d-SB zPJ1ow`Zw(L`62|%&GIt(XOiI?=j;<4ELU~W(6^06X`CdPnT+UM<}U_<%=HA=;uSv{ zF(jSiOKOE!6}!RYxY?Q8X}bf~R0M`qdcV4l9D8**KLkaWG1<$^t*FypyiQEdKh}N?rk9%^O4KZ$xSFutQ>qUxvKaklg3}t}*;t_;OI$+ozW%p?xaBD=MY)L+ zrqBQrTxTW9&XIpiG7}1p8`32C-20R z(lV7KixvkejLp@{G@9^%qtnXZFS)94%V=sd6VdP7-pGhKweBYAekI{z%`fq4YeKqJ zADTDA#sYMZA4VGg|5hT729DR-p)gQZd2Cq<}0TDer>CN49vmz9K_Sw zXNvxvF~GIllG8`r;|^;bAvyi@xmuFCt%g$wexyaXwg%2X9eZ2dMqj&cbPgTQ9OF>c zs9XPoL8WviQ^EHShVzz`3qb=|qx{Mb4CvQ-P?Zv8y47XI61TFDyNK~z|4|^4c~ZLO zMzHe_MvxfJb?K=~VQWBjb&*RV@BYfo&*WIc12Jv_2C^?BzxA{9_D`KP(|rqlim$r3 zsSR7zbA!MBne6-M`9NkcC*BGHkF;sGPTyIk{YpJcwuL+T`sI9ROkiYA5*TFeUip&4 zFAmLg`RwYln>I447@4j7CxWU!xMIHQZ@b{Nqdu6ksftQ9d^LF7M7wlO)5pu=p===C z`Ia7{LAsS&(VDVra9zlu)t1m&s47fdc2@1&G@Km}bW0c-T&SGLkk<5Zswt1{sWvum z!+Z>~YQ-}Fq4^YKvK8CeK~1|s&h&!4OI*iO`xc_gHMR)caG&heb6ei%O}@=hzz}sSEfLtfdMGczz?e{V z&c}()qYm76b?DyGGYF-_Su4Y229|^}uO;iS4_W|J)1nW=L;IiKmQytsma)2mlU=1ku1)YEIVrZA#(ree5_iZ8$m}!1mZTXa0 zo+)&!Io=)fDZ)M@hl%r##3E3tLbJ$bp=#xz>N1d}Y~T7=uV;Gi8*Q$wBWemz3ywiy zJCLIQQ|+2yWbWS^Vs7V3N0(fV1i9BwLkVTOzfaH-3vTNt;P21+$8g1SW}DG1r&z#~ z)&P^)h98y3mQGOEmW* zy>eaNvGB+w;oJF}rq1S{B&7U<@bEMx+&+C3Zp=Soc`qgTA^^lbe3YvXQZt?O$LaL1 z>m9@QA_(io}YU<`jiRL6nOG6xq- z_7b;(+Oov-vCZ12`nBJENlyNAEKBYUTCzdpzzM$?LL+~IJ4-Iyr5u-M)X7$BQEMVt8 z8&EvuiY8@l*a$D1g7gt4s@*r3!fqGmPz|Lna+}Z@iLXDLV-4 zi+R>Q4T*WdDZ)odRgUmN>4@NKD=x8L_nPCh+`QPA$+PUKP#mUK1@=#Di&hW zYBh5S9qzN4xbIVDP{o+&K8!xB?0orP+7&GP5$Rmbc`QmKS2QM*!K`hr@_N)MNep)- zk|{??2r~+NAhB0l+L>JBuwdw?T@;T%G@de*_ZR>AotMp7W*vy}LJGH~3`x?LDH?eK z)Gl{8eU?*3qw;ZD`O97`afuwg-)(>a&2TvLD|`%FgOaGtTq7NpjE zVL?O!m^9$ZuVJ35EElRNwyA|}u7{Q<%=QdCbSV5g?ULX(&GHRc7U2B&wui^-SE%wz zbZo7DcM*bQ(Gz2JHZk^#W$JK{9Lep2G()(`!_U@6I-?+W7r*duIGO4L@d2 zhv*k%pFZbsHdI}@3PNAdmkByd(5wKylS>YvY-*RUETfMc4l z@wCj|#3ct0&Qq6ZL0|3lHjaUe!gLZ|qt#RH&)gQbpRc&|e^sa3XT2RVIFG&pJVojp z3bRB^OR7C_&#ZX9u}^%!ZKAs(cDQg{ zU_OgiN1RYd`&*eWxpJn_>)YsVrLZvks_r69DA;=FA^h^o-#LcEH3O+fTSrszt67sk8M`%T}^(IJ$M6DAJmuRa!13k0A6Gk}iOggZs?SLg7K| zr`JWb$tMeVZ>cO|3!9~#V~8@hev9`%n@kttpDM{R6%7f@>AWgxpBno<#*d`kcych3 zN$D`7UX|4)t*Jl8!6&5LXgcJ;`(`CKee?%MAN$N2XctO&{{ZMm&(d|utwBNZG~rsQ zt9rFUh0Hm_+McbxYtp)+3D0KxLhd32ar3#F%d9yRRisl2cD+wq1M^3$l^CH6|DGVc z*4n>04D}BNA-oLD>w)K9(S@N07Uh74MDSeGOXSUv_Qy(aD)CF@t>RAO2jWikG^e^R;YLUsy5imZS$#g4s#L`Pb# zAn~0D0D2nIi=JEQNao^CZ2hcqVRa5LJPl091U5ZO;eD*c)EZrs)xgF;{)6$wGdJQ*7+r@pV57zOJ zL6ykz?H&gqEwCrU)QM*zV)#(B)^vZk)ucbhHCZ7&X`hIV*ITS}|2XK{k#J*?7W{J~);$kSS@ArY~ z(;rB5d%MUU_+aUOMP$g#1JOKz$h=jDPLeq(P43M2WZ`Yob@?)xLR!a{M`R)$^-4*$ z7%w^U2%P)ri5@eAy6}No$T1if0!@$+tqPd{QD2$5FbXeR)2X3|)hN1JBL~~!j~rjA zv@VO_6X)IjB$KiNYU7~!W{wQBhk&N^zi#m|yvaQx0O|&+kT+<#>F#}d4s}~w2OlkL zoMEV?@u?bh$h0{@wv1W>y=Z4bS27zU502<0L`YR0`p^*%z{x#b> zJa5SNWbnk?Li{IThMGGj+o>>L4<*RvJhTR?xt!j&PPTya#}_pLvq;#Aa{cV1k6N7< zX>Z~RBJMSBQj`mal@e(9CU8EcbW#CjU<0QdFus0zdbF$_O>lQg-ymxjYRaKP*CfcZ zN-T@s(eX>!AHNl~B5i6}x~ZsMqdUe}_tFnik45Ns?BX05n0=q~Zi4JIM3dZkIlN*> z0&llgR4~SKe)y8JMXtFwj`hz9X(QJZL=zoy#fSvM3IDWqouEf3K$*F&`p?3T@=)Kf z5@bwoV-9VYT0^7po6fXzEzL%@w2Ax0JZ`h+bG{U;q0+eL2&PB$LTtUA3gI~HdM?xv z)Dt*xvI4cF5RC+KLGfLsMa&J=JBXA#+=RDzHWlR~R0P`JV*OAW15wJ5g^V+h$vgq_ zARjl;x(`RdGo@%L^%@5Ws7P_SlZk1}^UB?5_|p>Tu3B(u?{IOt_s6)F*BFlIsfsWf z%3Oo+K`Rat9;A>yFwJd?+zLl}Huzo`*x6(+vbez++&+G@;%Oo+_Ch zy$`bQS_W(}bgNr#8}^*XY!8Uc%g9`Oww`;y^l`(#j%u7sg8i_6?c&DFfpPI75BDF8 z28xq}O%VDb8!V$b8T_{qjkPnIO*2<(x{C8s=)#%xQU}smAAdxa%Cq&ePe6&2tKtWi zH?#K=s{#fvK>-5?EPS+1IwP#^j4+<7lwmMY_bB6wVFHnvE|xh*3H^h?_DES*Gdda7 zK6;HgO!B}QDv=Q3OC1w3j(P*Lj}4)_y;17Vp*Kw~0kzU z(`jagi~;-e$-yQD7;yag{=Ra^w1|PyaSl_SR-7C5rwSK`L2FG#A{c+w~(gD5D=g)e? zGlPxSiuQJh5R&)@8QEX(x!U^L2lA+(bkC_8-$3~<|2 zzA{CJZ;>7@ygJN;K&$?)pu@MQC>#31B0Xsw^?Wv~Uy#i?gs@GqxbUe(ZpFt4&kdX$ z3!3!T1Ws$bPezAtZKu95Zl*yYXlOAAsbCb5Aul%`@Zf2+&|ijH4f8JN4U>sITZ?wi z($lM-UOV#6tr8vP;s`~2s3_!DIFURCiQam{NS7>ehzK1GhwxO_J7!W@1Ul_w_7xdilBf6 zq!X1Qy-9BokuJS=>4eacUZNtsNtZ6YgY*v4LhlfI386^u5Fo^}f8Y7$oSA#)p1FVD zx&LKCVDD`9`|jsiYdvex3l18VJr2@9*v6wHrr#x#S^9nkr)wpIQVhGlXL`o=`{gB; zRV?kSys+#s-Ij3q_NlEgXVk>&d#-c21+0(Vd`$2I2ZPUT}>_|Hwn{!OdWsj9XX`9Hg>BNe<&F34fQ%)wm zhbvKY74M(MYZoNuxi;Juw7hbJ9h0k=aLO0la3#OxTJk;pikaqkFW16+$sTR_3X^P{XKgUMw@r)WE3?Fmd|Gd>=0~BYB2=ADIbSHWb)3=#Q17v;}UF z!xP*)J;7u(c?UIGDyXUnPUUw+zwWK;B#TF>Z!~4()I#-p8(6lDjp^6;_}Sw`-(gGi z$K`-MLu9q^!FPhMU#8vfU%7^@#j!H}oWWFI+S*yyn!en@^YFJ>kD6w@bAC6%UW^L# zSxr%F@E)u&`F9R7ov~SxMW)LR&DO{NSB%Il@z!llaxex~;@(`fmr#zKajn0BJiSdn zpRpwNmo6`xKNzgiZZAH-1D)r6jO#0Zl=k6dL7r4zxQji2wd+bF;#n`D5@Kd#{`{B{ zh*(w6wY1(tQAHNyL^RsXz(f57XD@jE^8=BXgv{#iR`WiczID!r$6SaTqIYi-`Cl1z zZ73XDc<~U6s7m4$?5eA7m@P?*ZsIx7+OX}#BwU5^nTu_mmx(+B;YHL6d|>;cqHxZA zCBgPIQ3d?S<-POw+G*kZNm%uU?d?dDr${Y#RkzGyr7f3{4*%XQ-wmSgCxpA>BFk*h z9$&bu{r4%w7JQBq(zbQs-j+6_rhn0HJRiLy6!_Cm87RF4IVzABD=@F{4KkyyZH0hG zdRCOiZ0rgA`YSDAY5IV?vexH1zwN^scY;$@Smr!XU+0(4Y^E^2^UgTKbSy>aq?w80 zQ#{B?iee#1_AX=+K!;u3dN%xLzTkiI^uhnd(>v4RyH$L|2;S)V>SNyI!#g3Ug-D4T zrPD{f=B2u&+t2Cp2(IG9orvGgH&>S?;bpEGlau)y*cvdApG2r#xL%RA z@#7}iU$lw$J7kE@#Lwxc%|qRtLhQ305AV?$(2gjJ=-kn_xRSxAA4COo%8W%1*InA5 zI~M8sinDW&DW_D)N**<@6?B_?%MkgENjl0wkk1ph=F1IR7C}$;W5oxwlExF_6D&Vz z#s84J7j`*1cZegTKYj^BJQCtWaZ@W#+{6vKa3Ek}jw32A-=KrG)LOz^66R1TwaK&4 z_?xn>Tz;Nsn>qt_Y<@9)TO=Vk`}f+qyFEfb^1Lmeh%+az$MLbiH_=sYF6zC%8J2&* zCv%tb{a20rC1^(sv(7#+5T!BhmPwAaZ*FQn!nHb(dg?g+ZHQkQ)s^O-2TPs)%Y2tw#bvvu^FCgLaM2OFGQDf!X+RNF<5koA=+P+Qb`puBHND&@p$bR!$6qy7++FMB3{PN# zSZY89@f0g@>>r0-d9V{{Uh>yFo+>E8hl-8Ab%>WeMh@?8Zb@<46Up9}WB8?*dK@9+ zjTvU9he|N=JUV=h2-bDoZ0oIEmV@z7o;)yXvb3;y6pzc*{mzR-qX{joiV(zBWB3A$sQi;yofA%-Eq_92)VBNU}mEN zveSQ{UhupQIv7Azpw|@$I9R1rAkNmlro^%~2suB%+mmcoU;iOYe8_`G^M3Sy(623j zI^TE4!JW~xe}Va5f4W^4XwzZvIkOuPST%~zs;)7#tM?@_VQlH0j(g61{L{X4x5W}7 zzW0%Cj!%3!k0VSEAy*6V~_8AVRgj!RXN`J!e{c-)1w3TIukTq{d)Ln>YU96 zTjD3;M<&`5htI4jzMSN1KNObfPHa@EC?uI7%> z(z+`{)Xm3pG$B(U;hbJb^ zB6j$g6G2gMf*rIk?Yb%Om)$TfLJcaxJ5#1NQ*EEA)V#EcRk`}qfF|4jWIn92>D6$! z#wFPl42V3Qb_jky*Q0N2`kBe5D)vy*jjY>HHpf#`%bs}GMDHi3Iq+r3E6be8gw34C z3%ux7j@i2DUWczK2WJguby0Er(uOudslN_eU@p6;=8i4PLyP7RNHehLVBlBKd4&jD zqIz`l1=x}%I;zJ0j&xKd)j(zGf*z&!6aR!hEHr4m)`;$4dCnZliP+DHyY%8uLm$+M zWOgw~&kGg((x#_7-ha%gSb>=tRA{nos^Rqv+SrtCSb?1o9Msm1ZLKWd5Rzo%KSQ#-}*2(zSEh-~NJ z?z2DYiRVZEdAFDq9u{UC=xe`D=#Fl_2e|r{oVquG)4Sazx1}CGz60Dmr3id?&pciM zi3^)MA`vl?_xx7!Y(P#VXyqRck&+cvsAT!|#umC|1n(81l_9e%chr#$z9-bz+W7pc z_(%OFqA50+J^{@a`g1&wJ|o;jhgva|#hO;4WPbsNWfAc>cOzf+9-rY062zt5sHB+= z{sZN4wdmei)*8`jD6qSi2JUQAdFx2tsmm_49A7-)pQJ?(e55}^nf~%hg0A4CvFF-I z9=`q237;m+*VbF7|7{=LpQkmNE=33>MLaHTp~%pROjW>+*$6&!_78N;U$V`Mf2gwV zvL*shnjstiZz;{kV1;jIcP|=&DX_orGae1Dzyxq8ZD3yRef$HRlcK0}+wEvBGIVjA zitFFjKw()^#_|1hr@X}CwrJkxl?f#_$OBi+4!7`^iAb9Un^9ySh}avbDHoFEZM-JF z7Yz(m61o?m@ykrPnZ(k<%=Z^!bWdV+ejW#C<)LHQY`THs9A8WesuUUtUf+G)!sl8Ntx5CRAT?!rGa9zH3Gj` zI>TK+Fjh!hb^z%1-XlfL`67o{3$CX9%;j5;TC^t(XL*;G43f#=lE~@`PH0qugl^7? zm{;)%W^ipWrFQ>&%)(!HgYOX%dInK<&P#`-v|3WG!ITE<6Z>}s>K7GVbqgDNG>uep z^gG$Jj;(Y#YV6;3t3fC43*wE`Tig`Tmw7q%4AxJb55LulB<$ipdRmbGFgq9S-lKuB zC(lt8ex&^pwBO5*nwss+3rC{!+0@4$zy?cicS&Ko2m$ObWL}LIsIWrf7u6Fc<%cCd zdMEETT4)DGwtNcHqo;)t+(&#``@+j3_v>$~jj-^b>OG{O)d-xgT=KpVyrrP9=(UoF zPMlwNO!?swZjUBa8PKM&Y*Xn0okf|uIJCj)N6U> zB%B~Wsky~eKvC4D$LKkKd?U|aF>jF}69f3IpTJk5ajisji7}Om+x6xf8YCvjR@OO_ z;PJdSDm(y|NB&!0a2)>RjLk9wjEGMcLsTcE$Nh?R%yyiIR;a=X+xqIptP_{$!E3hm zEhh#at1GnZ(8v};D-V+Wj{@|BDS;0s)l_5;^y>3)EUpsG84_F~1-C`P-KH9iC1At=* zaF6Nh%(@C@rn9Po7wgE&=GbVZm~GXb$($su?6|Geuy$pwvQ~ro#fNf?Dhj;R8w(LS zZW1iZPHfttx zoZ44#MnU&Z^c#Hwr?vmpZ%>5F>Oqt7>tE>^}op~ln zKc>0^Do{fFN6H|1{9o(-MNN1KU<|!{sWFjw zz08uZRUNAxX|3w@zE6soKH}TM@=4qa9RNbU7NiA;aY7E+f1s$D37Rax4}=Zq18=FL zk^siwMGjCW0m`qygd;dEvKJ^9I>P}R8$AVrcMf16z!A}ak`*NleB^isg0l1l1BjP1 zpw>Uio`Pe}LExjRHWFamrC}S6QL}|$y`wRDCisAL|KjI%EORXwC;ko}%U%)i4|L`r zVuB;p2S9JSod4UIMXLha^#6Ze{~x$s_}~f?Xj4X|Gd$<7^LAa8{7*a6x;a^891mD4NeUAUE0n6%)UN(F^7;W4%ay8)ze;@;)NATv-P$ zCq49#HGy{5<7Wq8LXNTeZ_7qAF0YRa?7f0}V6zl|^*;*y&7M(ZWiUZgs-2Qz7n zcri@pI?G$T@`VsYY8H*E{M2dgY7mon%bg%H-lVxRFn>s;9&emDmy6T?>(e3{*oEdf zDbN?|*C=80Ld55h=hZ}oGf1C;ympyIZ^U))a~JS@0tfPsrF z?3|;0dj7_Fi$AYTJ@EkKXwbbK&S)j~zZW+hq;at<6NRfJ3fJuQc;73!gs_@u@AxG$ za*yoz+`Nrq#n0GisYk&%8f@KKv6tB>>-qha!`~|kceWtIi&C z2jr~Ij0Z~jK_maSt@d#jC4MW&awq5V4X|~?(QCW7_HqaiD>3c_Xs97EgCT!KnrQJ* zTs>T5o`Tw#P*g>*{&Lo@ziOo^$Xe@sMaHt&B+UnU@Ap-b<6XRCOg_2)vfi=sNqpP(FB?>n zAx~Jn8ZAFtvN{*0SU!rBFmUg?NPnitWl1dg~-+MbBm{+D1cb3K|0 zbUoR5?|u?23kv>yzaY>~jCtO^tnK&9%1EKb-L!h;_2-zGUhCxC-&d{dB0F(d6<3aq z;wfv5K1@O9iwln?x8)y9nlbCtPn+v6Vdf@C&T`Aj)|q{=_$aPw-<&Mke<1xER=Nv1 zUNNfnZfA+&@pGcr%a8uPWaF4vloPAlec9hp!8KND)iBNft@>Vrp!7P|AA)V;@5S%) z(tM*`pVH~gZu#<%W#kp^G-YJ6)i9ZvFSb4ep0BJY6`|#_5qoIf7sC_v;%%n7Qy9HL z4OC;>cR`19vpJsrif09#O2dyhm0)@=`}9wCN$vCsL7#x^GCqzG*l6LO@j973)`95L zl=Z|_xynMrwV3O5@IV!f$8&}VvmD=7fB1-BWeD*--64`_lcLMnD&|d@oeoSio~z7jXcad~DkdfCC(Q6FA#RLEmO{32RG2yyO1-j+HJe(? z%TyHS7LZ*jmR65TzTz&FgmXg?>(P!_!iEwnLmu@E7LCCTa^EICdn|b)Eot5=E4fIxUy*Cxu6X0zX`5(&>f#&^9kPtxb;)L4)a!hF>rbNlS1VEH zjgrQO({*?G4t#XPK{4-k&|nBDG?cVU==ku;%MQIySA~O}Y~!eX2cKRkJ+T%ipoUgI ziQWLRu3+coEPpomTDS|6C*b>JMW`Hcw+#hCs?W$s#Z2>5(<3L8?phXB9ub?Amotdz zlpK-JP)_=WZ>O`~D2t7`%ET1Hw>2cBfSTsDp-vfOI1(*(t?}(n|5}^aaSt91p?4P7 z=+-;pH`wc`;ED%dKu+#!Iq?USJ&&At&mGJ^b+-9@yv^Q)oc=eA8~5KZE^z?LDilTX z4Eym2&}I3*#H8~nJ-E@Y4bVq2L?+vn8EffTy^=H|dn5?T5YhRo64s)~wRhwC5A@Lb zD7Y=G)jRhPgC0MZb0Vr^^n-YFk-Ao3_6jm~S7l~t;0ODBCHBlrsuI|qRm*%)@PAMBMR*eU-M_a5ZH$<9#C(0%VZgjj@MjYeto)Q1 zo0oMd=0+)u7OWmR2)9aYEA_Z%Fyp6)fsi3R9jAM)@2DV0`CM)_0jtdL32nbfAYMpYJUBj-6Q zOXvHY8RB|aTnxPogTo)T>2l^~qWA@JZXm`Oa@0z6J|?oDgCxY4-#K5}k*K_kbj1ql zz!;_Vt3$eEM=5ynq=jNh!v)ON2b^aQ$nUuM|w|f{i+Mr&9Sy;P`7mn*f3U zO^Ye@{+~}XR_d;s1;c-!(f`zqc>rob5=z^e0t(}qFDJ_L?Bwh34}+CQ)7^`}#~nwlcTm*AFNc^q-B&+xy*cyL<1DMqW2QwbODj$>@GAL=wP@I<)4 zZ{N4ebanKSP+Wl-^-WshqsI^DRW7W?&mSdWl8<7@<)_Ng!-qi!4Lly^97kxZu@8g49=3{s-cx zZ#gOGFy|E+zLsSMp5Z;*J5CZ>wTxQ(C+j-nn(r;`xMk+}IzOXJ8K4|d{7WVI# zkp=P&&3+2gw|cpS?ZM`W--b<9UHNYVf;d7x(>@9!ZF=Fq(}OP=30HjhW=wzFw-P!K z6cS?k*}KW~wfnt0=f-A=(YIJ;DXE02Mp~QI(_tI#djSFOcKh;Zzwi<%IeIa?I5oJp zW~r8T)-mj@XKX4~PrB17HjHqtk4G`rpD1tTBy5uLE^3rLJT-nB+1^Va-5%@lfwhPD zpzT`>wU|cPPvtrd*?Z_bVP*MCSJfu}wDwRk9+#po1wpn0&*IwsEomhT+e2E9SUMJR zJSw>2(HQwVeSLtBmU+Qxb`SBrqHX2f%VIZH(O7!Hr8i+ZpL!({c2TYhKMf0>e^qTN zU0v8|q;{`Q9~lrTQtQ;}T99wpT}U!PtcG}_`Ho66J!gKnM7`6ca+i_N2NCmpb5?&I zS5NutUQ+gydPDDnJn=7s(`~v74*vVT*X_s0fI`D$2K*m{uZkoLPM`FA?5_kF=K2Pu z)4f9Ct2D`Oe^4$4DbwHKURdg^l4g@)?Rz@?Ra;%>-V+%jzbn+O$saiF*{aV?P~#kYK8<`yIHN(y3P(0sXvp+BddvMJP8%u}on0p9ck24wD1B zd77I^Ey1-TfMdnap%U!B9QXg3pOzG$5e)q`3Bv8uYGbO8JrY1Vn4mtp&Z?c zRsih-PLxZ6sKlaUDheEnuf-G=4@%S$2gNZ@oP|0XPaJcbanNv9-G2k&eKz3qPHbku z8w@y8TzDN>BjETdYmjiN=qP?hj6peYv=z$o7QE+5qQ;6eiO*^ym-VkWKD-x%&iM4Q zeIf4YdjROG9ixMA)VH}UPr=sYNP{jo;ioaX8a+1y7WL}93^3_yPsfq4^;zng8Y~! z5C(cLgP-+?h!@%n66MOjWU%JDGwBcL|na zTw=;F|6Pf7)Fw7akB8zu%|Q2^Zw+`H$fO=H*#3MI6T?yWmA~2Z(5P;TW6|jGoROW^9>LAQs|(H*sSRRFn(y14#{OQ|BmMceApi`>}lJl?+V=|f>R zI^Udf=n{H7e`0(w(EvpLU5S%i@x~%^u5-Ujaos^w@CWIOg@{#RA3{*hf5pP68iTBA zMzKKC4QQallCh7A;m<^yrP;%z_1`;VX1@6`$kH|N)49$UGiS+o2PkVqIjaMf2D$q^ z@@Z*dF(k5Eo43%i!P8-e*RA{U9sys2&PzkHW6602+PapTk4~B@*U>C4H-DoCg?D8} zQ6q4|J+ajns~NKTViB%YUqt?aX15HZJAqtuk{~yEez)Hr#>=P6b0fG_O@Go6`8@}8 zT_HfOUj+@8=ZH8uYgdQ-x`v#6!lCsh`<_Eet2Hu9K!8b%&C+PpvCNznqFy^rKf{q=A;{iO+w3wxa;&o?yQ*#!om@tz z=K?M^sY8_57x>bI_q@0c(BDZh?AUV<^d0F2{~$rMr~u;CP@IVW=on^DE!1*~;YX@l zonb%qo69ff6`RHc!_ImpkH;MV#a83BwzD!s-+ILOWkUn-Png6#UD8l;=5H+u+7XP- zq9j}Mml(G@gHY%%Y_DG_`IZf!@trFEmWoS9cJ)3b9|yCAMYC2;k+lA>?H1mHeS;Hp z+RP_hd6oHaDV(w<%p$ALdTjNX89;2Kg=)Wk8ft|M!rdF+G(5ZOlLBQpfe?Pdi&)F? zO&SLApVr4H3F8&i8oG0e5{9Wr#N*~j5uhK=lC*&4_2EIcCKCNbDf6J>wBq-wXOXqm zbCVM^aM26TNnkGxXRm;+NCKccw;S(=Frnn}0wI0V{?1=Ve8zvu;<$bBfN`+d=v$@` zvuC)DuJ*$z>@R{J9_p)w{rtyUQO(bKGa}VxT;4W)3p7mJR+k}gd)<5jEdS0rTKHri zd|Ph!4-g8Y0fYv_`Q&yeXQPc1h!d6j+jpyVRyI|mr!WE1Sa0`}+kK@P@s`gz0)k5U z?PoPfC&-A zrs+TTw;}v5vLde!D5RqdRV1f$CepJ0J2{DL8B2Xz1hH9}TSH#=u3977pLr_NDtw4j z4SzpvQEw2W`o8)eJ;Plm?}!u-+HK#*;8?lVZB6Cb&ZS6DmY>t)@`~}t2_>-zmyx~h zL^Cgj3?Mv6mYg0q+hv~M+Z zp&t4HYZN2P``cO+jS78QtTJg{$(8uu_a|?COIHrU>~LfuM94{d$BUAjot=dY+g`;~ zt*lfxOF-dZxLt40=h7VLJiy62br8{*^BM2u34~R98(AnB;oDREXyKJp^1`V*f1a?* zz$U+0v1ATQS%%>3058k?!d0EZS{*UUH{@2rulsiuFI*n%O*Mkr@!bqmNc^X9;OSag zR@kil*&Lk#4BoQy%y6a@-l5zk;)%b8tVdCEJ+!eefLm|(l+!!5v|O&WIdUfmTWg|c zlFr2=#2pN&gOgXmB-v9OXA6f-56a~Nw_ z=AY2zT=6(xY{AZxVt-f~onh*raaB?yGW2|c!2>o&jG$R`IZu@#tM%RIl^y04PJWHUm^Q~<* zK{S{dVZ$~%O$xf!_-+^M+ykP=i<~Wb`41EgZC^6k0y69VflfsJqiEuRTt};33c+zQ z&j80bH~86N&*bsN*RR+&SDGMyH@#@=k6DuI+mL^t37bd2I)D8bg4w}o_D~?0`?mK@ zXgmYjRxfc{?4FMrh-)J8vl9>1y9EWL;AGyy5Ns6p%J~WST3wVIav0y<10E(}3pUR< zr;E)>d@l(ceERaL;3nw_fU8`?uidlu%~#r^fU^ky2U4&szrq&D(#M!2WQs6r??FVM z!~wg@@pYg-G*pJMkc4+c{G_E>?s1x5hNh|4`yqjzcWFjH%l@x*ivsu>|L{{`XK{Qr z69t#R|7|z*e|{z9fvcOuwPyj|NIf6hUT8}2c_apJXfJ5v--`j(nln?-QhE6cM%-(* z!m8RRfBzOTJ=O82(}|4c zqEtyN5Onzv=p3MYG9%Pu7^N91@o-yUzJ5Od)yjK~9;{DN{Anm8)M=OMv7It{+ZxxI zqS8B9_NJ5SD)nPs6!QYh>8IjY5uE^o2RqL9Z2y6rxw`)%`8Z`!C67#=Kwo(KMYi^| zmNo62l6kUvr}>la!XMj+R&Y|G2cyU=gWfl5zZV7T-^@$wshu_?OSt=oV+AqWsOUL| z>)sAH>F3%^rDKL~Y!CO}nL2D?QZv$Fz}@mK05^bpQS~Ad>`${z*yR69YUja-$6R!MmY0=sC^pqv}63D#gMP|(!W1sRX?Ndr6IoDYQQ+_WGU=#5L zrrif%^K5r2Eab1g9M0Er{EYikA(185_{evbsjPcIRykqtb*OX0T_Q@#4s40A!#(>B zVQ?fH&5;U9Q{$;?#2p!)vFQ&#`j;lsio3DBC&hd;4+vLPyS;IwpifaHN_v!1vebSD zB(3hBcxFqRs>^~Ca?#8yH`U?alSX)LR!Bn10t>g4=H@Em<5Z69KCt>ZY}++*`rgsxsDU`$UnHel~-k1pRA06^$Y(2JDee3 zqQlf@9{^AgQ^bqasPr8WEEZosNF9h5w91$NKJ)d}KagA#utv85N)o0su?{B1$#T;F z+MC&F$388{>^!bx{PfUCbOy$emH0}G`8XQ>DY_FC9 z^LX;}yEDNvNoMAU9mJ6V9Ti(2Rq?%U-r`0x*lvsb`Jiw-EZG_@(A4uQ8tf)awMaR$ zYrBul>#2CX?HlW*|GHt&A6YmjL_;hk!H*@MXnV4AK_wG#JlY{4e&g0GH$!*7sWnB^ zjGCI^MyvEO#W}UqV-M0hJ$|vfqc>c0xu=k=jIQRBEPDricvV?ThBfwd*27=(&S8qm z9?_b;Mkkd?I2`p!AJLTAL1eJ@enBAPCg&UCdTqEgdU&weAdzJ30Ukcts*Ao+5A!zz zJ+g&^oLBw7?iv^rT0T*-f8FOz|LPu&Y{i%BHj_Uy6#fXu=&Ph;&xgU$czTa&H&r#% znq3Y$7cS_2mx}ov)JPKh@YTb^6wv9FY085+zK@X+s3g+)|7Q||l_iIFX|`LWE(-*{ zJklIQS<1>Yc}tgng>Mj5S|1k-n|cu9PL_fob89Vn8u2Rb>9#I&aepo0H9373dWGXUb+PHq5x4&+BH_HkK6J;e>Y0mr}KBJGo(b|4|=1De3KloOH z&d_8QH;to7RcQ#Kl=vf??0g9z$XiFlQa38IP~6+wUlngG>H)yd2IfFoT)1^s?m6z# zThv+_)$h_mGUV1)>zadjIewUKw;63`!L`I6U25xr12@ziSyjF^l6FoO?7d9#DD$HQ zdtRkfs;4$yT<&wgj=I+F=ckrp>BkOhFlTwzut;^Zml#?cpT7&sep}-<3c~p4V<^$# zaEzQ7S0TdmwG7dEy44IF>=sD(X+F zA1YPB1cW*0ZmWLVeIOVJ5*{J=lkdwmW)|0B%n1WOoe#&H=|$qwJ+iN611Vf-7LKA+ z{0hJ^6@##?^J}!g_=CmmgitB&(dFH{n9)TZ0*Z5e&F~y-E6SCOJm%D zp#j#IxRmAD({vc)B)^mUX{%k*C>5iq(LU+O;dVZ5ULA{6gB8p!+)q_Wesvn7xJUWQ zi5l?P9_2r|%JAG(=1sFE=)4H?mXY6CpOi0QXgWCC@y&PZQSa9JUOZW(T*kNma8OP& zBJ*vkGOJpRraBa>vb0l!l|pg6?%*fjH+cxvZerJGx3@3+koD$9Erb*gX0g$~8kI2U zNj$KO?$lqeqv{6RQW?m*!JP9CyKPr38J12e7e$C6gkAR|%M3Wv77Mh+#+qF=pi966 zm}Yv--u%awRK6awDymwV9W$2G38evtXA!C4DG`m1%tbCYtr}2KQeQ3<{|vhWz-nnn z^{Q0QR~zwJ4vP9sNrOF)d?E;Ek0b)J7I&@@nf7G{iw5vdL*pxk@72tNRelzYJiHjK z^}b3p4@nR?+n%t0zkK4|_+OXbz1@$bboA|_Vd2{LSk@atd7mA5LD8NQ8PUCcpZROu ztYRC1w&&k^d#IH5%gdlHC)wd8ygA-(kH2RK-=je$b<-*%2H#JZRUg&GJ+e)CKkUq> z{f@WMjJH7Sb1ALD?4C5?h->~KBQp#1fawP2UV9LwkWfj95WOFopio1dAUBd`%V{vF z?(VMb0n?d=n!m-7owXnRT*S!PhBqyb7YNd+5QX?InoD4I)uARNHO-kMx}Hh8#}?XZ zE5i)V2;`>hK zxk){g;``YOB2}6gDAf8vmPa`ze;Fw*sP-b3w-fNtDSG(}Kdx8hW7@GM|KWXG5s9`Y z;xq$QsHuEe2J!(1EAIimBZKpKnN#waGet1vUc{>D$Fs0o<&Eb=8cm1^Tlj7&|{^xXug!Ihw=JJ*n) z?8+Gay!unI&J&TVkG-y7|37zRzxY#oY9J)=j;+J_N5h)5gMCm7LROP}2J1}rmPpd^fNbr zqCCNdGdpQKKTxL;brkvu^zt($T+2c$iF{DuK_%MMSmn_I18OVX?bT!AQnQV{wbrS9 z4`GJ3^~_}@NT*e>TFs={G3i^rnNjH5UV?7k`%Am0&NKN$8jVLX$@T^PMf#rkqJ?`0 zvDAm0bHPc?LYDnk2+6YwYg$y|bFKnyt+&kebPk$D{f&i^%Jx3<45_u3Q2XHmS~`Z3 zka+d3R(rUYPrYyPwVQjp1vZj|6yw@~Y!XNnuWK0ZXUZ_r)dim=F3vI>T&Xym%l<*S z54t(85xpn#@e^1a8o;ogeEtB@zP)ey)1pN*04>JPPGka8;E9wkQ0ibLv~}E9z-}Au zTkRLsQB(fZ?k`rT@v>NmFyW1}?=KHENWAtobcjKrO7#{o0ov2gMkXlhYBJQUyr|QQ zb1F7@v>f;_(Jjjwn~?$Pz7uBHZ|?c&e@!1@v#Io~k-5 zX&4R;FCH_mRjOt&ZJuAKuQ+%uYoF_TKZ9jFuO8!wH-@&P&|xVoAs7p2IJa`ASnpp&3QZKSJ!jsZjdXt8vb63zH{^LrIu z()Dm?_C|AE%6Z$%!rwgIKGdgz1M$d8h5h>Re;^MXKTc`$)#*5uruJz!&pqVGVPCR7 zZ?6#KGhTn+HfA$7`cJGO%mw;mC#HqP`Xa`ehA}w@;VIegX#3fh|6=Os_o-wM@jEo7!|hCKR2h1+MgsQJ!d$uN~Om$W5$}E{ZPIozq zx<17h=aw&{wOytKcO_D>y>Ev$Io2B1tt4w6gbgo$(5xIQ z;j%P>6ZE@wl4QJa882)u+J&;|%^wcoW{aY`WYmAHH5T1Ai^`XX*UoreoD2U0VPvyg z#`9l%;WmCPX?>|BlbZ)J~Jct)i@7d2n%vu1R{_N5z+jO736qvlY_tcN))Qx zVA3iv(l@{DPwF{R6U+N;$~=m8{2V;PCc^@A$Q@t@*SRgtvGenbD~7$6)_9716RgrS zutfJacitVySNt8kxDWJ_+Wzw^ciTUiU{qcFSFQ^JH;yIZ9SmeAed(d@ZFI34f{OQ^ zkKQb%FBpCU*(1b*H^pUw0qj5ZM#NX;HntrKeg;^?kn=m%?<_xWGGJ)cqs_^f`DDWo z0bz}#X1l+irjs~^KLI|HkA!_VI^3T|wZgjH-RoZn;K+{+jP#cc`FISuu=M&rPm-q- z;vq`|za2bP})+j{kuW>X^9I^0I7SyGj4Y+ZS!r|M_dJ z1%jdcPEE`k)Ub_=+KEZ%vxcF#6f^1av># z!tn`U0ZPY<>EObWP3j}v*ffr|r1Z>>naXIfoh!I!2G8+RDhG`ACw%1@6guX~;vBX3 z?f}-L+vdk>lNZ^?jJ^9$*I?o>qn2a`d$&lY4aUhsb{Z?B;_FQ2+LOAFai^#64DUXy zp#48!u(9kwuHbh21u0;LF&?g9@(6GK?C1mRN-O`XW#o^j=`Mg@YSVVvFj1P~m0h&m zewkn$XcdXm1}B(HxknA@x0t{B;~Jy1OaS!FGv#q#!f*$oY}y%8Ba`)uGn{&9m8Mgh zjn2d;at9rV8Nc3Fe-I**A-E^7=Y^go8rPZwzi7;gUNS_AYG%BP;qHn$H@M$e!#rrq zF`GqW9qp|davX?-)%uJ#t__G)*=*J{+S#{5i5P1sEa@sWeHLSn(d=NaUDw|5N{m0k_E22l%M;H3y{J>JLNjJ7 z1TDVO<-_T|67l)&nH-htdTMg378%~2S?eN!BzB8a3q#82Rgs?^<0Tf zy~VAM@e>KO{EtcCzbN)PfIHL7tY?mIi1ZoFbUuHBMT`9;W$IPHXK>An=@e<1v3 zH#d09wC|ha#GltnnO#?J9i3lIJrgbZ`LKTUZmz8t?qv01vk5|heSb`r5%-xN95@N} zAm<&(zJ}$qG9=uUd&ewjse^})C%(JkJSQ%^#YqM$6c%JC^Z&fyAX=tl@CLSs!FC01 zYp>}jJ`K}MTi>8KS(%s7u6*EccIycMa!j^cjj!tE1%L$62R#9~8%js6j*oC-?*IgK|%%qT{g z?^(rxhkod`!Lp<^zx&GMquf_^zZ--nTN2n#UG;=~mGsWL>AYxIg zdM|$ojUqam6Xj8BH!k-J_+UZNwj%vVp2kyr^~|bbv_KiBC4c|tjKz#y+nq~GpIh(c z!w$fg3b}+-6z$K4@(TPwIpN=)Dj%DKl)2r|GR@E9OBHOFIMPcUv zPpgYx8StpzbRX~Ao$OXmjjdtTid(Tw>*vz)Q~u&BbL5F+JQlSf?^SJ@jI3E{H(?bx z@on7^Nn3St{+dxcw9~Xl6&& zrM9(_u)RT*uHc79+G*J+CiEsq^V=YJ!&)!nHk;=@8#_cB*PA?0M16g9Rb^YbL?ARz zDzhDJxKZ32idAizzD>uaZ!XlfWfC>o&zki$%Tw&ZnuLAZYWEo?b>d)H1-7%Pi=uTm z`ky}DkwT}M_Trz@K9!YcTC25D9S0SmR-Wr&Qips(IZQ;Yexwgb-gh10CBW{g=p82o zBa-1zR0-RmsCI(V1|o~wO1EQaqg>>4@${>%Syf7c^H_;If~Q6^p|-|yDBeDM2HvF+ z%!5jqOnJxw_ZnN&=GE9ccMA2J+`N6<`e0{N>S#HusMjUa zp+ODmNL4=+ve=BC1atb0w(3mq|KuB{`Um~ip$Kswzou2iLl`KI;yS+poE z;r{MSW}f*x>^GpX)|jALs$VgbVz5V$!+3-*H(SMeqkdD&cF4phP4Sk9 z)>(H!ohVE>dT|(nO{zJqCo47$8j&rK5{&=Iq()s!)IsM`R*<5*22B7%`KlL9hO@k% zo3$y5TIpFQiVoJOG<=yjw-BB1nUb0{61n2wNDeL(-c*8*>Y8y5Nso|nLr%qjB0hC@TiYPSw|UY-x>eYE7ft& zkF_`Gpym;ggOLkpyvOoRB9gl3POM&KAPv06^$(iV@~W2@&6qU!l=6KWEhCGt>|BF} zOPXD2-Y5q>4De=cCJX7#`gh(`_o^E zMl1N;ca6sO`FtY5T^Ambjto++98=*xie1RufhoE!wzbmcNAV*RDH@5dM9R~$CxQ>x zvLrrF+UKT5);k(Ktz!eU=sgI{+ysHFp%nX@sN>q2>N(u~OpIbpxDxS}T#ZP=z@k)@ zkYlcP)bm?Zl#A%+qB37N-Zo&ZnAB8TllUVf_`+{YGeL4SQ)=OLTYhjTr2}!U&3FbY z-!vl$lqx3D@69kp5r^=z7Ip8rm2BB$;GQQl+Y*{^+1lK2cF3r#cK1FNEZcV+>TjGe zZyY<|LquuE+J7{>iWNAx%e$Jfl@k1}sj{+FP^WrX`UH2NNO`xZ&iiz}jo54R5qY|) zv(}O>B1J*{ds0o4i|=^c_`tqsyLhz64u8?7orz#J7jLFajv8%PU9^;eD)Mvmi`^z) z!B{?lqw30VTRZ%}yWKuasxworcUwlYEPWMX9h6dqX)OK~`~oBEtO!?4p~Zgh%W1Tu7opYI!wyN(}UJ?tEsUlD`Im66jm>B&$orJ;yJn zb?pA+_gh5*)|`vKdY>v{MY&5}q(c`1@1#9Ms!wC?5ePl0t3u-&2WH=o8)MI6!mZ2bymuX)rw}FyHI=BOdy& zOrW6Gt`QH4t$fhRJN(+Ji20yGmd)%!=>%Bn7r79D40Zoe(}o#^OS3dkk7k}6`6dU3Qyy`C7zm}Sep`iq_y=t;DfmBru#Kv2YQ zMHr|@zOyvXY`Xn&dO+L%h@B3}N$x+oT6L8?%zP-nF;O5ot<6$RQ`O6UOGWSad-k<5 zLvHiDFSG;@=X5i_#JDYC%{2Ay)=D7ox4E>PXC*t8Vb~}v^lT!_+=vJ3y@KM*w=}S% zSJf;q%rCY5I3S99QV%fjwn}?=ttqEa+r!4+^}gt^OJ5bj*(@#c+9LZN3NgQFR4nr? z27GTnI2mMlfmibU!jU(Fb{C=54?QC@vlnSOqXX=o1Wo?_apQ*6B~OQ2kLKflwqf%` zU!WQ1sCRn#Q#t>*dynfjjq+;YD@LDr($jw%8f|nQ-k53DOxFRaQ7bjA^(xg&NMMse zll#Y2AX%vNVoZll!CVoV(o3*(gDgRM;gz?cg~(#q;yO1b7R|a3P>IGGDskzzkrv_o zG@rU{gox|)cpa(@Y>Mk3n-LWHXA}k&hrdhm)wswW{*%bQ%9ZK$=iZVG#m!X?gXRcl z-rIMiju4NZIez{arc+l3-zHBzOwE>j+VhUMN2~KSi(Zt03`|ns8Ulm>j%JONd~y_` zkCw?kXZ4skB__49y4s&B!NYk(gPkRmuUyC93XFX3@jXBosh^?e+9gr#Cb6P5XlW5; zoUiXjljO>ArR#J+|5|R;?HZ8+zAVoE?H`evn}08OXwpjUdR-Fc(G&@n!MMv%-Tw-r zNq%-xyf0nBLY6zI|DVepXdX6}!v_Musi*&5YbC$e-odWcf=r(7zmd>H5m3&m3RaM4 zuKn1gagR1;`0%(l6JV?%84~Ib(!8fRz&j3;wq!72rAbk+Z7^7G2+&uXTh|qx_Fc>S zY3e)JFupfZi%&U{=hK#O&FRR2m6pbLGIr~EWTiJtpQ|5Gy`!c_yj%U}(-G(EnWCuo zi{g0Ptv%4)C-th#%u8686EsK*_A+Ov54=_uj0 z-?3ILwmOC&T{!-*+BbEORmTLz;RzsZ6-OafXpK0ItqX_y4uV=t1N-o-r{uSx`PP^g z4#J6~-ssWo3FrgH!O&{`HGZE;O8UNQ-(@4N@oQ{*Y?>-Q;1@G%;#@#+Oj@dhLh^_C z(^AjEO5c{M(&RJKytps!o68PfEa~iXDrcFsD3l*b-S<*FI1P@L844)wJ0$-7S-(K=XdE2K~w_7Dj zhJCvJrU=~wH0@%^cLr5!-H4U|RbBVC z;fxMemfh*B6?8dpFsNb!?d0yxJY#yQswI3;kKzepm^*o2k8^w0m^8RWbQ5sB*pfMo zH_WFQcWNyR?l$=)$Gm54(4|yRtg0~TX;7KTQR>+R2rQSY2*TlJbaNLMM9XF;1Hqor z`e0#WjJD@=-Gpqj{9VBo<`ZAN@w_oHwQM#aQ;6(W$7B!a-PUL1@RV0an?^ypb6Ko} z4xD*bc@}3Z)~&^Su!LhK;PK}VGA}oKXeZPCARQ_DTaH4MIziDQODUyYo-!XWo-tJj zB4?h~ylz7_TT~TwnSP93u^z;UHlcgAvTRfawRc+RxfP9GTaxy#wMqY4tO)Hn_FT`aJ{bS{ zH{liI4Aq!eGte|J*wTKVy&7(^Pk6BZ+)a^hs&%(De07v7L#?hp!p_Ovv&gKD?=HRI za7e>oNOQX!po3xfL7!iV?xqMS`jLLMQ5pv76zn!b;2b}7A&3p^`X?E}Ns!cal;upV z14?&F`7c8sg9jeOVgLeB@T$FAyi zkE^fG&Wax;&1EysHyu#NAXVL7ro13)2D$mNCsf6Oai<1W$DHNx+MYz_P2qu`;=HP0 zqU7N3QN3l?E^dsjj@!`rH$Q`Ona!>=tSDiiN*#Ag@Ogth-wz2@3X6pbIo`rJlXo^; zapL=Bfp94Yn($*s`CCCcfE!_heD8VnJBIRM0edj1aY$A%o!_*$&ieTf0Wd{EU{Miq z{(S{@&IVu|SuQvh-*4H3SD9r#@f=oqD(T}h>`wY^R!|4}l0h#aWiuYiwZTSAvrq-y)q#7jm$X}; z;ROwbXs6u*yz#`O{C%$=RgoP2mxH|Gyb===d1#0~F(g6)mFEXh`cv*!lc5!Z$i;G1 z$Ju4rHs+zZ599$WYA-W3JKSQ)qA+I*fAF-@DJmBCXR-1kftTbb$0;4?K;KueECg+E z$!ATi*$E~9IlSf|ixn0V;9gHj=YksJjajQ-*021y=XJM=ca!HLBNYTf#+skOKJbs`KX2!h*OpM%7bIgrc%HUNWd$8+85Ys|+)-CfD~o{mA$U#)ak zw;_1c@u!gIAQQR(euZc1-MK|m1fBPdm6<(XzID%Q_T?%UMEf9qv%Y?H%iE;&P7Ck4 zg7jDj!)7bhQJiEaBL-idk-q^CO0A<~0})7`2p)r4?KPv8_4{|ERz8Z#8O_H9{$k}B zZ(>+LiD(HJsJFuV^{djDe$;;*@Kp8aHcAPLmnwa7&0zVn-%VFJ{?97Ytjp}4OR))h z-<+?fyu1E?3&sCOCfon`MEhX)B8ZHo!(FhE84hV}m-iyO6ErLMD+ICf@(}%g)0R5D zN|(J6WP9u~MedGH3Rraj`*ZIW>MxD_I!O3C@EGlG(kfS<8JVg^Id4-pQS(i zg9)(|XZR!^*5_db6N>fDx#K7KuA*g^r74ZEE0y1^v2rhV5fymPIM(dIZzwVj1QN*W z;k9g+R<=-kb_p2u)(Td&ehfN`s9RC0Yok>8vTc~~Q_*}Ycd*~v|A_ie+>QC14v)Q# zZgX_!1Gx^n;+{B}$=^Jy@hX9lZGk|@^oxm_;X4IB@A@1^b~t|NvE@6qMpyBxRUlY( zqON{+=o#@=F?Hrp9wyv6!F94q@4A1nYZz-%N0H)`X5MD-3Lm!(yMs@>uI?q!4&>au zdOQWGdwOiLcT#yqUB~x{WB0~WvbQ8F*f-*|%ff_4znFXW-)kC9T^^3N#rFGP0)zv# z42``6GhPzLoeK00oMQ*HCPop5v)O5)x<%9<{Jf4~D7j$Rkl?r69NeQD_MyKoz?Dd<%vuLiR0(=gkFxFmFNc6eVQ z`Gis8J~fT5JVaV(<@vTzMrY-4!K`yC+^88{Ydxe34@svq(?{LI%E zXU+iCp@9XB0T%Hy{EQ8Zk~f)gecD-Afj14zC@7<3=K}++pVd_|4w?I=6%xJ%U5)Cm zUFN(_100)Ym=;`g?=Ny%#Yq{i7<<5*+~6q=5R~mGq&S#bQn(+qVraoBM!WJ1V|vRiElD71xnO8INR1HfMeOv z(mR%ahopOK*G5)zb@5|4a^U?|kb|T+<;+y6_F;1w+TLrV*Lftf0j!m(R~2_jm;?}- z(|)vY?LAnHhB{856KT_po=Qc~KB2lst74L^9W>(sw-s%|)+#iol=k)(R$?ao1rpcZ z?^f@cn!bw|(Rl6?u}YgS8HMK77M3HUdlrlpJz_6r6|O=V34p;p2J;d`!Re`c;AMI@ z#Idm}=xgUZXd%--8+Tz~jqqe+nK?~^lz^~xskk&%-_>df$n548P(4N{{*xs0gIH!DmO zxQ^<&-@QMz8m}|k{M7~QOCm1@uGre98c|f@63J#rkaStel7I2WR8xzH-gY9 zfYoVCYWRd`Tp{fM+y3{qSz58MKaR>S3t$gQq|R#2E~P4=RXUG{rIGCMszd$hd* zdqskgO!37FtgKu!()D1=c*r;2D|-fZEM+B{tH#$pSDZEi&s7L<8XlA&hCnm`vZ^Gl zc`M&Nuze*W8VMu}dLhlsdj(L%TFvKaLQTG2{tNVSTJ8n-*HGpN)g*EEndDCg_Gjc) zZ2m-$nL_@1GRZ0n;&5~HE_eLIodZ*EjwCrw=pJnk*>8S1tR-ZMF0VgmAsXQv=k-acv;G+3St*6xYV$ z+ssF6AaShzf;K94oK4@dI;s!`Cvy|sdp-p&OVy@!tPYtHU6}B=PXFDhY5r5jx}iegTvR^DRyekBV%gIN zT`7)xpAe}@!3A|s!H*~rF9u@hr3tuVYY!_T+ar(qpx(vjVLu8v8q(OSiwX)#`>o?Q zHt+)8QiUDWd^XLoNS)sG@)7V5KpRyYT*H)iGI$6?)Z97LGIQffoE=K`G4VB;J1jCg zY2Fgt=BOFr?`mGsOrET)OMNBHvQHJf$!GQ4uw#DBAmU!!dY`8!*mW4s&CqRigyPuR zN**!)W$-*sGdUS8tHJ3iwhZb*$;g*0qy4g1tv+LCw0NFri91BdLh;vZsf16AwIm=* zURx`*p>SOm{ml1Ec@6JTPx5J>B;np9Z3KL0W`0R;q$=Wu^9{t*PG8y4#~L?<$hCZ) zxs|L_1z#9_ndsz)Bo(--YIn_|NkiDP6>W2~biqoq*YS!uVN)wD4j}l#{{H-g zvPOx8=q-zDZ&B9PQNuhdDK8Ig?szF>N*AGrdBsbUF;<(6m0APyzvfD9N)0m$ww8!= zl_u+(dzBDA92_< zdt|K)6CKW#{D6ysHM!)wIIfbuMT2`e6hzXtJ%IlXX9JgPXOnZo2`$79xsqhX}*h4bVQSpCn=QOAd9r z9@x+3<;KsVwe;Ovx31mzdU7=9KG{Pi#>38aPRx5q&SVE7!0N;oWwmMCq7d0hH>MjX z3)wU4b@ji$&*ZThQw);c3XwY^5V=Z*h@(OEy1b@Zm|w&U```lrSi? zkkZ06ZvC#laTN8oolH~bBqO03>=pUmgzFpPmtue3{BYGZTh)alqPaVm+{0&_!WS>o z)nZzDSPOlAHzw}>#?WQIPYY2PS9YCVPEsQF^joN1JgHmY?m&gk)opF&`ni*W$vT!u zbh*Oab==-2JfyHhL(KT!Yj}94V<*QkHAC$0BHQJ+nD24tFMr%vV*FpA4*$#7_@K+8 z;j8wCp=-89);A9M6d%dI_#Lvt!Z#iy^r9t1ZRGmgsyskh9cMKe-b`4gp9hlLehOyv zw!G(>J`x>eF%7^EFVZt}maU?F3kKeb>Oq->zk23aq<;sc^{r(@l;4kZjl0!OVMnw_QuGLeibY#%K1HRLmwrx{j6K#d9g;*Q$#jI6C(D5FZ}vZo z#4E;=R_mO0pQ}edvvArFuk-Y@NBmAd(Ff-u1BC|nHfI|ZQTq*^f#?b}MP$pf6ZekF zwVeBw=r^UaYmJ|NcR;%#2a#9z?B>aq*@t_$utRq?vq5)C$Zaikv&!hsqPZGU_5HKL8Px>;QtOVw zb<&l~G1Vjb}6XnT?|ILZSgl%`z!q&CH2Rpte7X0g$Sv4;1lB{5k~o+o#YCSouaLc1iX=89f$H zk!=~K)Rr8$Jv)bYo1{jKK|wV~~3P~(%v!tj+JX{mdt*w>Ss-vPbJu{_(sNWPQs$l{@~ zfWjiQi9609nISsr>(7=db3rwiJo{|H72G-V`WmRJwrwXo1!YlZYu z!gr?xru-Qu1_l<ga7Gjh0ZmoqPea$DU@^}Favm?O>k?a?myke(z0s*|fo5A9$pLUDt#paP(junLO{YY(@=MG2r)E(Jg z>#b$8V1raRzByw@Q1M7EV&{z`RB6wSt70WkDCX@t4}R! zDuxbXTg3Q`y^fy|xk11{P;v9W*W9KGs9AWX@;^gXIiFU`KkMDlzTPd`Se$ZmNY^|Q zblf^Qi(hz53YG194DppI&!V@_+ zNYAP6wu#;UbYh~&`@?%xoRw;u=i7tiV)6TPq{CvSPw9wq8y8k#D@wM<)Bg`Jcde2w zIwWcpH9}n;HlG$tytux2CrB6Sj_6CoTPjznVRLkILO{KTGwnGJ{w(RwLuwNLdfuY2H6aEv=5AoAPWZyzv?}IpXqwz2GyQB zasAxtWM@7l=Z+k3Z`i2$4_QfF=KkF82Qa4Jl(h9ZurSs__Yu*897uVA?D^-Tpffpa zSCOFB!A9+>@YK1LwT7o!mgTlK)y!lWvEUmB{aDFmV2}ouOVeN!msxt>m9oa8`y_*u zaDrP@3~Ll>6QVuYc2#Z7*$;rs z#C0negO`Dvi=jUlLm{=0_)VnR<@Gd>IU%GIFG)EHnsrr#G;O(1ey4cs*1;WVws)@F9kfgkK;XSOHEj6v6>chYlrmGSgXtB zBj;`7M=cDEmx~&8Y>o9h=4^CL9z{WKn$t8h3{#aM4FX$D2Nf$vtWAdf)aG>$^-Ee< zRZgpVQu3Mk^RcURb7-orJvPJ9CSgSTaEaU?bK+^}MoIwA2JI&cBxjymJGXF;XADG7 z2{boj-peHSXrA-8vS=(6>QEc{`3-Py`zMS((C-e`HL=tY(DVTrmZi4t492<2a^oH-DM^XK^AjP_=GwsccS3j?Ux>cEtAEwC(&9M+3w|tJu2G^z_In+ z51zlUx#qJ>N#}XP>5CRdGJhG`h3Zyf^oJVjI-TX%edhxi#7?~fLy45>{2iJmTh2kl zFJp^luKl_Ze-y)^JHiHgs$1t$@tM#*-^2XMJ(-9>Aido#FVA#?}!fz%MAK_u9UT zKG%5<;vHp(blLAk!ZBnwn|DCiEFw4x85L_M=!l|MrBri|;N>`+z!Nu)obV3y0j5C@ zq{dv40)%9oCBgkqDo5{WcmP+N>-;5UJ6!%0-YwoV%}KBXxqcXXxa4yGI^m;mH+8^S z!UN}J2t#z29p_DifwuH0G!HG+ZH>`1aT6#YZr25G4Re3x{7Mo-jA%D#T!_32;KDWG zH_0|>R_2Lkl@oF*MJJv!Pix}47qrCSU2h|c`+Nl#D2{bmCo<+nv$^*8aqyOmo1?(> zeeGLiKAVi18wz?guZF#LqR$X^h!rExrdlyjKP3^okG7-l)1>9>r4uxgH%iHyp1vgN za&PRn_s~XpB_Vs6&WVM7$m((|e6rBNRZIlhY^OKT>F!$$RId0AAWW9M`uAF;&qpdn znF6MX0NkQKR`zkaqd_OGkkJ#?{P(p%d@txpl8N~m>{tOq5gEaDAJML(0d?)Y+%Y*| z>C1jyj{Y^4F?)LxA3T%;?u8UH2rNl}Rsd2NuCr2ZI^D^@mWn86tdx%|TlgOeBZQ0_#F&+3=M(z&GppAE+Y)mULDmNO~@4=mN zUq|QqnYWzprv6Fxymu(G+3b3NLuRPU&$?9SknT9vRlWKz&purO`beJH3P_lSxy8oR zAV-Sr1V(lf>Quhk-%o8^>-#lxbKC<#%p1*2vCY5d>N#0%;xj9Pv-~aR>pjDCl8n$T zm^AN32-CEaqDDR%hTgpus)^Z4wi=)9tb@O;twmnn&$0*7W0fTSq1wenU4Va{m`lYP zKSF6}WlYX4fQg>l=<>7UP0wBF5GICn2fxw@?%^Y{67Q>6Mgsc;uenJ}oo4>s#gwBD#+EoN(>?7Hi?0e`xFU67X z_vOl~pA}$VlqW@}Sf$Wr+QT8ij82i}nE*TCm#*mP4#%dARcSt>REvI4uO!Na)t&|}_pWls;?}+kAp`lX{wZRm ziru4cahyZzyewuARCD(n6pA45Y;6Ty>VkOn(2PkdhU;crN1n+#n?y@%wUCtvj7{E2 zWVcL20yKA!Qq5)fX)Y<^Ijvn^Rz0EW2cqc-JhTdf463GO z1=zIda?gwyyvAb7CPqiLt2h6>M&;^v(_EsCeFm@M!7LNpnfaOPM@zcQn0JPd4AvHQ&-4z8$<}ve$`(4G-Ya9Yv`R0xL-?tqU`EmQFORW zO@#Cf$y2hwmZ24b9GWi~wDVukhIAYN%q!i}hX}druujZmx;mI(~ zcwL`MMXKx!wxFa~eMUBB*W7}|>%kk!-yMN&fNBl9h}90fd|ISS0kEGZjv)TSF-@8x z`!9p^=8kp2iLd?>ur2qIpK-FcZOU9xQkDvXSdIx3^04_>{dDNr3cRSf;73x%u_fAh z+LF`|hGdac-YqYL+7$x_UqBKbl#RV%v%NKUfJ?9fEPv$?nYGgFD+Ru*w=H>j+rbUC z;Y9nh+Clk7MB$zGhq~!1UK)McR{GOOj-=D0`{m*eM1Wz2YaiDAO%hP(U0##j#Qg+w z(O0+rKX4qko&VoX3~BjW%<*|2$>|Hxi!V0%*{^K>=j!Ka&q|adYU$QOi}%?Z_PRpV zrHHBa&!!`yD$l;LVSaZ(pVH+(>ddN+BgujB;i%NWk0!gEb|#uT>b*BIb@2w;+I*>G=x>@p#9nm69fn`D9r~&jDqk!z zUoI3GT0O9J@k{8I*R*`RIFsg2O8}D7IcvrJpCs;A_0b348H9i;{2U33h6>L|T^Aut z{&x2;Kg+vRHCD%QxH&sjDL?HCZ8J9EXhgqd&%jyoyzb`~#tYH$a`6nJ4n0&mz;>f7 zpN98porrBZCdJm!T`8UimsQNFIm*>0(JdXHw#uCHr%IIx(HJ63=4Cw|6&u{JBkIy# zjk4XkU;sgXtTpa$FivA0?!_mvk&vrEMLZ~odaqDu=Y9M^``N;xdCft9x(v;lzxavJ zwGS*Vs`tn;CTQAH2rSD{GtSEJ=W8LCkLw12TP%+E2$WBVRLKy>%G3mMG1)mqIA*4 zVl%1MaCuK_`jhre?W?Z(J%L61X`sRojsj#s&K8kW-!q98W0I&CIU*qaEGtKpg(J&ISh!+NI6qN5TBYqBD%$`iO$r;Y1TM0Hl@KV^e0ZYqZ;oahQu(RQe3> zS<`a!;>)nC4y)Lq^PA@g5cb5U-TldHdLi;F~MOJ^yV7GOn91d(;po-r(+DMHu@n?&@xaGXt887uE0YKOX#6D~d&=Om}ZA z42be~NOfz`#UuG(4owBy(`7a8y{1hHbktw+5PH$E#=U^&17`j&s>#rImXi*RYD>kp zd$i*Zf8c-i2wyWvjA@(O8y^>bXrM98g+_G2?HjD1DtbM7aq7k11?9>3t3ufqdmgmjkV4DmUglPEULQU#1`$Gmp$x>ocdpL*M4`p^S~3R0Z{oT~X!-=bw+e ztTyv<<<_wI=4R^cM6~omKevuZ~MWcxtjdEHlBZqI_s8RIg!V8&D#_(vR*62M)gnCYjS|%CwEa;F%m)n7`BCVo%wi4ByV4R1`IVMsAYGxhkz&3N zMJe`Vxv1mJ@%AgTs(uem)9kwoO@&iihaA-{jTMR6*!g_NxWsf_Bb(0(UkqCb=52Ag zSAWtVZz@D~HljU|hqs&M1U=yIL8X2HqtPZKvhJ<3&(vmkS6dj>s`Wl<5U zulxn960BV;p8DCsCceBl}fSZNa*4&+l4kX)WsGf)N|3bwZL-% zD6CV&pQT;hOt|8Lk$d5$y%XeRcgV}w2=wUxUL_SoGpf-;>MkS`m&nEY*B?O-M`hxG z|JnT~@MO3at0z7QWV}3hBlP5n;t3p0jMYtB zyt3I^xG`rHttQj*pZzKS*Z%H}kTc`{a~9;y68?LcE$RL24Y5yS#^bHKJfVAHW4p4N zH{z`SbI`>9Iw&e?E4(kF?$-m+tIzoV-V`2k7Zg^GA`f2Y)XCQkZmp?c`R17?x(n3q zR;;OE`>*R#IGCIHJjnJ}T-zwrv&AX>Lu)9T#7h~cLT`h?w276X=1XbUI&#*Gvb5I$ zz!i5`VRl6g+E82ql(3zXiGrI3O!KrK|GoCTmQ4Ek#C`#gT7g|e@Uz|7Vu+F{$|wJJ zCMujE>e;4uWqTJ1^JZUx~C`1S1Bbcw8cyq z2IE-XzTaq}c7m9qAS(F3&zB*Dk-&G+h$01soP>sZ^E>DY)elb#y9cp(IY>uqYaU#t z9w?qo6xz%IWc!V>pjtcUSBmXL*&=1dPIh^P^rc`uexVMau}dhJEP9wDzgFJMFFj$U zi{{S(f}BwQ@=X~?xottdCnalwqq`BIV2;~%$$4^hMSEZn~4Dz;6cO0St9s=pIp0+LSJcUNfT`~M(--n_7ImlbQ@AX>^Wq?_Kde3zzn|D;Pjs;RJx#uM zdwxfs-NBc}%wmF-@?3c`V<(Q$6|Px22KrXMvUWFBb2_Jk4=#vs6-%tmz3)tdxJ^}9 zcpsrF`q$1iz3oZrr*Au8CTWO6iinyM=xikGqdk=w7|YQFYDuCoydWopOYXM&Yit7= zAKGrc8v|w>$BNc8FU&r779dD#(7w8Yu3`Jv`)PPNyv8~;#+IFI&ei>FH?{ZTj=JL* zfwLP1)WqA#VUNyDvdE~>5%ba6HzVE!jZM|>-JYbH(3V_e z>0h^G!4D%z_egExgxH~%9|m(j79HVqlby2o`SZh|@`_@<>+{x+nsjBHeO(eeITFJ$ zHioega^785yE|bxgFiJ7-1TDK#RCEzA=0R=el?Ddmsq!G;h7ep$LTife?0T0>ikgl zNf?MO(4{e(%BE9_&s0)Qz}yR-!tNvnTf(NbT-1S};O>z0LaPl)El|;9zlZ4M-}%@o zwl-s2WYYr=gf*Da2l1e*j1*vB2Y9TcEGqc&Ww4hn%Xhu@cB}nWO;Ss3qe(3Z=k<0` zuH7NU3Dh+nt-s`I4eJHqk83gH+K_$W_5`t7y1F;F9`pV=Fsg4={**X%Zaj9vq|{V{ ziB!V6l?#^HfV4E6YMDN^2B~^wOUuQ-B3jT7%ue=+zNRq61a zH6g}-l^7K}NcU&=s`RH#Rf6?LFqR;?iM-|Gb|lc>O>9$$wo4+{-G)?G1yOxv_m_Eo zU)2ZRKO)D=Li!qv0>7M9W0p8CTmC$eEzQjQaBzftdn#O1dyyqY(tYqRXeM3_Gla|7 z4rIM~5TzLzn8WS}wP{{oT&OujV`T0ZT+rR0JLBrfEoqm00 z5sax6_mO(28WnwBfMvgy>?B|Z`RCVF@AwrK@B5eY046CHVm5VKG&8^sE)b+x4yI%> z1xY^Uk2aTxenj{0aS%6eLXeIwswNE^UT)F zTYjALdt<&vP;SK zD|W2+Cecpq&yFC<`!`lwbz^2aTh_rY97KPghdVaYL=_mDs*nK>i_0($T*vi~M;drr0!x4@cUE0qaM-)mNIH z4pj7VfHHgI1AKlz3kGafUPQOp!G>v+L|{68?KIIC7#T}63E3_|VX~kVv+1{8{~KPno`hrHv^_9(RA$*S!%f66rcV-XJP z2zm|D+JFf#S?+6T;c;VH2LRv3ZAIe!n+0h#@fdIo8PAX`CT@N0Bt~nDxU)32O|R|d zkZwHP`r!sm1X=nwTIvT;W!x@HeWYm@Zd6d8apm8g&p!(b{9b-{&~xZn$MW|z&77L0 z>P!o7khEC#MH=WdKO){EymOAv*11LyxCReEq1`U;VM!+Durc#Bte1{2OKFr@?G-J{ z0G+YP16ThWYAN zgS6vH^a`QA$=RK6m(N6s-&oXzASUvE$1L+BkN8B1@S!m=it$5NH4{3rU#KE0JmiYh zW3d-~c42b!ocRrJd+i2l8x7JcWK1?!K-HIHTQIEJ%$wO6$q_>mIHj;BuRZWx`3>NPFx%ll+q@^UYc_2o|yVj z5o1h3R#|`|o}Sf25yHgnxQz-S>?{e?rXj3(AOHrgfs7k!4AL%6Y!>$d)&pX!{=McH z!_YF4NiSE0QPk{{mT;9Z-01!?%YTw)JHBm-PQ8S0%oR3CF?Z*uUYxMS`Pg_eD=_%vIf1+&ma4vGG$Ix9)>Tv`Ic-+*^pi6alX}`s=tzxW1IZjD zk6fS!+dyaSOEvS%v`FE}vV3kGaAwM>eIAiliOK?jAFM?=AWR3^<7veTGSdVmjx?&v z&&iOX_J9E#*={sD>BT?g7TfN5J796N5Z6S5bT+934m)tCR{4VzQ+ygoXV|^DtLIm} z$OJ$C{jnU5G>&Q?*E;-A?=137sEY$Rn=z_iI`q`kyJ1N`ql5p==ayE@{k5gj?ia`9kOt%CCvz^={J2^ zwomncvHV{63zWT0j2_M`r5AS#C}> zTSvmRAeob5grC~{@Yj_IewXcn2-W&p;p#{1FyTkKJHiP~FQrN4JfJ?^8wch0rhb+z z(m}m!7Ikiv{8;ZiEIa+Y!1Q<;6jvNBM>g8ox_h9Cm%K}|d$@}=3!jT{kNrA*bq4>e zt@xmQ!i(m`UH;b$Ht*!W4>Myc5_4YG*4n$}X}zM*>@G+XmFHL7)w6;q2uBxtfTBGd zS0cU^ZPkGG`Wb@<)0j~K-XS;r#qFmZ_3NwIzsZnFD+x5m;m|i3->@(Y0ZgL;m7SQbXDzs;N?K7aQ2*QiTkF z`eQ$vSU0i-q_QE-`JgNRb&!Qu-IlfWOz07!r)z%|0H(OOL#7IOUpoDsKTA7~m?F0;T>|J!GoXwx~Uy=rFJ=-u{*vb-VwT7#6XiVF zRN9}jya;_A3C7Mp`?^u^7k)9MxKVHSs?$M*$XfB?Jx3R|wuS18yLPt`3rxXmohFJ7 z=ggI_MunFx+`2 z6_=lf0uN;Q;VrMw8+bl)!1^bz=379Y|a&XnHh)S%9Vb*wiZK=r`7r{p=5Pkfj=>{1Wd>T6R4S zA@-|8OkF33hlh5sPQ?ITmJD_5D6{mF{%&QmOQT8P{pqGIHI%`q{2Ze6dsRNmvJ? z%O(UZ_N@%E$|u`ha2kcmZuR}>Pb(Cb@Rf|zWidNKsidB1p(awJ6`#020Y!v!uk3+;^3y_m=C@%20oCv@u+Mu6Usdo zF-KHd)Y}b4OXSP)!g>i9 zodZ}FEXmj%Xa@MU3I9C-9aL+V5XKXxb@RGN+De>8Nvbrw&SsbN_C9NX$MbcO7ut%E z&@|K8tva|^g;_W52Bd!leeLnDQ(%pc!CA`BEoeoF2}DJ=yHXLv^DjqaP^Cy3BhsQQ28E05iPIyj(v%}dQ&02ujsH*+Z?&kh!?;0&f zdmGv>BAJ(t1|@qA*g@G$yf12}AKb913TtS@>9D48YQFniX@InE1jw>RP~0n7cv+25 z8i}*e)BtdxWX)XC?8*~VvzpwYi_|sv%ZNoaJ9ln8ok&h45l<)m``PNc#q`eCBTKe< zvZxZ}ou!dGfD`%svt?90#ZHPitaC4J}R3Cmt9o_(gQ&pP#U zjWaV}tan?CfV`Blw23sYns;-5%*1&wQ_- zZ1G0`sS6`J#Ku#-S%5QX#d+y_f^budhQq4(bVS=&empo0!?n^-mNEKLtEhG>P&!0Q zjWSbe-d)FaBB8p{v$9x21%s6Rzi7JZfT)`8O$aC{NH3v)Al=>4-MxU)-Q9w8NyF08 zv4k{;pmZ)QB^^t5H@v_5e&6@|Z{zNn*)#Xdotg8T=P{~@k6!C~<6}o11@Wq)Ps7c& zB3jSVw6eULobzR!I>8%mX?ptjAbFZlTWY5(6c`SLSCbpZ=fphQuBI@4QgBYi%LMR~ zeCA>04NU4#O-)Cs3?)WIF2^<_H7~Y3oA!NQvS+=h#MkQ;W>u=iIks+1Aja`r1x3w# zrXQPMYf^q3R52;6sl5TjDk`4G9teC#s$xqMk2wE9syx?sdRQqdGlbtVo1cXu)=qjm zV8{V-QM~im~_Q7yJPOToiN16pYaosW3L}k`V?975kb5?X8zxrtWHdURJ3^F$wR6 zj7?Hmw?>Nre($21=UM;S*FC&_8}*TWZ&>^@17c|2nosC4@m4*_{UP6Mk0@dJL0{-} zdY4R~(ITdJF+i1ej~<+7Ua7ft-_s<&@t&vTj5iVty=l00ZctpL!Zi&oAu~-`_kd41 z2OF7onQSgh`#pE8t8i<=*)1TTKvDRE8Yt&nBM~x3MnBLJ(KN**?a!wZ=)RC*Bz)WUkI{6dyW~Tb8 zfl$j1=Y>~Y_5SFhe)19(MyI!>PFdEL`&eqxh@yUr@GxVU2_&8;#XA7*rF(dM7{{ru zAE4~g>)GPrn(b;4ZiPwF_+M9w{{{*=w`Pkvn{-T0K_%f!H@ z7~fbiWayOby`^SiM1|uVj7s`Z<60v-QMh#Zgp!>w#ZC19ve-2J$I)#Y_NP6k9UMA$ zB(zbX54NBm5P0D=xG${r$i2b-djB5SMU}~T((1pY*U28Xiu>e*t?KQ6VTwofVI0u$ z;Siu8B$DSPlg;2#5VMOfAK#NzQK$*LjBl1}VQE1>)v21zVAfOh>R1r2;+vUWbaoRb z@(HwEy0{^;iu7Uar}$b>UHwCUmJUJbj;gVS1;I9uhZOegEiAdJOm*4 zV5c5#ewj0h#U)3~nxSsVIT{(gtl|LSdiHxHcUD)9UGOEQuQxVx3)z3WJ1&qi?DbKY))%ijzqw+OA*Tx>~7g+*B zhy;H<_-9wJLSK$$T#r@|oF|>0c|~bC2W(DhkrI;}6Jtof*rGLFrNlOT?#KK7xUJpU z{(v~!k9lRT!_pCVNwfwo zeX68WFG8kusYGEAA(l5oIy_%${-swj&1yyvD3SnQ-%t{b(Px84AWMrK3y2~LHmXkE z5AR1SWyMZR90{gW`2J4j_cR#U-5)ox)ZOtDyFKN-;mw7zi29TyW!oeBnc^Gjo z9tyO|`_Kg0Mrkn0;WWoC<8owD-O^OQNHP`9x5``^V2HC8F1hk1U6sH1=mf6lWLxQq zhpDmkO!XjYiR>SwSM{tx##e&*i?kA_DlO4Gxxp%vhaUNF_Gd4aZd;__SIF*Y#oJ!z zi!N4Jv}L1vtw;1fy$?GN&I9zAdKFja-1pjX`6BB|h#m9NV6L1h*^b-U*Srj=+h2P* ztNUM;Lk97OXWWso=EK)z^wOuzNvmSHnP84d=`bP_duOMap9VRh3x6ql2$dZCG5gE< zVM;iUgxYz36TD^=puabARN`5Ybq*o4k2vt+_U6HL-&)<+rv(v6%}iFltNoWa9RtVP z{*|UF0>UQF0G(!G-WG;!t0RqD)w#`N$ti*R>3@($C3gp&Mwg5n64JtIjl!q16aM7U z-hK`X{>fbIBT&XdCW80e>E3&V;mgn|A~OYd3P}%M~xdA|yc-pXwP^~ymUEFquK~h1 zzq0QKtcTBw1@4?IPcF+6h`uc>S4Xs?``g`|$Y*SzNO3l;xpT_Tv?TEjero%CHRtq! zA`V>%(Z~o-4cuF$itm{?dw$n?ZAaj)oDPw6KL<|yguFwDzJRNukU0==k!UBovppuP zpM|XMOOs0AOS_Qh)$brDxC!BRBGex{L<$vrw9!2de)5!#XDdLCA=i|}fC~J5CEnzW zp8J%LLA|@;oin-ScH6JEu1*4@o3o-Ni|RY~H!w|R;yjP(%-DFo5b~&icQGw5-`8We z?GRSqRcjP{Y2CS-iS4wyP>;d#AL4)CXadISCvwlZ9tj3M<){md-;T~VoBh4;4j zS$^2VcDjOW$hiOdt|~|x=#n@>m*t9i1j|3xU*WN-J#i#gJ8)~<^)ALoM3)xTK&Oyd zam-YuILCBtwM%-#>wkjyj~7K4!q^aIvCrHg&(sp2IFIVe0PFQ;*^L6 z$%i2Yg3&WlY=N?`NP$y#(_t1JQXZr{rF^jE3Oc^)K90&8LpD^!8!Yv~;+z?2&A$=S zPJqJQfi^)ucYIT^@>ac}Ppk`ZC5&|O3#H>5M*M}c@gCF?A^qo5&S~xEcYB|MLv_+Q z21>N@XnqnBZaBN9dncBG@4j$#^)r8hIs4>U&{4!(ax{sqXpQ%4-Ly?AdZ1Q5=sFdN z5WD)OaF{9{tECy|OvaVbB@OStfmNBD9fWHb-5ba1FH{Q$IhB@1c4}o_y5h~l)!Ep= zWXKf8Pl@Sk)i-J%mu*hRiw0Lay?Qd zw?e*Ei@rkr2MKC0QqvSsr8Lys;eVIx#mc3l$QuL@T@(^{ek<*DAjy+F%v# zSz&RG3F1Em->2ngr>D`pXPqOZ*Z|V;p4~HfU0Svq^swJTq+`{!tr$QWwB+ne4bZ*Ul7dPUDZ;|-vfZ`4Y?P>KU5|_`wj!MdQH&0Pp=8E5xapon@rt;Sn ziQmNp{78xLSQ{y#oEAxiami(AC#$~}2=uTVmnLZ>vBr675lkEOou(H@4>O<8b)S!T z1{YhQ_#%HW+yON*@gTY76GOCa%IPiF9F8jKgb@#voT9Bg<*MWwlr=xC8MPHc0zo(2 z=b$v2WZ!~U=o=jJ1r+#+hDhHiPJ=PsLS_9lEx@t>aYKN+Jof}E{aZ74_?Hgew@As| z!A#!*dg9bkXR;zV@22ZJ%o$pUSVogTv^-m8c%$x`s4W)T8UEIE>L?9)p%DGLyK>}e z_wGkKK9S4HgpDK$0P%LEjmsgxTdZ8HmxJF;QvnXuhs@Hu(XyTI-mS{tI+ z)ThH!$l5#0Q#{hUNs8HQknDau}!lXig$nk2YD^mIk{@9CBhVPp_{P2hiy?J#z=phc7Gd$EsHJIZS{4 zjG0w}uwusD+0nb7r6kx%;4W?ay!v|sNPVI*U%@?T9xOd z3-AonIOIh@lv4U$$jeXWUMDC~VC!j>_FOZ|uPqKfEP2+%vG`_N=O(0m|8Asr44mT` z=5#@$EF0-@N_2H(i()IEZ+hqyHohq(lHXg(qnaH!% zeee41BZ~cUUp5f^Mk#qv_0tnJgixs=I%RFd#B6Ukj#qd(W6yW!vShrdP=Bg0OU{&b zOm6)8mAozn1G#R+#pT;Q1XMovV+~PLW<9`zN{jJ*Z0sR@*HGr-x_P|1vaB?RgshtE zo)v)%&C|KRBOtye_+O`fucQvhy%5XYQL(*G@W3@^fb^MIQjuYg(?Wp1h3C3-<_=NV z1E&w48bC5xI$7&X@DWw6OFFi4!eI%+M$#1*gT2uQ< zjrt4pC`OH0E%K(+*rw%mdHbrv4>J7_EYwgyy>%1gkuV$JXA!slw}y7$cMxHco?5nH z%=zq!cn6`I(6=rP)A2l#Gh%ju4aLiP;lANjI#~TiHMYy0U9M>3l1hNWMFCWimQX@yzD0(^-6YvD94T*+OA6rp!=aBq#9l?$fa14gC()t&l zc$K~BqNB~wnYfGu2htjf$Y7?k@pblipNrp~%lMxY#A3;_3socejwciBHr91evu_HB zMJ`>3*H9!_`$rIIB{z!Cc1sAs?wlD+U5B;j%oxjac&;owZSU`TANvaUb%=~S^(~%M zpC`o`M^mRLl;J^?oziIRrnd7F5Vhis{$Q3B%+Gs)59Ln89Xu`sxyutilhd&mkGbZ54$f#$GtHmcNp4~| z>z!>MP1kKsg0=Hnw&{u_V_=~FT4c>&qv8lmI=h|m8Yl%?=Pm+TL#2cXR?|EqD zPIAlrM1AHD&>rNGpLsnK09t`$U3BB+*q`3#+s2QU#IN7=8xuTE_Qi&%nWSE}(URFc z*wb!0sZkEH#y#s;vf+BA7|0XGa*TD48AeZEfX^oadh^WkuHJp)I{kH(q=%kD@kXQL z&46868;k9q#4^RWWI-ZncO3SlMlJ7@ej7SmxiifSGdUiz7_z7Z9DXnKz$d#l1v6(# zes_5=RT#D5ciFqhw$h#1MhV7YY>18Z!~ElcdW?S^@i!Yi**yiw1nHLGKD#RmWkX+0YWCZH=agb112kl%OTs2s` zQ4@-_Vo`vH7R`4Fp7u{jHfC6sazt-=>M6ohKN=lRC`U%TML>KQ$i`m8ke11o5Cr_p zhc}?eBsFh}(kP_lxXy;k-E}7zhH*UKh)omFv?EGwOX`ON`fNh%1)j+hB!skNEo~b* zVxVlUgV4z=Lyg8$9OW(%|1U>cTI0$*BGQLAtLhfII(Mwkf90^0B*g`EgV2zJYjX95J9ntmJnjfJ(e6 zUV0>zj0R~Jxj1p^8LqR&}onK#1g~9j)vHogJmx<)t4x6FF zIiMr*yL4ZixbfCAn#gfHH+^+tZBXLzpmg%mn4#OyoYHD##opNL<29U}l^`*|UvKV{ zS*}3QX8jDf}+qd?@x_b0|iT766W$TfE;ZQjLSv>nJ(=PSOzVW1Sg5Xa|V4K5sif4`6;k=vC=3Zs_$LaKj5{r(TJiG@K{=+ zoO-YRA_hSQQ_OJbX1>?a$6!W1pZfjwlg1~KAP|7MDMP1Td1ORFM?Hp^a_l(C-A>D+ zVIvQfp|)CRhj<`(JfC&2QNJw2)t$shcO>BA`I&P1rQE4Y{l)syZLV0Ou>Py@wE!Qk zoT4|Rst<|EN!ER}1l<9sr2&^C7ylkdsH~2=-fPiIfK%eMoz~mQ%d)l( zHSo-S#-4|$ur`FoWo@YlKGNR`8vghwly1WGvwnxBE*c;rPjQ@ zFlgbd5Y#v@?8S6{M?ucBEZKOF&*9eA1NDSwu%x__gWxhKU{`Qq#GiM@OeKPkYYm0+ z)cza}(OAOhD-Z5tj1~j4L*ahcWt!kRhuxk5NTsZ3eB;fJJ9RcyZcVVh@j~0hCugYJ zhmrK1wj-L75jTu+Un0UBY^(AWI=VNH;w3_?eCLP93B&-2Rt;xr?l;xq&!7gyo}RkJ ze4)LTmK%TF&;pr7c*pM4wW|nN##%Q>^7roY{bawrIYiRkov4~sxCM2t)17}V+0>W^ z-V%gS;oRz;LI=0*n`jNX2Z>6* zroB7!l|U64ftA8^CUB#X$YTHYifL4Hl`k?x{O~Muu8Z9yv51m+0A=;Jn^ACqtuEp< zM{k%HS;HOVzM3%DE$jSta8+xf#?zR+XMxTyIQ%CA=ED5qk^VUshWD8Hqrq*{`>sfy zV6RLZrd_bE8Rb$ zN6M2C*aIS47m|b992NY6Y|aStMFAy%bm4C#w%;|5eFOS3YXtw`>#^GA_ z&sj3?zO|HREY2d(u39>zjp4*B`)5MgS+Uh7C=m=l8Kma;KB`ADCQ5QuH2T&G@)2kA z0%DtDJMME`8MBL|@EM{@7s4$44v(i2R4~wKkDrKv6=FNseWv^3_mDWN3h2IerIb1O z-KB{CRAd3KbTV`N5(T+yQq(3Mlmm|&)uzbbIz_Pu`5n#$rq^Qvd0{H3{tv{h&7=v> zg@R6V9!IJ5RI(T)iNCakdCEJDi-nB1bJ2YTY}R%#O)U|zZn9pI_2spZFLe7_gQoq> zI!;fTOa@9~@PPG{x0XVN$eWp<8=}F``nHm`zrHgDq6(tVXE|<E4**eT0e7;P83>wegTE-_@s^1 z&#+)^t!kD+t9})+P2ZVQVnDTSYJ8O-dO{Ui*B)@nAwluycJ6-B-RJ@*vNB>z6Q-{M z0#_vjuf28Mq6V7!(YFyqiS9lOS@~j1_d4ojCw#65hU4-a_W1PU;3z&?)f9(SD-|fP zYj`tP7J$`uHWR+m1Z;eTV@JN%@n?|&6U(}(!k1e@wVVN^45|9|<{zza@;`f)4Mq{; zMZA*>w5Yd|&uIhthJ0zY`$0l8aVZA#^E~8=SDIyS@oyo=TlGC;;1ix)Iq(@q$fZAy z$s}|w!g;AfY;Dnp;i9sOI9&ixZcl`#d_QbUS&`^KCCJw*-WR;f>= zyXLFpGUVsF>}v%b8hte+DA83#eMRO*f=|I+QNKve)Q-4IZi6Le>DpSSc%%M7 zYAmuE?&FmIje1_&;}-K5P=1-txl#m>nxco==5oxbdPYGucVkWp&rA@qoCm#Co`4ok zyH_1LBfc$udZHg=CseyVtLl_moy@^$_zLM8DHw_WAQ>wr??BxZ>66TFc0z1?2`Q}7 zGLJb1xkH$^*#*RNd7>Q5TWMpgw#b(3qTERQYAJ0CZ+}zA>oHRFgjKPErZ1gr-RicH z-}NH{r;!>z5y-m@ls&#qP(`(oZ?=EIqcYCfG^xVYDTdO#L|N_IN#GrK-6X6ZIwF>~ zi&28q&+P>k7vjX~#0riVYJVy8_DXu-`mu2XPUAU3HGtj<|{OR~ujU zQDnpiRB?MhX%7}IJ_Y)eVZ3!?7DPIJwFZy;y`~-V)cuIKD)8GbQb+%U`|9xnfFYIQ z6ScS=y)4;lx0$m`^!*sc-@hYmytbB_JC*kX;jxDIW!gba4kY!uUmj z;tT0!Svb{qh&%;kBAHBKKyCKBC6NYJ%AaQs-vLEAFyG4>TU&bn)RHk3F2*-;Itm+$ z(^|1SAI3-oY0826!3^qLn@qAB{BK@gw4Gju!Jr-zRYVpTg5ylv8PKs$x`ODt4-a~l zWaq=1(`85R%pBbf6ZUi$CZA29C(08nU5#w1M0eU-@gA!Sv${kAPo}uXp1UHA3q{9g zYFce>SG#vqepK=dG4b6D=#})vT(2eiBpFc}qsYjQYp;3F-!5)*z~FUBM&E*9+|0pEW2Q5RVe_l~oH>6oZ!Z#J zf;+9+2j~JM3#Pc8?CNFhVNpUjf!u1A5KK;^9*n1`ylGBfJ}NO#$g{TOQSpt)Q+SnL zoYiRCiJ3#$XXli6<|A=9jGsizF8$CwV`41u?BlR-z$>wCWs7E{&)a4l!RU7FH(@_T zU2FH}yp?BLKFa6=OdWfdwe}drPCmEg6{4!Eh@I%{zGo~r^>Bi)QLJg>vak8hMG%AD#D{LUGyhVqD67owjch4ka!0K(Pa4W zAfXR-KQT(-=lSUr;Eei?AxCZIBwV>-B_C`2t{`gNoGG9R_kBABm%uO`ug36=!!Yg7 z`LQ{H`nA&Usz6bQdPf460DpYn>lD}P$%fGeMb*t#R{{?`G%^ca?DL)0%zP`3K^+kV z)z#YoY-)G|a`)ONs>b3ZNv<~nf_=ht35^6;^pk&{wc(4241~$`nPH&j6s4rR#-VTA zpM!4pqThsHjE_UBV@;Kwk&m+W~|Kl*~V}G(e;RustIx%2`Ji8csDbO`~+8d09p7 z4*DiR?b2!0WcW4wDA-ddzrR{84x9Y4=sjy9cUs?kq5$PSgMi=@({}^xNN2@bVF#5% z0SyTBi?exh?$&e@I)F~qzrEL!vbCoy#(JR?hdF?EqA+;SuawRiN)PrXEU+ehv@gHN z+cYfAr+mtON6i&iaZ>|kn`QD#U`~7urGv?#G56JUa|sZ>mfWi1tzUTIn#@=xFjz3^ zOuIcsJ6c|{Sg;-9$DBFmZM9)e@AFI^76cC#x}>Gj2ex?`yt3o&>Mbz)I&0nD%?SJP<&HDOAXR4)h><@QbqHwy@AALD)CW+bfQECURK+OH8FlU{P{lVr5(#?WpPPz=L{# z_ifc_nba+Q`F{Q1Thks6rxv)wqVocm8y2E7U~XgZIHeEbkioh3R3XT~aDr-rOM*oq z%GlVEwNdGBPq{DbC9W8txf3@RNgLOM8h0MJf7IBC<~EA;4NUo$zGiV4RE4>jWz(1( z!r5M`P$x7?5Ur3*HZFF}*blxb-;&xUx1i(?Ugen$a`~QF!!xW#_Q+TbEXvXJK77S> z-;=ley*af-r^Tj1I|fq}RL6aT`*k~a`@_1eTPkQy)K({7aCtTs$@#6vi6RA6lZ0?V zcNs14JbCq^tdwzhM-XEHiuAN(eWx`mLmPHK6Y|{*H?Tsb8)k^~0!ZE;Onr@=CP#8f zYu^FVK&k}!8wC^md16jX@=LXTNNx%+YN&kexE(330hd?O?R3=R#&@HTElY|T(LHa9 zPukJ3ogq6-jBcuE3~rRZjnQjf9_V!FNr{(o0ezGE0 zRLIM3u5lZ2xKPQ|77#6kt40TrW7GEuDYYW+@b6w5mx=XyTh3}n zbthKD`Dcv4}YM{TAZ5v>U-wGs~sQH9rM;M`r)mLb?Yhbf4>#}z@? zKPbu!VJPM~ZRPnI!Yswb=WIAO_8r}At!dteq9c+Z3u?S6}O5TQ1(;!_n5)wfu<&t}30mc32b-KT*}9sv}y zcy2?|MQvl~>S_YzTSynDUSTR1FHcnev?dGX=_Zv5`vMsW1?ud)4;RtYez{+<-Yw@& zzrsBR?VZR(2nfOi%9wMm9xJfD9s^byX`8tp%Rji)!%U$994EIEs4bk7wg>b+cI6?z ztOQ=btK&OD?Lz!U+cu|uj-Deyk&JH-k`^+?P>&~&-tVC2Bxg`SkleaNy#sC6$B#js zXt-Rj|B6hde^eXxEhyZ-Mex$JjD!J4p)T(69qGjhQVE@Qbf~?Vrcdap3Mm6qkYpo@ zSE$=NJOvddHgx=mW9pK3Rv0b?qc5^dQrEoQK5*mbT+$xM|8Q;2|44=bBKOeg0rWOc2a!T zUgE$ZKvBhUeH5aWR0Us7M_DC83O(9HW~3)z?Xlvs4K*8xB%@wT5>C`<9R+nniI*mc z=IgH%L}lz4ou0j|$!|XoRv;sI3+qUhzxK(!y{m8eJ7GxUR@N!KQ#M<{Sr@vtu*7nf z7s)$ixFqL}IR<)KH~y>FF7ngED~W?lF$rVjs-|Zw3Ae;2oj9J>MgA_(PmjGbHFx6G?by4k$93_aV<mN=ZVFBFRz*47=|#hfpjsu+v+ z`uXZNZ|w8jtoOOkCbkY$urL3;qz&b=H4`iSWn%rxjsywejL@TTvQIFV$pmdzsVn4> zo-MRl#j>(Q-m0kncr9jCO(Ex|ZRSH*7`-McardQIxOp35Avj=Iqbbd@tUaYb`Y@wg zhwM3k4gs6<93q)wKqNnWp36VcL{_>ctoT5QgXV{4fQr2~mDbQLP+oUU4LaJP+$qST7Emxyl7%zP9+p{QGHbw$p|df_J4PFO!dad>LXT4CD?QD zbjyy7+xiqTzpD?V+)c%ngX9ackeTzupFHxcgxGB8)bD_>kTaLeMhV2BFsBLQoNT}k zwH^49|0k4mP7?~TS+jt=pk-uM{_B4xfjVs-7B``(M?1cKS57L*RAY-jB>3fr@XX+* zftDYnzj8lO4OQzfX(-fH`3ER(K4s=sOZD(ZacLaa;+(yy~! zinlEC%#Ytt#IHG?-4PBcZVN$|!kZVp~jxHD*>`!q{r z#3iO^I=v=C`_wtk;!NeLCYjq=(41Cx~Ym z$oKo}nuLD`@2)$X?C5AQj0;({)+aXz)lJ^0)i(uF;i1jj))pB=#0~v$Jh}Osb1X8K zf17C1H1|xqFJ;_9@_GPsfFd?$Am*}7O;t`9@x+HEWsjR(&f*1VvG~{WZauRTu*V&F zE42m9P%QzFMW%xrhn)(4%H|Eh$64dX)-8PEaMQDZze=-iplZ_-SQu&bgm46N%D*Dk)Jy3TknjGIbEY+n z`V+o{thyr6ZzCaS5FBlDf$G{^{(0L=L)AcF(5^f+9J{BYjQRNHM4MBtp66-{NG@IN zqJZO&7$?&)&|}l^_eR`o5pOp!V^bA61ehi0_!)RYTmo{CZ>MgEc)5_hSQb>mF{P<#@q1%`6u$o%Poduw50?^ioo#~-ANq49r)npuuDc282L6fL#9Vkg z%+EL-Q^sN~if&(%MV_|8fa;0@A<0Xdr;Lpysl{aEz-ZE#d_6>I$EaUl$xCB3pruiN z6jNYfjn+mFDL@{A;%8##$;iKaYu5PgSUxXAJKMJp1x10BhX}Pq69u{jUl?dh6iu(M z+3`=mpaYJEnT~q~j;KudDz~?!2?+4k)j8BT?0+z1(=a%=;P4z{AkBWw)Z?EcckWf; zyPnh@jHOzB61&iAJTQ6x^Hy&onzv~xU1p9Iu~G%g=zcLLOI;D~6qy;I&iD@!7UNIF z8#}rA$w~9Qslpn&HG9M3st=G?#XD-6>Md`WD}7jX;@_4$iV+UHQO^-?cW!2|J<)C0h}x7h*I@&X>&kA0v)euC{)$ zH4+EWPt8-i4Y`8iSN%^g2k;+>&Giq%oY%BtZ+vT0QdPNn@p9aC&EENffZ?#Jdj`>qjSedsg8Khe38Ihr(QQSJN0WU7vl+>-tt1Y6)k9gr*c1>e@)nb5dIF z;l!XHOi^Ye?^+3+8FJL$vX9G9SsmLT|IR44GjY#fFDOsI0I)@;6T_iN3)5f_=+9Xl7Q*WsbqBN53j;qQ6^)bb9 z99!`+-QUp1#Kc}Q*^;wjR4})hZm22Q_2HqN7Xw&&`VIsP42cpJS$^ryoK(C9CqLh zO(T^>lGrY6Pm>j{*6*^CM)WRFb&1uVuwKOS)6TgEYN6C<4c0nE6Irjz!vrrlzx zW({qDNetDqrZql5$|y10+o^Ghz}K#*^o5fGPd|eokjf4-V-(#d3GhCNDMpnP?Ehfq zHCsM(h?){N9mC~A^!;s`8|cNOwwwSK z&>8ZNBTo~5>U)FHQjBf9F$4E49Gdy&h8tjXacK_NGvx};?{4Q_@4qlhP3~R8iZQ{(qYv-15;7I|PrIOKJd_g7X@07p<#nr*!#d5S21sKc`V5>9*Nv(H@3S9iCw_3B3Nqd@N zpQTPHtV^(HJqkqSs?Y%Cv|e`f(66g7)T^?%wB?qZ37g15t4*)V3OSU@HBrvSm?VxM zP5!Ip)>3XOs(twvZ(oI+ynYsELYx__=L21<9Y>IHp}&!i(j1qjmQd za>u#k5DGtZyM->~NXM8Hx>0_d+m@iai5tGD6P0a2O_;&r^SjxAVq4@jVewtB1u}it zmLB(Ona!XGn2~wiK=rhbVcro2Z;WPrm?LM;Bxt}++)}YMPjZRZVsc`ankY2Z2G_b( z$F{CPsyMoTQ_MDw#lW$t(m-XxRRA7uDQ+_^#(XVqqkJzKbdRi&E{cP`$z_qp#P`Y9 z;R=8p`s2&H*p4EB!vI{<2MUXrWE!tf8QV8pHc>D|1?!l3Z~3KQAvmX7S`t^PIq|$= zprXO~x5uiL%3lB+i|M39MUqO?4i07hHe7%r2bE&Dk${f-zODyxFfqzvi~(q=acQ(4 z+#eCZv4bJO=jHwDd-h%srRAk*r*ib`!q#P~OUC-;=-gVt>-Nzd}Zvx+Bu=SP>O!1WS z%@ab@j(JHe`wVgmfQjFCc17ml+g}S(T8IbM|K4u%H#%Sr#$L81&p{arC}m~~IukuO zaMIy5&y4*u92)qvESt`q|iPoCp(2d0rj!t0Vk#RPwbWe$=gza4%f~ zn!-!eQ2Q;I0EG9F>dK79+p4V++42=Ba%HbFXNWySLSHH@!A|>=YVc5XN$Vs{a@j&L z#?)DLEygpOQB>j%O77`L4R@SHO-~^h zoZ+G0#l$c$I_1kkY(6C6%5txiN`2`TyUfy{Tk|SJ7kKvQNR@vDGn9}Onj|P{Z99rn z7rUO44wdLl>cgOOu~Wc(HEyG+eu8mPF#3(pFA&$ZJ3@eLZ%>ko0+}L35l?_(LV;-$ zh@G5G@>k!U1_bHz@W^%^nY^VFmRvjLX46RXm?XShI3X{`kWg!f89`{SF-S2VB?g*g z=zWHuz{`UBGmZR?oEaKKJ$Z7FxonU_igtt?V~^E$Phlg`Tc~+kHk0C_6sqOiihQ7a5Dl_$9s9V`)r7vunrz4QyADTN!u(U7%3DEFJGd}eJ%Bi(>kP%ekQYm9 zq}lq;S@B^?&w|SiMr%g6m~RNIC2_&WH?<#gdDXOrYE5+Vzg7Cc?g**S<$~;`a>6s? z!k+fS&jl)@O6sv=&(G8c@76AiBqAhSka?z5FiqJ zVzw@0^;xe#@$Nd=BMIW|o%MUB!lo;EkyUEzVxjQbS`q#=Rw)e|iwDcjI{9;7Cclk_ z&!_y?5z$Xj?|eS{AY!$SFjMWkKCJ{OmH*kzfM0MV%MlOW3p?t)x&jh>rc*q>b9>W9 zu`c@xQ~ndH*7!}B+D~Q*xlT&#gJ7JzROiFYwPoNeHW*Dv_<;KEG4doBCi%>}c0N+S zol;X|rW&+}_w-0)M)|QDJMb6v!@YQaTN#?yw7?Kb-R*-dg)Z;R#w&4#=yQbUi&A zy4fwLgL72@hB6eAM|*zNGV2?Zzj8B|)n^p|yX)%JKS<<~m3yE5`)&((XEI)EeM3NN z2hj(!l{hDK9Yo$v;X;cM&ey5bHf+~bT7V9Z zJo7xIsltwFMscB~RN{?LfKly!@~H~waT?GcYJ}3f_Tlq-ao_U{d}#5h1mo%1^sKy15JPFPoo!*1JL4n&nC8TJT!^_oJ{XTV>a$3P)ft=KgI$ zJngAZI_1G!k@|uNpxODi+f~(jWUy-}&`Hy?qW@Un3V;4PIUv4p{a>@iD%Nt${ZPQh z7c99S6_2&YmePI)LVPdDn`)n5umjyRoQwv#+0@c<@^2$L!s2bI@{sKqF5IgB{OFmN z%ifRH^{LkWUlY$fC8{OO!S0M)rbp1%@m-6=UAM2h5Wo-OK)EIWYYH%6cB1i4uw(OW z=}7RzNbsQ9e?PS$KaS`T|KIhhJe6L-?i7Z2M@QJ$IClTLLg+X)=N}{oL!c%LAW@1Z zDA8(o$lRQB6>ss79Gp1{K7AM5pYaeZ)vsFr->&g={yS=B?VzWQ?+NXlHNgk#!Tq^U zZQs3SS^sl&qNQHLLlzem?~@`u|)0 zFy{f>$DCE-k1zjqs}67pfih$}tV4X!D{pm=@tFrvyG#28x)8kfF*siB2NZCtl|@)D zIVuHTi!I5FFnDl0yS34s=~B1pt-OfX z(mzO!^UwSW&pgl#JiI*FmVfO44L_whZw0%&Zi(W-@3GhQy>3@KD_=F8Kpvd?*C*or zwez~b|3SuXzYfodcbod}kTJ|PY6{FAiT~?js8+=Y;5FC3*4M{TLI-@m{z2LU20qw% zSHk+i>VIQABb1xJ)GtTh^!<|je~vFqF`f+G<^Jz|!dq-RefW&vs)qE(hXywtb^U9( zQmuNAx~Fmh2oQGQy!$h=!{}y~W}uN`(0^j?RmP)TBji^)E{SI3^FK(yCB_27i=+#B z6Ei>GNg*S6)l%M5wMXY(0i@J|fyVf!FMpapDB?Xlb|DPTwGX}e zOT>1+Tsg7;xU%SfBj51L74$uld3n#RBD4Nf`L*R628aakx_6^hp$!DI}^1q1ezg;J5z>pt)CG?QL*w;>z z^HPL_M>&87gHP7VY6{KpNG59^s6EbWwfok(Ci^}{VA`EyT|$-i?k#_go77Ydmt zh$`Y-{Na{~%(>bqzgZ^H7lTNHu=ySTtbZ`#KVOjiY#^gKW`8);Z5*V1u&i-oD*`5p zP{>VsvBfg5)eDT77$0Cjfc~z{y=#xFItrJgxM-czS?d&#F0Te+_)m$-IJjYsCM#LPK8DhYYne(zer2>QW=AKe74USe>)#MzF)=(vik| z)M0Ylke(@NO3T4^@Me{L@ubvbc&6 z;=yO3`5)_zM{eG2Jl}gFZT#dbPGlEW(aA_I585$=dFaUK zW~Tk&Xn4G5X9lEm|KV6j2hDS}VR+z*EmBTLsJ*Z1cBA33UZU)6up9q2zI68G{umdeQsE}&bC=3f!m`1$ z*6&9V!%MKFqjhG2mDYlTE8Fn=XB(ubsBg$;4jLKI3RrlXj>Ljyjh~J80uL7h-?P+G z{D?ixC4lURK07?ukkZ^~UQ^$K!Xc|nwpHY7`ONtpbcp2#m>mJ%oi|U=FN(vD2!A+! zh#Bxwl%DBXOV`Tc{Qyr4vG+P#;(i$9DfL2D5a(FJzgR;>JoMY2=we3Z5TJ3l}4ae#Ewi*zm%{u>3_#%P;!&XM2{9;bKX<7&0}qWQHJm8}zdS}L{) zzhLC9Wd{rSyL9l|U#3?&Y^bZ-O9s&fG0=%0E*^QVWe#?EB>*+t?MjyO(^m)z7keut zNfZ<|kmJnvYH;kjx({(~1WLb}C_82tyZA)F(|LqB29{l1DM{P`P$&3?Bb}lvY zN+Q)j(C8pj$$X>qd z37~gvsEX+dFv2BWRgGYo8l5BKhf`-6A^b)c1J?e#r0|S1CEy>p*e4g5Z3lpR&oSCq z@c1$H7E~+OJ7Ks@C4w`=oLk<0KOiAe932RF-3@V*_q7_<3bwcIQO$=Tys7^lmB?`a zr*78&|8U)H;g~iTDatH9N-0S+yj_BKt8YhHX7L67D(c~qP0wsK_?a4ye0$7gDxgi* z^k!(cb^Z<%cFnJ-=?X;)qr}&dmmQ(L{{=kC{?F{#>H7=Dn(6=h4~nTm8wiFhJ*&MD zK*7Md%2p@qeD&%_+%sNbM4oUTL4N>AD6H73%VpHPgl{^MIG$$22WPv<9zmJC-{5@Z zH@DwBOj+nO-v%#uB#`gloez&=o9KUlta}r~Ko2yZBP9ifyMI))r#bAA_465{Kidok zFE&4tlUJG$ogDSQ=|)!~9r@hzn_PmRU7M@|ge7>TC!we#-ySIoW4rQTAUhV>k*$yG zEpwijTlw!j*4u7FoK-_gG7X1YxJuv}1Ed1Wk(?Rz{2|aQ{$IJ629_dw+}AbMaXOg9 z4a;iBo*p7P3_s@is4y}VVWsiw<*^vx+4(*#oYuF#)Endvzlxg6%6>)e1zuXdW0i9; zJ$?3HtA@-`M@~e62C(N5?IUBX)8ik4!LQ#rn&0rS5|-wFxh$betYYdPy+%A9o{Q#U zk1e>!yfg-_ltAk>48Oi`KQAYjVd#k8{K?v{JP@@1noXnzy_2B` zQ+1|4pAkBG6?0~lb$OVWrw)>_Nd|b{WM%ik)sJ2=K~>BLlBcO$ZJ=|N(65{9#{Lx1 z^vct(Jik)D^toE`MUYDu**;4O;nBar32d;*jl*fZyR-+r z>{AG~>HXMVcJqHmMS?&;j3>&npL*h z-MnZj-hV{qd#yavL~XGs^4KRC!}2ut!r0N% z<$6LAmZx1b8rbu>A$=z-!jmvfevpSbMrp)jPWU)1p~}`#wWDto(4eoN#>U2iBjXs# z@0|y#E;-;gUfCb}N__8c4wJwPNL_8Dj^i0N{yBzVX6$3L8Ip)LgSuJ1$sc6o#O&V; zkD(gluBDKhbCUOF|54Gg)vt`fgR2(__;t)Bx^26TZ~n6%^&t@s2p!dxQ-3(}u?Uea z`%!IE%T99;Xh)|wtys(XKj!D(GnNOlzM*QdWfZT6#67l*SF=kNSwW4<+dtpa-j*}1 zgs#2_=0&*jU4okx%|0A9xCAqvHCOe|T!Oq-H8wan<#PJaW+FhuGn-NsTCIw+u>E}j zB*q;z?(lzJo-GbQyUG5^r4Z2f^yPL@ZiDjjYxQZ<^&R7X-8=DlSJ$&umw=;~<0cT_ zCe>)oO6^dK7^;GeUBH#1r^v4o+0-s50o?_ERIY8vudp{2z&z13Pg%3c5&Z7~cZZ_J zqvm82TB6?3!vbE2-lwi9$3=5GnTej#*S_dkv#&H^PIDU$J#xG~f)!lNskUnnrIzI$ zm<;pCh!*l{kj*m9*HxyR8`B$HtmB9a(4g8EW2wbW_nbY2DEdL=du8y{awSlJ)r;a~iNWOyIy5Cu4ZuOLOiznDKWDt(rGbpj0+wW{ zn8u%41ro?_<^2~7yq0oTB9!}Y{4A6E|5wT{rUzKV7DmG)V?`4jsg3PC2K?%?ISjXK z>tg_=W4o(tnwK3=J(;#5@uhoFa7?*q8KY(}@E+7rGkSm~yR0`a0WCrax(T|i;WPa@w6Q}xH)h#&TH^NAzl=g>PqB_o!JRc{ zH0$Z=r00Woef{>%`ZDGFxD(%a}6_7y=wsGv44;BAql}| zQBD?wWl@#pKImAFwv+p|L44A84?NE;?|o`M*yyG-w*xOu!=U&>$qGmbo|OV`NUyGZ z{ex>+MBUB_FYqvGJZW|mkc~WUG_H zeIth)STc%!k!{LAsv?CCQUlH@{P*==uj|Ne9JrEKT69a4{4rz<&Yr zf86PJ=GEL84XVXxQ4$-8c@CXPbQ;I?9NV9@-8c6pjVPiwvBLCdOJWk=I4X`EnGTo7 zl}l8xg^#}F{ub%hD8()PZzcA$-@W8EMxvYNGIg^f3el(d$vbEOK969`+1Q{6L!yEwcB)W+Xu!} z_?(A@wMXgFvEtsEqS4Ji3OtZ>VH1(-^k+XSOWVSB7cmkL0V3drlK}At|64sv%K7HWqGT1kuF)J5-Jz% z4a!Wy+z&l4sXw8a|BO`HyOa&Z8OMvq00>l9P0sNT)~sswlgGi#q>$Z} zpEmysEK{ju)al-Lg)5olWrd znLivrV48=?8OzX{>;HG6qN9Dq0x;75ViQgHHPY+}sE%JpJ_@fds(s~`p+X%>>jZ7q zDeZS%u1ik02_5=YER<0-<~K~-Cf0(krOu=O?}Md!axa>x{2!*`WBXfepz!)wm%W;| z-j!m)xyr8r3ceH4sc~;Oo7kz96*b)-zIz(?++bVvryEQN%vuSAo{hlgKO8nlS-p~~ zo$x1i7>o_AsPERWcZHIp#=iW-Z(`E556Y)@rGEDx2t>;#j;c+_YTD*Jes3t-Gd(3y z(aX3$SiDiF+3v9Q^(`z8A5YerZ)|$pFzhZL`{r1BJsVynx$=-5Q}{Me()L}3@*#)G zhn3cv@^&{yel6YH)4#Lpa?5^Nv-g1IbX*R>@2znlH0%3M`_+c~PNu^G{bm|At`08i zcQqp~h0&g_M|#ZHsZ>3>r!T*Xq|4a;qoC)^aG?9+Z31+<%r$e?CS~NzUOYia%D0n| zUgxT6@>e2M%Y_~SqM5!&MF=lM%Aw+yIY0uj=*qypg^66vnlfMQ-JgiWg}E4kB`^n; z7sj3=gVs#Za})4rMOp%uCx`xM{tA$^(L3>)U7 zEPVb_wPZnPiDQMsw0}o<+~sVU3f9PMt=IECr34)p425`qN8LEX?`^#Iqt1|N?z3-@ zYUsF-&--EZgv2zIiXO<44w?(#@z!V-N}YN%917d)o+Xqm-80`smblqN3`8xI{n-P0 zDcRm1nxu}a7M+=oOFn`s^;~zmzGJlEWrO&tjR8O>O+^7pGK(Ou}$qcy8cQ1D>6Qs~vbjQN06T>Y!uq6;HTXXqz-Oh?JgyK)!K^R?De z3#*$hSs&pSh7F{c+F2k<8u7)xX|a_+rm2j35b*2HKBhbPzLEV%opWW6o6*d2JS?1b zrh=M4X2s2$V=mmpiX~Wl4sRH84qWc}WHxsp)e|d2_ba8>&IxsiDC(I~SY(E6LzB0H zjrpCQsj9o>UGke+2o+i$e8noeK;xCRTkv|o8P)_XMvopEO*xoPz!$7n)PE725vLyS zxK|UV_l`!!zM>;2{WQnH5bsJ?N>x3Yj%!FE;rEj_I8NBu^>{z#ZxKr|S6<-*!$f<(GI4tK2FU=6Gz&b~yOcbkhK73VVYv z8(;O!j2!`ufEfXOR8=5YBrovX(~NgUWnp8jeBqqAa@Aj|Ndfe$bwZjsEDb8M(*j^Q z95Mxw{_H41`~2#-ZCY__M8M`C<0{jgK0Jrwqnpo-+?rye9989}=Db%GASJD-*nXG& zy?~@!6EQl;RokHh+blv*u{2V40652foAWU@R(r{~WS#g59$JpZ?3xfj&PbN>vHxLb zG#}$Kt2^?b%@4XMsZW(DC^u1+77YC5P?>TytBZ_J=ZS!u5+0_;V0b=k0_f6nJ0d~T zMe|g7yd*D?7fsp(c3$|y;cXI7U6=UKX4;LQb-g~{_(o=PXY#%02xbJHZCM=;N}#7)>$2r>J%qS??yp1*W_iVi_F82 zDmE|Ft`t8>%SOYvNT~MJn`)an5b2hD$EVU?L|8&=Mv+bMb&Z(!eKIVjTH4VNSEn*~_%?E9k`dl#h+-&9TE5b#iIThwec>z!X4j z0UXxAkWJIOUEv)@s}v;9j9K8ko!n&eTlg+MfqGr2u%Ed#x$GjPu=svNCzUS2xCsT; z$`nMG)4^nJ7#^Q{x;K!d5H#w(Lu3r@FSQzg;{hB7I&iUcq79`tRl^|AGht%|4N;5) zJb==!FOMicTD-2W?cWNLiFa7(+?85FNr2f)67fox;Hq>J%09Ng{QD2yYOm9im#A;j zgOe%Xi*NuN5pWp}hXRv;qB748X(OKIr_Qk)&uzfxPvxFlnwcs2ur;N%>wB|&@WFKw z+UI2PK^N#J>*^NxLUHkNv zYkob*_5x-=&@ucjB1tZNa ze{c2EubyGbL0d{n0o2d4zhl_pLtJlDJ+^5gq`JDF9hEq+M-zU4S=o&|{*oz==A9Je zHwir*w3+_=0Mq|*zFOG#k$ypKz`kC&$;=l5Q;Pb6*m;CIqbEwsk3p*ShDJ*VCNf{M zL1TrM@bak|mL~nM{~i@xnER|gDr_*MMz5ppzPt+M(Hy*EzD)g4Qi#t=B=v>a;W#e% z-O?Y9)B6gl^IU{zPzUOK%vosBRl$Suqb&d41~K>Lk2ks4llsa!0^-5qdWIz?p>s%% zm9WXt%7`Jiq<=Dd`GjS~16T9+YUQ$T?Z1OW6-M(WKTNqr{Cg;**Xu4ts1uYVWCe@W z$P*N^d~70n^F?d)8m>#}0$i6aMCHLs!-9>Qh)V8$w1x)C*1bdtyKr?%(^^pZYvV@? zue|CfCP3sG9R?;@lAGR7dJR{43)dP=ub$U_XYueu?f6aO+rGgB4x&62OtMWvcx@v^ zL3n44c4(U5&WZF)|6^R`$WiQM+}G#zUK|1h%`M=xf9rD@h5k=;7#__!N)eISZ&^HAU&gwz~J$ zR;Gga#!dueVd40%3z<5*t6=a7(Lj)ro8qUYf42Sbx6;+WFrUmsI4>LoH&GqE_KK}I zxxY3Samv) zFP3Q{JMP-t2zl4q?8mNaC17C~K0XUlz{8NV-atFyCxK?~4PMf%=VxOj1*t~Jb|p!; zesgnvr$u2}&p^5TR4FNIuS&|EZR(v3yI69l+w`)sRU1+LWoBHcc}Y?qS-CEpQ$5Ej z7Ms(?(Mv5^Sl~x!=_(7 zf#TmaPnJcxQnr)eKpxC9=Ec;;r=6Ei?YQ>c14Id91}RPV7Pa-3X1b{c2nGB|nd3o1 zHOw2XM(ONlO6}l-{Yq!G^V`sFC(?scmy`FlPps-p&%Dj2l}D6L8~TZve3+UsY$jzf zT9E7D+_8?IZ%XW2T?muLsn_^_vH^Pki;P*gR1-BU11gguQ(kWLc+%{n#padC2VDNp zM2g9VE5*y6DoQtC9oP=IJL}wP6t(C+5s~Wl@dR)E{x~~fA&|2evOo&}8^%A9>V*nV zqBbxLXPDA`+(6}(A*KbXP>}uus zOuOQyu+*IH3|j}k_0VqMQ!W?X+q002HdDX;7qxk3t!;?l z|8}tV7ROWolAw(;S97CHY51&4n>+L33MSV2jAw?QfmYo_x1T z#2sSsNo8`cHjtY2M+Iu7RJ~bB;(FL&olzR*w&-f^&R7<;e3%O zS_)+*m&C{W12gVWXhWVB{t{f6(ue^%ETy(q*6B0#=iU1y>73zcSX8?yc{-91ibca$ zeNvbl`K{Cff(|xAdgcseCch!9@%-nkhs`Tf335DMJY3+bfdO!XDJ!2F*dzAU~q&#riHM7i;KXzBA;Z>e~a zV{fQy_8~C~_Fikxdn@GG)N;sC&9|V2`))8Pm7i?uZc`HjwZZ)l5VS z8&()!$ut7zh}^mbs;*3XGQ(&mqDj{kpT9iGrK_VgtFYt-!^s;!4g486&)qfDVU4qFxUu&t(Hxp8uLAw>59ag*x#Fgb<;(kfjSsdneo)?aSVK9wEH+d^9>44J__)I0W3~;BDv~D&HNlR@KF;^#TTCGxsADzNM5*;O0J#fAy0NMDygJurWLv1W*#fsSn1JwE z@T6d9%){j*xP8fM#apvpjsHZ?v<8A*?b?ACNHso(*+rka#(&~k<$8Xkp4&qQh!a^oit1(zy{beHOt5X|N%_Y{@+N+l?*K4BN+89li8tUubZu zC_7*AOkH?$|DJ2s#zD|ii93w~i-GD|-EtK-!t@ZoBV*E7y*T~=1f+t;kaUn3l7!yB zzTuwEu(X~46mk7LMzHD)L~&pRS0W2BNjT3x9LUJD&Z=6sXoX(X)KzWy$-iLz0?fhm z=PdM%VTZf4TYL3iYUj4;vTURc5U1CyE#IZ@F5gOUZwRYq9nNx!HTmufpeo^l^n$rw zuD^!@Lbl|O}l?7BD*9c(gi2l zI2kj=9sA-Q&}xZlt?d(Z2`kR}j-rfAO4DY!G9?t}&1(Yc>)U=~9vJ-n&w((o(1_9w zPA=l15SemHB5Q)8;1spidy|j@%RBEOZmX%$Jdu0507zVTWZ@;sAC4AFT_}(bw%Z{o zGGZX?W7BABGcYZJQ9tz$b6D{&0NRx)SWic?lRD1C#1>Gok0>kkLTBOgu^vCar5H`g zUy-)m1l=@olNM$&8Z;|QpJ3dBYYny_H1U9QD=DQ;7Es)y#+t?{35HfbH|qzmBq(30 z%K||XdI9c4@G+06tl8KCsMOHR#4n5~klEBNJj3i|$-}9lUU-kw7rysEFWh9pywIiu z2_3|jR_zRlan4UcY>YE;b@AUXi-&f4u8B5LWWGS=?3n_k^kfT+LUza4GyAtTT-zv@ zC}RH-Kfyz*48%**y>`9XyPpk>5f3Q22_Kq>+i;~4_FU&c9`xQltlP5Q@yKDUFl#!D z7%5RzP}LAuGUMG?3r;y{3?gB)0+!=-aDJ!r!Heeq;1U_57%< zBzDMje#kY!s5Qf_vB^8^()}Go)!Ofk2V5d4uas57*{LSgk#9zpam#uj+KZ$57JX6x zV3n~I^fkBmDgbX93O3iwwLX#S{AB*Mdkf_LNd6LX+ouao!8ejLy|52Bgg+uoh;LLUVd z=T%lopQ{V&Ut5-Me%cWu&M0B&G38u%zVkBG!RL~jkvS7~vErD>9l)h^;6=3nVWw7a>>xSr;>dEGm)ZCKC}An?FT$Ryg6?a-!&$KMg5%s zbZxsh%^qm>%4`aKG0^+HLFw8O4k>HU6GYq+)O_i^7KtD2x5u5+jlceu;^-j zvfd(YE?)yHO2_c@r@+I5h4V+O2Y94vL%QCE|gX)OGb8I~?LF2d1W) zro{kr8MXGPlOdqwqSFKZ>W*nDc;EjhboRu1@5)vGF z9HPX-QpiYArYpUk_Z>Oy?!@mrw&J*YsuHDob@D4R%|Mz$OxSz9H+z=;Yz$f+Mo=h! z+$~+mElORCJ?!!@vYMp*Mrq4s72CI)h!y)ZZ^vRqyF$^teZ%<8NY_zyKmBj{M|}qO zzHfeCb@pmQPRw_(WT~FWd3KevJ-!U4_;UjUEm8GrI-kX=d9d(iIiKO%Uh|!zvP91t zevw-rrVpg$vkMIZmtj%gA_YqVcxum}7Y3$;geLaCNgq4%>BrBCO}T&}2FK#s8by6? zIc|?$8&x#@lHE?x<|z6LlU<&UPX6g-33B$06h@GD(&!lKss6iTYK0FN7J|*rc)jy8 z3b}=}2u-(}u;t_pEFV*^p8iE|lJI=1Zu^2_Jj-MM6M4pMR`-Evm;3^*Ht=;N7A0N( zYaeo3E_J;QDegVFg#P3JL$v9& z>6s~08H$;zu+Zf^hzdmQM@N5;Wctlu@OgyalplXbLy<6j_5IWeJG@np zrgX!r+n&CSp1b#rYX}>WMKh%|2A8$j(*1-AKzo<+J4I}leMadC4D%StnwaMo_5 zO=C^IyGeB?UZ-=Cmpj0d;LdOt32IlNn&>aNt7%nNrX&ZC`fsl~qmX*aWFDov1$Y-J zKcI0_eZo)EDvHA#AUOGVPc*q+~UzUMC$@5rO&|y8bz^-;53= z;WpA3k7H(I^hdEby31$mC+gel(mv{%EVuXmv!?Y6*pB;a?&IU2Up)7%p zaz&KlB+8Wjm1g?oC<87LN-995@Y6F{Pyxp%UD(fFpDlww9F|Y@*-7&&41T!jKu9aU z-Di4n*{{`b8<~0g=Ej$%%CC0ZJo>^PwcHJMQh=2ddr90^Ftg6sJOFamDV&v%x1xYM zoq>;PGmV^$$5^v|9d|X%{;?xBV^W$A@ck!OYz$)xu<~@`H`Xau z*C}ACRElBh$YaM^fuiCQ$%txR^`9Q^Yq2cfI{A_;= zZ$7@VB(?oLI?at~O0nITU}u$qVyj`hrXNkAi{m|msO!DRJf|q)yG%F9i)FRWyiOhl zZXMW*tX>0kYCi`YoJtEZP@$_bjNmtjZfuRWl99Th zcJ2CxGIkMM;`Ela@NXGltN~|7HZE;ae zk@;5+yX<#QU(9ewI{ueqmz#~a^5u)|%^(SggKQEOpkCjZkTcMkC;cjjSJ(EPk$;*8 z1#k<-*{(B|GS5K!i2bM<8_85o!0>_;DgpT@1^|JBsgbfQ-#hNPbQDTaDQ=^sL_ zRa3LPzYAC;VE zUsJs0S}$E-w`;>tMn#ikC^SBP-LlTxBPo7`7CXY{0!`ul{b07nf#MgRL3ZPN?%tS5 z7WSJ^SoZ%*OS^5;$pu&HeEXedg8jREB4`wuTaWcu?F@gYwI+fEP$df_s@@^=+iV~O z8WC$IbmB=wa0oh@%xK8eDDul$mz|cVcCc~!kvDu`FNF7uXLKI7JPR5ujM+BZOA|6{ zpwN(I-aoHR2Re)*sP4K9(T#fN{?~l{Oozt5*%CEp7nt@? zSMf;l?T8cMsU3t1QqbT5+s1_QZVPdBsmKJeGP_xbhT@Dv-5|4!QQVenM0ig z!K!_b6S=pFzU>{!_ja1+hwG>6ES6f2Zd|U3r+F{#jJiIoi)1$jJAP60^%J~K`Q@75 zvH~~ic3W2xbXm=%+8bp1R{M+{h_vnyxj199_Vy~r5p^ZhH#6c$+nfGq=4wIJuk>sh z|ETz^FSY;D4`pg!NwHlgBfDu*A{U?@BFWv9zGgct)41#TOhwip#jexG!Y`Fw4>tKd zH=JMVY}_O3bUg$w^cT?cfMOUkR82>8zP#_}(CMP=mUTHhU5hZ*;-a-wmSOWMO413D z91}|qra9BoNb`$KKB`N3$uFN+cfUJpx2*a6m5b#6p%WRJtO-c~Jt>-e18bmIQsJ2_FlmiEyZS&Dy~XhD9T=4686zb z9w{Fl>WwBK>i#YZ?%V{dR<}tU0_i?396aCIXO38O~TE3xY#~q+Q+<}PtBcK zE3P4&zy9qh;}BgiEwQ&|Y?)h34RMQ`SGI*I>1tj89S8ipz}S8IRn4koay+TxXBN8wUy}8 zT56`|=~kE(MsDX$R5(Y7!$$1$Au>Xb=d%VzG$mhsqSZ-RJ8)h>RvmW#=MK+|T3qa@+FAEDt6 z@PG7=I*?KzMc6SbDNH?#zCp93+4(AHb(%*-pl;_5=BoG}Y{#+*zS#}#2#<0YgBDB= zjwoHDtuNO6C^Hq!8ZN$WYU5ocS!u0$J&?i5>$SFo4Dth~VmcxuL{peF0 zg4I$|R8^lT@<&8rF8_{+wi)NZujrBL7JVBh*tNN zZb(^Deh&@P(4b9bCe?lYc%Vl6EtU=2>y0$Q173iump41-==1tC{Z~DCtQz}lp zO;)MrP-a^sy@u4Mb@J*hRc>hn3_41hO%n4X=89%c#&zl_mQ}*x*e{KPk{ZJs`)3-< zmvNB+Y4yPPnd`WOcH7)xpCst5cs`!-sqdV(SgHiWEoefzi;UKqPv%;romtL@!4a4X zz6*7(9zeIVnLSxd*-qGSoeGuKUuGS~ufl1+(P=1)|gBQ6rXB#ssHt(+PJ_Bjd(1--^ zNnbsIDnz>~wXyT*EaV`if!%K6)}Rf#zpyYzEHG9JKAQwGR~_k6zwR8O=S54QE^h+Z zbjl_Mk30wK(iLbrR%hOA*9N(w5Q~D_*4g=h>$o;Z#gG^k=P-bXPnJVLK?-+?9RBGq z<9XyV*fq8+wmayzk?xI9y>t4ek1EN*2FH&YH)2%jq^h@1$0b1(= zG3v6L$L4=4s830#to5yLPPUz{fy}~8j>z*&=)8w@I2lzC(7$;Vog z2%WVa*>tpk7W0PYZ%GZgbO7JA;8#agEMZ6yA5}1$gy4aB{a{VrIkA!Wu zEemNig7Dy*_}V!G99W=&JNM1SqEA6&2lwn1v(b%8QZU)9m(Z5MIz%c;XfeyOk>Ztg zGuOha%;;p_m6ugdyZiV!R~?pLfAq>rF=dn&xd*(6Oo*#w_fGP~#>iy3P!pa3epIJ} zbacNn+hn4St0wo?Q68YFN*&>VOZC0FR`wTHExF~nU;H{Im8tqB7z|8VEOwkwdLg^3 z`#Gx>S}4>B?M@VlxoG&Ix72SULZWvMq1K=NhB{ks9v)34;#0Vo*U_4$2^~TXkZ40o z2>j&(Ev}az@^^#a_bKDwEz2pm?+4mne4H8~WpHb10G=C|ICGO;AN(Fxe6Qf=s9G=J zG2@}a2*oh;a15M?^O^#u&FI$Clc@59$TVWInq6^DJ5)R2P`!y$?4Om0CG2~V!n0>9 zKd-uI*6wRVLGRu%%weFpv5Hf%z*Lrq(R`tPiDLw&WuDd@p_*H>U+CWD5qH)EiZ_Jj%zO)=YyxiXl)B z1+clkxef}g>DO`r?cx~DqOL-QJCiqn7nTG!sigOAdsc^}?+8Abu?1fBTQfBn9ei!I zSk^CqMygJ%d-^h!KVwJA?DWFtG{ALsiF?~Jpc@`3BKutG-A+s;x(Q#WnEF^~!$5Ed zaIhYdWM0y*mz7TssC1LFc?GE`YPoJa==FYTT~fXA=)I6RyPo)F?qqtdr|1qqODw!m zB5b!aa;BQmSf1#{wkIIwFUz8d1O*a`bZ z_da&IRtq~h6lk=#Nt~!oxShWln&yZn4j7i%nfgKCl^@LTB9;i=ec3jII9!9CI)9&n z!dG)o1VH0k{MwX9M|k&%32FCc-YtxMERkA9PJzh>?MDIgLJ3&WRh`bII5M|mM>4~X zO}_k&S+S~N4d2E2$12uO@MHB<+Frb(l$4Nd+s5iEd9iOD3?Z0U(34Z)v1?Bp`> zLj}%!Viz2g%OBHg|Fuu+?KZ)5V-i~3*>b*TG{oNZ<|oGO*=&h^0g{0%J&)>w#~WOw z8lvdr&zjck&>-F4TZuaP_sDQDB9f~-p&E$JibQ82vg0-G9M#5%hQu|7yb9(Sp zQ0o2D7Il$oA5D>407+Byb*j-GCsS6%o{Bd*R-< zdD#I}-oh%--_ji)4upIx=DB?D;PW9d z#gU#+Vs6Lp0GqtGuO$26YZ@k8(Ayc8AkYq*S=;RpJ)Cxk-<6uCeH{HPR8&79nyxn3 z-yPXAtkF1WkU#lMS%^nYt|B$L7q=-D+z0KCB#Yhy?IZ=DO!Gp%xRV)jXlYv=$RTIV=~EHr(95mFbV z4qWXC?*yH?=_(I`RX2* z-37QHfQL@V&2lzm=J)=?AtYop6k3i(U$c_Vp=gYH$>v-RRX;S+)_J|g3*Oz{xi^OR z=29~IEOM#bjooj1esbE^G3k+$VjH6$BTgJ^wnjG;&MP1+s$aM@f)$NknEVu;mj$GS zbziWzN@yL~39LrmAfqUM^To|_Z|!$WSb{lM$z*b;N4_^{ypPTx_-kVX8|cv_V4|dC z!$^y3S$DMln;4@OWVFggS_Jn_(|5`Jy$MhG3*G*n!9zpus~1_9>27nd7Osjlz<5dN zvt3^Wg^{XD?$r1vK(j5}T;9w{O4t`NKv1!o^wk_A>)3d&WyR3fl5p9pB>Gz9D5B<* z*OfuCMDPDF7>ZXHbLSkx_ERKYApc=q za`*+)>66g8n>OKIpDORZoEgPZ|ILbasD$j4>+&1RXNw~{007t&>wJ{_bE@ZCq;#J| zakEFiL01!QV%Y36y>b5Q$CzvE@s3~a`{zBfvjv)E6AL!{`_m(K;y?E^W+fR^qjIubf`bh`%AijP1bs>i|Yp%U%RBn zpkF9RVCC;wCqt-guJpuDX4ydUM!V|@;nHOCL{S(2bX+wR_YeFG$(UYN4oj*-6LZ3a zc%Z86TCryNf!MF&(efM9LY8x`H2Xn$%AA^s$H^pLQ4GA#f=FK`$xZ5*y+d}w@TF0p+{ zW94nNc#&X>@7fz+&C|h#H)!To&u5iapG<4nIOxF<&YF`oQ$({46@!o}n)Nkf}gfMvk#fNQ3{o(58`dX6`RfNfqhKcXnwysJ_~V z0BqyO_i&u+k(lHb`AU0w`tR*4j9}T^z+J^(dz}%>ozVsayr!*9^T{`188dfJ_B?%S z9k_=n?1nzdk?Y;_sy3wi?(^{&a?tf5R~@@5b6!09;0j}&gdtKxy17y8R0!?okYU9W z86dvb)#Uyp#P7)`#7Wc@)`;XSss@^>A=YbXn`!FnWCD@p=*U~SCBL%$Y(||5N-{YA z15vfE;{4!qgt^+!l4owEfndXes4t?>@06P9z)XGlISjs^6Vaa1(rj?4T4mbltA)H{ zxu^DeQ8A@Th%iX_Ku;DkKRqgy&Np5D_f$+?pZr2~{imo*Y=iFNjc6Z&CbVqQi3u7O z^{%Zqf7)^Ga8C3$X5M)M;>t$`sfJ%0MR+XPWY613Tp`=$L zvUnpPZW6tn$^?cL$%l+++Ql}n33x6>u;rt^&dH-t!-ez!b+5oJ`t*c#HpY+Ht-2%PKt3$NmwWMRpceFbm`Fu!s`!*}U7An0q#X8@~-AZ+(d{I6`?nRO$M`{4|a4? zjXGF2sE?Wx5*MKpXnO3HSXkR;KD!&)KyqLo@sDl(Oi2k|VQ5BU;-_mopM?BP9d>(p zYI9LXiK1H%!rKDNpTw0QWo|KO+`DM3tu4L_R);InW9RK+u0ap}rsu4>=YteI?G1Jg zqvdhOgm*@?E8r&YpawF;5E#iq^C2vIBfREQs^H~?x%B!WST~6aZx454C(JLxj7KpT zEc8eE#{J;YTwTzj%b3}ww(z6TwWw`;FTdeX=q9vN)aHd7^rItK7`=twC^o*k9Q#6G z7m!-7Z_+V)Q5_8iWAoQYdZvQJ{5vM+lrsicS6frTtqG<@&e4(y!?YKnj5HyJgYeQ$ z;eD^4*75|#WgFSeX-WlYHi;#k26m#Py*aQc;A!n@uaVWyyQjYFJ_0#{tb`lkJdqmg z8iEvix;2H5#g9n{@BOfT(J{Kn!g%zr>|)NX%-ifp07bK<+B8zl+Z^!l+f7Jo&!Gi{ zYonD>N&QYQxYTNW)QXKEN_El3eDuJ}|1aJHA^iA*;U5lOd^FSTXVRnF6v(mdC0Ry& zK?n7!7P_vZqj)Ob^G>^DlE(F;wRZXTC{cm)XFrZ><8sE`3I70$^;g7>%Fp6Ijy10U z+v^&ai7q6$TaBwS;Z8Z@>_pM!Ki9(aE8;@?-ek)xXX?F`Jz>4qc^z*naJ%zC!6z7^8%2D_+vml`hlo9)tk z&GP>Mwa4LKWr&=qWpR`ppZqcNFq8I->O5!Rj}Lf1;zgdj;k_OUTg&Y<8_R`{b4{MR zw`V!81O2h|%MT3r(@oWUJ8HH!7e#*2C9m%H*=|>y@moXq5nhLj zIX`H8WBXxQ+jx`y3N=1~t;hC@Ket<_+1QYwb}t-ay{lf*>?HV^Vd2Sb{L3pRPqM)y z77=mSo=4|j&^bI~^%_6nUy{r89U zd4I9)9({8T&h99lfjT zFyF&Gs@+bS${69%*u*52b=)%0niR<#@P0sEr7$^QTl^uECd!#U-%)8UTYwyavt$kdF0 zq<}^ZcOMP3y*~3(@z;j#tmAtf%`e%C{Hps;%smg&71>YVIsVbXX}5Px;f+1o;n=Oj zk>!}5mpwVIAL2A=bF5jxINXJsE1zobrTjCNukjJicoqn)Ja1zSy6sn$!!A!hHC_1T zOr9>Y-gAq@13iX%SBYQvhowW|D|o8f3l9|BO=lR5zjk*oU%D#3sS+*iq|3N8@~X^3 zADns`ss8{B&OgTaotKFKTW>HAnPkGBPn1_XdQ3VO+amd8mTj^~I Date: Wed, 4 Sep 2019 20:45:35 +0200 Subject: [PATCH 128/154] Add support for short hex color codes like #CCC (#2658) This adds a few lines to support shorthand color hex codes like #ABC. They are treated as equivalent of #AABBCC. Fixes #2639. --- src/cascadia/ut_app/JsonTests.cpp | 2 +- src/types/utils.cpp | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/cascadia/ut_app/JsonTests.cpp b/src/cascadia/ut_app/JsonTests.cpp index d8962c4acda..1e35fae16d4 100644 --- a/src/cascadia/ut_app/JsonTests.cpp +++ b/src/cascadia/ut_app/JsonTests.cpp @@ -79,7 +79,7 @@ namespace TerminalAppUnitTests "\"name\" : \"Campbell\"," "\"purple\" : \"#881798\"," "\"red\" : \"#C50F1F\"," - "\"white\" : \"#CCCCCC\"," + "\"white\" : \"#CCC\"," "\"yellow\" : \"#C19C00\"" "}" }; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 8554c00a19b..7b3f6cfd075 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -83,7 +83,7 @@ std::string Utils::ColorToHexString(const COLORREF color) } // Function Description: -// - Parses a color from a string. The string should be in the format "#RRGGBB" +// - Parses a color from a string. The string should be in the format "#RRGGBB" or "#RGB" // Arguments: // - str: a string representation of the COLORREF to parse // Return Value: @@ -91,12 +91,25 @@ std::string Utils::ColorToHexString(const COLORREF color) // the correct format, throws E_INVALIDARG COLORREF Utils::ColorFromHexString(const std::string str) { - THROW_HR_IF(E_INVALIDARG, str.size() < 7 || str.size() >= 8); + THROW_HR_IF(E_INVALIDARG, str.size() != 7 && str.size() != 4); THROW_HR_IF(E_INVALIDARG, str[0] != '#'); - std::string rStr{ &str[1], 2 }; - std::string gStr{ &str[3], 2 }; - std::string bStr{ &str[5], 2 }; + std::string rStr; + std::string gStr; + std::string bStr; + + if (str.size() == 4) + { + rStr = std::string(2, str[1]); + gStr = std::string(2, str[2]); + bStr = std::string(2, str[3]); + } + else + { + rStr = std::string(&str[1], 2); + gStr = std::string(&str[3], 2); + bStr = std::string(&str[5], 2); + } BYTE r = static_cast(std::stoul(rStr, nullptr, 16)); BYTE g = static_cast(std::stoul(gStr, nullptr, 16)); From e0762f6bb3d592250e6c01618feb323b0095e49a Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Wed, 4 Sep 2019 12:03:44 -0700 Subject: [PATCH 129/154] Open-source the PseudoConsole family of functions in a new DLL (#2611) This pull request introduces a copy of the code from kernel32.dll that implements CreatePseudoConsole, ClosePseudoConsole and ResizePseudoConsole. Apart from some light modifications to fit into the infrastructure in this project and support launching OpenConsole.exe, it is intended to be 1:1 with the code that ships in Windows. Any guideline violations in this code are likely intentional. Since this was built into kernel32, it uses the STL only _very sparingly._ Consumers of this library must make sure that conpty.lib lives earlier in the link line than onecoreuap_apiset, onecoreuap, onecore_apiset, onecore or kernel32. Refs #1130. --- OpenConsole.sln | 18 + consolegit2gitfilters.json | 1 + src/host/ft_host/Host.FeatureTests.vcxproj | 11 +- src/winconpty/device.h | 27 ++ src/winconpty/precomp.cpp | 4 + src/winconpty/precomp.h | 49 +++ src/winconpty/winconpty.cpp | 384 +++++++++++++++++++++ src/winconpty/winconpty.def | 4 + src/winconpty/winconpty.h | 43 +++ src/winconpty/winconpty.vcxproj | 37 ++ 10 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 src/winconpty/device.h create mode 100644 src/winconpty/precomp.cpp create mode 100644 src/winconpty/precomp.h create mode 100644 src/winconpty/winconpty.cpp create mode 100644 src/winconpty/winconpty.def create mode 100644 src/winconpty/winconpty.h create mode 100644 src/winconpty/winconpty.vcxproj diff --git a/OpenConsole.sln b/OpenConsole.sln index 35fa999fd19..2824b840799 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -248,6 +248,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LocalTests_TerminalApp", "s {CA5CAD1A-9A12-429C-B551-8562EC954746} = {CA5CAD1A-9A12-429C-B551-8562EC954746} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty", "src\winconpty\winconpty.vcxproj", "{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|ARM64 = AuditMode|ARM64 @@ -1029,6 +1031,21 @@ Global {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x64.Build.0 = Release|x64 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.ActiveCfg = Release|Win32 {CA5CAD1A-B11C-4DDB-A4FE-C3AFAE9B5506}.Release|x86.Build.0 = Release|Win32 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|ARM64.ActiveCfg = Release|ARM64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x64.ActiveCfg = Release|x64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.AuditMode|x86.ActiveCfg = Release|Win32 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|ARM64.Build.0 = Debug|ARM64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.ActiveCfg = Debug|x64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x64.Build.0 = Debug|x64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.ActiveCfg = Debug|Win32 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Debug|x86.Build.0 = Debug|Win32 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.ActiveCfg = Release|ARM64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|ARM64.Build.0 = Release|ARM64 + {58A03BB2-DF5A-4B66-91A0-7EF3BA01269A}.Release|x64.ActiveCfg = Release|x64 + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1090,6 +1107,7 @@ Global {CA5CAD1A-9333-4D05-B12A-1905CBF112F9} = {59840756-302F-44DF-AA47-441A9D673202} {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} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/consolegit2gitfilters.json b/consolegit2gitfilters.json index 6466ad537b8..d779dec7ca3 100644 --- a/consolegit2gitfilters.json +++ b/consolegit2gitfilters.json @@ -14,6 +14,7 @@ "/.vs/", "/build/", "/src/cascadia/", + "/src/winconpty/", "/.nuget/", "/.github/", "/samples/" diff --git a/src/host/ft_host/Host.FeatureTests.vcxproj b/src/host/ft_host/Host.FeatureTests.vcxproj index 7a8205072a8..ffebea0acd1 100644 --- a/src/host/ft_host/Host.FeatureTests.vcxproj +++ b/src/host/ft_host/Host.FeatureTests.vcxproj @@ -36,6 +36,9 @@ {18d09a24-8240-42d6-8cb6-236eee820263} + + {58a03bb2-df5a-4b66-91a0-7ef3ba01269a} + @@ -56,4 +59,10 @@ - \ No newline at end of file + + + + $(OutDir)\conpty.lib;%(AdditionalDependencies) + + + diff --git a/src/winconpty/device.h b/src/winconpty/device.h new file mode 100644 index 00000000000..61387ab7b3f --- /dev/null +++ b/src/winconpty/device.h @@ -0,0 +1,27 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- device.h + +Abstract: +- This header exists to reduce the differences in winconpty + from the in-box windows source. +- Relies on components from Server to reach into ntdll for NtOpenFile + to get at the NT namespace, which is required to open the console device. +--*/ + +#pragma once + +#include "../server/DeviceHandle.h" + +[[nodiscard]] static inline NTSTATUS CreateClientHandle(PHANDLE Handle, HANDLE ServerHandle, PCWSTR Name, BOOLEAN Inheritable) +{ + return DeviceHandle::CreateClientHandle(Handle, ServerHandle, Name, Inheritable); +} + +[[nodiscard]] static inline NTSTATUS CreateServerHandle(PHANDLE Handle, BOOLEAN Inheritable) +{ + return DeviceHandle::CreateServerHandle(Handle, Inheritable); +} diff --git a/src/winconpty/precomp.cpp b/src/winconpty/precomp.cpp new file mode 100644 index 00000000000..c51e9b31b2f --- /dev/null +++ b/src/winconpty/precomp.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" diff --git a/src/winconpty/precomp.h b/src/winconpty/precomp.h new file mode 100644 index 00000000000..6bf892eacb4 --- /dev/null +++ b/src/winconpty/precomp.h @@ -0,0 +1,49 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- precomp.h + +Abstract: +- Contains external headers to include in the precompile phase of console build process. +- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). +--*/ + +#pragma once + +// Ignore checked iterators warning from VC compiler. +#define _SCL_SECURE_NO_WARNINGS + +// Block minwindef.h min/max macros to prevent conflict +#define NOMINMAX + +// Define and then undefine WIN32_NO_STATUS because windows.h has no guard to prevent it from double defing certain statuses +// when included with ntstatus.h +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS + +// From ntdef.h, but that can't be included or it'll fight over PROBE_ALIGNMENT and other such arch specific defs +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +/*lint -save -e624 */ // Don't complain about different typedefs. +typedef NTSTATUS* PNTSTATUS; +/*lint -restore */ // Resume checking for different typedefs. +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) + +// End From ntdef.h + +#define INLINE_NTSTATUS_FROM_WIN32 1 // Must use inline NTSTATUS or it will call the wrapped function twice. +#pragma warning(push) +#pragma warning(disable : 4430) // Must disable 4430 "default int" warning for C++ because ntstatus.h is inflexible SDK definition. +#include +#pragma warning(pop) + +#include + +#include "../host/conddkrefs.h" + +// This includes support libraries from the CRT, STL, WIL, and GSL +#include "LibraryIncludes.h" + +#include diff --git a/src/winconpty/winconpty.cpp b/src/winconpty/winconpty.cpp new file mode 100644 index 00000000000..14a1730da92 --- /dev/null +++ b/src/winconpty/winconpty.cpp @@ -0,0 +1,384 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include "winconpty.h" + +#ifdef __INSIDE_WINDOWS +#include +#include +// You need kernelbasestaging.h to be able to use wil in libraries consumed by kernelbase.dll +#include +#define RESOURCE_SUPPRESS_STL +#define WIL_SUPPORT_BITOPERATION_PASCAL_NAMES +#include +#else +#include "device.h" +#include +#endif // __INSIDE_WINDOWS + +#pragma warning(push) +#pragma warning(disable : 4273) // inconsistent dll linkage (we are exporting things kernel32 also exports) + +// Function Description: +// - Returns the path to either conhost.exe or the side-by-side OpenConsole, depending on whether this +// module is building with Windows. +// Return Value: +// - A pointer to permanent storage containing the path to the console host. +static wchar_t* _ConsoleHostPath() +{ + // Use the magic of magic statics to only calculate this once. + static wil::unique_process_heap_string consoleHostPath = []() { +#ifdef __INSIDE_WINDOWS + wil::unique_process_heap_string systemDirectory; + wil::GetSystemDirectoryW(systemDirectory); + return wil::str_concat_failfast(L"\\\\?\\", systemDirectory, L"\\conhost.exe"); +#else + // Use the STL only if we're not building in Windows. + std::filesystem::path modulePath{ wil::GetModuleFileNameW(wil::GetModuleInstanceHandle()) }; + modulePath.replace_filename(L"OpenConsole.exe"); + auto modulePathAsString{ modulePath.wstring() }; + return wil::make_process_heap_string_nothrow(modulePathAsString.data(), modulePathAsString.size()); +#endif // __INSIDE_WINDOWS + }(); + return consoleHostPath.get(); +} + +static bool _HandleIsValid(HANDLE h) noexcept +{ + return (h != INVALID_HANDLE_VALUE) && (h != nullptr); +} + +HRESULT _CreatePseudoConsole(const HANDLE hToken, + const COORD size, + const HANDLE hInput, + const HANDLE hOutput, + const DWORD dwFlags, + _Inout_ PseudoConsole* pPty) +{ + if (pPty == NULL) + { + return E_INVALIDARG; + } + if (size.X == 0 || size.Y == 0) + { + return E_INVALIDARG; + } + + wil::unique_handle serverHandle; + RETURN_IF_NTSTATUS_FAILED(CreateServerHandle(serverHandle.addressof(), TRUE)); + + wil::unique_handle signalPipeConhostSide; + wil::unique_handle signalPipeOurSide; + + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + // Mark inheritable for signal handle when creating. It'll have the same value on the other side. + sa.bInheritHandle = FALSE; + sa.lpSecurityDescriptor = NULL; + + RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(signalPipeConhostSide.addressof(), signalPipeOurSide.addressof(), &sa, 0)); + RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)); + + const wchar_t* pwszFormat = L"%s --headless %s--width %hu --height %hu --signal 0x%x --server 0x%x"; + // This is plenty of space to hold the formatted string + wchar_t cmd[MAX_PATH]; + const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR; + swprintf_s(cmd, + MAX_PATH, + pwszFormat, + _ConsoleHostPath(), + bInheritCursor ? L"--inheritcursor " : L"", + size.X, + size.Y, + signalPipeConhostSide.get(), + serverHandle.get()); + + STARTUPINFOEXW siEx{ 0 }; + siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW); + siEx.StartupInfo.hStdInput = hInput; + siEx.StartupInfo.hStdOutput = hOutput; + siEx.StartupInfo.hStdError = hOutput; + siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; + + // Only pass the handles we actually want the conhost to know about to it: + const size_t INHERITED_HANDLES_COUNT = 4; + HANDLE inheritedHandles[INHERITED_HANDLES_COUNT]; + inheritedHandles[0] = serverHandle.get(); + inheritedHandles[1] = hInput; + inheritedHandles[2] = hOutput; + inheritedHandles[3] = signalPipeConhostSide.get(); + + // Get the size of the attribute list. We need one attribute, the handle list. + SIZE_T listSize = 0; + InitializeProcThreadAttributeList(NULL, 1, 0, &listSize); + + // I have to use a HeapAlloc here because kernelbase can't link new[] or delete[] + PPROC_THREAD_ATTRIBUTE_LIST attrList = reinterpret_cast(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, listSize)); + RETURN_IF_NULL_ALLOC(attrList); + auto attrListDelete = wil::scope_exit([&] { + HeapFree(GetProcessHeap(), 0, attrList); + }); + + siEx.lpAttributeList = attrList; + RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &listSize)); + // Set cleanup data for ProcThreadAttributeList when successful. + auto cleanupProcThreadAttribute = wil::scope_exit([&] { + DeleteProcThreadAttributeList(siEx.lpAttributeList); + }); + RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(siEx.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + inheritedHandles, + (INHERITED_HANDLES_COUNT * sizeof(HANDLE)), + NULL, + NULL)); + wil::unique_process_information pi; + { // wow64 disabled filesystem redirection scope +#if defined(BUILD_WOW6432) + PVOID RedirectionFlag; + RETURN_IF_NTSTATUS_FAILED(RtlWow64EnableFsRedirectionEx( + WOW64_FILE_SYSTEM_DISABLE_REDIRECT, + &RedirectionFlag)); + auto resetFsRedirection = wil::scope_exit([&] { + RtlWow64EnableFsRedirectionEx(RedirectionFlag, &RedirectionFlag); + }); +#endif + if (hToken == INVALID_HANDLE_VALUE || hToken == NULL) + { + // Call create process + RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(NULL, + cmd, + NULL, + NULL, + TRUE, + EXTENDED_STARTUPINFO_PRESENT, + NULL, + NULL, + &siEx.StartupInfo, + pi.addressof())); + } + else + { + // Call create process + RETURN_IF_WIN32_BOOL_FALSE(CreateProcessAsUserW(hToken, + NULL, + cmd, + NULL, + NULL, + TRUE, + EXTENDED_STARTUPINFO_PRESENT, + NULL, + NULL, + &siEx.StartupInfo, + pi.addressof())); + } + } + + // Move the process handle out of the PROCESS_INFORMATION into out Pseudoconsole + pPty->hConPtyProcess = pi.hProcess; + pi.hProcess = NULL; + + RETURN_IF_NTSTATUS_FAILED(CreateClientHandle(&pPty->hPtyReference, + serverHandle.get(), + L"\\Reference", + FALSE)); + + pPty->hSignal = signalPipeOurSide.release(); + + return S_OK; +} + +// Function Description: +// - Resizes the conpty +// Arguments: +// - hSignal: A signal pipe as returned by CreateConPty. +// - size: The new dimenstions of the conpty, in characters. +// Return Value: +// - S_OK if the call succeeded, else an appropriate HRESULT for failing to +// write the resize message to the pty. +HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size) +{ + if (pPty == NULL) + { + return E_INVALIDARG; + } + + unsigned short signalPacket[3]; + signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW; + signalPacket[1] = size.X; + signalPacket[2] = size.Y; + + BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), NULL, NULL); + return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError()); +} + +// Function Description: +// - This closes each of the members of a PseudoConsole. It does not free the +// data associated with the PseudoConsole. This is helpful for testing, +// where we might stack allocate a PseudoConsole (instead of getting a +// HPCON via the API). +// Arguments: +// - pPty: A pointer to a PseudoConsole struct. +// Return Value: +// - +void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty) +{ + if (pPty != NULL) + { + // See MSFT:19918626 + // First break the signal pipe - this will trigger conhost to tear itself down + if (_HandleIsValid(pPty->hSignal)) + { + CloseHandle(pPty->hSignal); + pPty->hSignal = 0; + } + // Then, wait on the conhost process before killing it. + // We do this to make sure the conhost finishes flushing any output it + // has yet to send before we hard kill it. + if (_HandleIsValid(pPty->hConPtyProcess)) + { + // If the conhost is already dead, then that's fine. Presumably + // it's finished flushing it's output already. + DWORD dwExit = 0; + // If GetExitCodeProcess failed, it's likely conhost is already dead + // If so, skip waiting regardless of whatever error + // GetExitCodeProcess returned. + // We'll just go straight to killing conhost. + if (GetExitCodeProcess(pPty->hConPtyProcess, &dwExit) && dwExit == STILL_ACTIVE) + { + WaitForSingleObject(pPty->hConPtyProcess, INFINITE); + } + + TerminateProcess(pPty->hConPtyProcess, 0); + pPty->hConPtyProcess = 0; + } + // Then take care of the reference handle. + // TODO GH#1810: Closing the reference handle late leaves conhost thinking + // that we have an outstanding connected client. + if (_HandleIsValid(pPty->hPtyReference)) + { + CloseHandle(pPty->hPtyReference); + pPty->hPtyReference = 0; + } + } +} + +// Function Description: +// - This closes each of the members of a PseudoConsole, and HeapFree's the +// memory allocated to it. This should be used to cleanup any +// PseudoConosles that were created with CreatePseudoConsole. +// Arguments: +// - pPty: A pointer to a PseudoConsole struct. +// Return Value: +// - +VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty) +{ + if (pPty != NULL) + { + _ClosePseudoConsoleMembers(pPty); + HeapFree(GetProcessHeap(), 0, pPty); + } +} + +// These functions are defined in the console l1 apiset, which is generated from +// the consoleapi.apx file in minkernel\apiset\libs\Console. + +// Function Description: +// Creates a "Pseudo-console" (conpty) with dimensions (in characters) +// provided by the `size` parameter. The caller should provide two handles: +// - `hInput` is used for writing input to the pty, encoded as UTF-8 and VT sequences. +// - `hOutput` is used for reading the output of the pty, encoded as UTF-8 and VT sequences. +// Once the call completes, `phPty` will receive a token value to identify this +// conpty object. This value should be used in conjunction with the other +// Pseudoconsole API's. +// `dwFlags` is used to specify optional behavior to the created pseudoconsole. +// The flags can be combinations of the following values: +// INHERIT_CURSOR: This will cause the created conpty to attempt to inherit the +// cursor position of the parent terminal application. This can be useful +// for applications like `ssh`, where ssh (currently running in a terminal) +// might want to create a pseudoterminal session for an child application +// and the child inherit the cursor position of ssh. +// The creted conpty will immediately emit a "Device Status Request" VT +// sequence to hOutput, that should be replied to on hInput in the format +// "\x1b[;R", where `` is the row and `` is the column of the +// cursor position. +// This requires a cooperating terminal application - if a caller does not +// reply to this message, the conpty will not process any input until it +// does. Most *nix terminals and the Windows Console (after Windows 10 +// Anniversary Update) will be able to handle such a message. +HRESULT WINAPI CreatePseudoConsole(_In_ COORD size, + _In_ HANDLE hInput, + _In_ HANDLE hOutput, + _In_ DWORD dwFlags, + _Out_ HPCON* phPC) +{ + return CreatePseudoConsoleAsUser(INVALID_HANDLE_VALUE, size, hInput, hOutput, dwFlags, phPC); +} + +HRESULT CreatePseudoConsoleAsUser(_In_ HANDLE hToken, + _In_ COORD size, + _In_ HANDLE hInput, + _In_ HANDLE hOutput, + _In_ DWORD dwFlags, + _Out_ HPCON* phPC) +{ + if (phPC == NULL) + { + return E_INVALIDARG; + } + *phPC = NULL; + if ((!_HandleIsValid(hInput)) && (!_HandleIsValid(hOutput))) + { + return E_INVALIDARG; + } + + PseudoConsole* pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole)); + RETURN_IF_NULL_ALLOC(pPty); + auto cleanupPty = wil::scope_exit([&] { + _ClosePseudoConsole(pPty); + }); + + wil::unique_handle duplicatedInput; + wil::unique_handle duplicatedOutput; + RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hInput, GetCurrentProcess(), duplicatedInput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS)); + RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hOutput, GetCurrentProcess(), duplicatedOutput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS)); + + RETURN_IF_FAILED(_CreatePseudoConsole(hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty)); + + *phPC = (HPCON)pPty; + cleanupPty.release(); + + return S_OK; +} + +// Function Description: +// Resizes the given conpty to the specified size, in characters. +HRESULT WINAPI ResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size) +{ + PseudoConsole* const pPty = (PseudoConsole*)hPC; + HRESULT hr = pPty == NULL ? E_INVALIDARG : S_OK; + if (SUCCEEDED(hr)) + { + hr = _ResizePseudoConsole(pPty, size); + } + return hr; +} + +// Function Description: +// Closes the conpty and all associated state. +// Client applications attached to the conpty will also behave as though the +// console window they were running in was closed. +// This can fail if the conhost hosting the pseudoconsole failed to be +// terminated, or if the pseudoconsole was already terminated. +VOID WINAPI ClosePseudoConsole(_In_ HPCON hPC) +{ + PseudoConsole* const pPty = (PseudoConsole*)hPC; + if (pPty != NULL) + { + _ClosePseudoConsole(pPty); + } +} + +#pragma warning(pop) diff --git a/src/winconpty/winconpty.def b/src/winconpty/winconpty.def new file mode 100644 index 00000000000..51ccf32791b --- /dev/null +++ b/src/winconpty/winconpty.def @@ -0,0 +1,4 @@ +EXPORTS + CreatePseudoConsole + ResizePseudoConsole + ClosePseudoConsole diff --git a/src/winconpty/winconpty.h b/src/winconpty/winconpty.h new file mode 100644 index 00000000000..2cfea5375fd --- /dev/null +++ b/src/winconpty/winconpty.h @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _PseudoConsole +{ + HANDLE hSignal; + HANDLE hPtyReference; + HANDLE hConPtyProcess; +} PseudoConsole; + +// Signals +// These are not defined publicly, but are used for controlling the conpty via +// the signal pipe. +#define PTY_SIGNAL_RESIZE_WINDOW (8u) + +// Implementations of the various PseudoConsole functions. +HRESULT _CreatePseudoConsole(const HANDLE hToken, + const COORD size, + const HANDLE hInput, + const HANDLE hOutput, + const DWORD dwFlags, + _Inout_ PseudoConsole* pPty); + +HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size); +void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty); +VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty); + +HRESULT CreatePseudoConsoleAsUser(_In_ HANDLE hToken, + _In_ COORD size, + _In_ HANDLE hInput, + _In_ HANDLE hOutput, + _In_ DWORD dwFlags, + _Out_ HPCON* phPC); + +#ifdef __cplusplus +} +#endif diff --git a/src/winconpty/winconpty.vcxproj b/src/winconpty/winconpty.vcxproj new file mode 100644 index 00000000000..6ffd09468e0 --- /dev/null +++ b/src/winconpty/winconpty.vcxproj @@ -0,0 +1,37 @@ + + + + + + + Create + + + + + + + + %(AdditionalIncludeDirectories) + + + + {58a03bb2-df5a-4b66-91a0-7ef3ba01269a} + Win32Proj + winconpty + winconpty + conpty + + + + + + + + winconpty.def + + + onecoreuap_apiset.lib;%(AdditionalDependencies) + + + From 7c66e66ca11fbe959c182a45aa7d9ffeedbb2b14 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 4 Sep 2019 12:49:15 -0700 Subject: [PATCH 130/154] Fix redefinition of class name for constexpr method I moved from CPP to HPP. --- src/types/UiaTextRangeBase.hpp | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index 34461c443bb..e1c767f0a26 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -153,9 +153,9 @@ namespace Microsoft::Console::Types // IUnknown methods IFACEMETHODIMP_(ULONG) - AddRef() override; + AddRef() override; IFACEMETHODIMP_(ULONG) - Release() override; + Release() override; IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) override; @@ -309,8 +309,8 @@ namespace Microsoft::Console::Types // - viewport - the viewport to use for the conversion // Return Value: // - the equivalent ViewportRow. - static constexpr const ViewportRow UiaTextRangeBase::_screenInfoRowToViewportRow(const ScreenInfoRow row, - const SMALL_RECT viewport) noexcept + static constexpr const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row, + const SMALL_RECT viewport) noexcept { return row - viewport.Top; } @@ -359,40 +359,40 @@ namespace Microsoft::Console::Types _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacter(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacter(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterForward(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacterForward(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterBackward(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacterBackward(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitLine(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); - - static std::tuple - _moveEndpointByUnitDocument(gsl::not_null pData, + _moveEndpointByUnitLine(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ gsl::not_null const pAmountMoved); + static std::tuple + _moveEndpointByUnitDocument(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); + #ifdef UNIT_TESTING friend class ::UiaTextRangeTests; #endif From b7c1e050609b89a1481743c11216724355764174 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 4 Sep 2019 13:40:10 -0700 Subject: [PATCH 131/154] code formatter, you're killing me. --- src/types/UiaTextRangeBase.hpp | 50 +++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/types/UiaTextRangeBase.hpp b/src/types/UiaTextRangeBase.hpp index e1c767f0a26..3b9e4c01b4b 100644 --- a/src/types/UiaTextRangeBase.hpp +++ b/src/types/UiaTextRangeBase.hpp @@ -153,9 +153,9 @@ namespace Microsoft::Console::Types // IUnknown methods IFACEMETHODIMP_(ULONG) - AddRef() override; + AddRef() override; IFACEMETHODIMP_(ULONG) - Release() override; + Release() override; IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_result_maybenull_ void** ppInterface) override; @@ -359,40 +359,40 @@ namespace Microsoft::Console::Types _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacter(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacter(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterForward(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacterForward(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitCharacterBackward(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); + _moveEndpointByUnitCharacterBackward(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); static std::tuple - _moveEndpointByUnitLine(gsl::not_null pData, + _moveEndpointByUnitLine(gsl::not_null pData, + const int moveCount, + const TextPatternRangeEndpoint endpoint, + const MoveState moveState, + _Out_ gsl::not_null const pAmountMoved); + + static std::tuple + _moveEndpointByUnitDocument(gsl::not_null pData, const int moveCount, const TextPatternRangeEndpoint endpoint, const MoveState moveState, _Out_ gsl::not_null const pAmountMoved); - static std::tuple - _moveEndpointByUnitDocument(gsl::not_null pData, - const int moveCount, - const TextPatternRangeEndpoint endpoint, - const MoveState moveState, - _Out_ gsl::not_null const pAmountMoved); - #ifdef UNIT_TESTING friend class ::UiaTextRangeTests; #endif From ce3028e12f97c2641f8241b8f61fbd99b97525bb Mon Sep 17 00:00:00 2001 From: Kaiyu Wang Date: Wed, 4 Sep 2019 14:34:06 -0700 Subject: [PATCH 132/154] Clean up boundary between terminal app and terminal page (#2208) * change 1: add settings pointer and some member variables to page * clean up the boundary between Page and App - First working version * First CR review change * Sync and remove declaration of TraceLogger provider * Code review round 2 - apply missed new changes * remove useless comment * CR change round 3 * CR minor changes * apply changes from Aug 6th to Aug 14th * Code review changes round 4 * Apply changes on Aug 16 * Cr changes on 8/20 * CR changes on 8-26 * correct syncing mistakes and fix formatting issues * CR changes on 8-29 * CR changes 9-4 * apply new changes of App * Format fix --- src/cascadia/TerminalApp/App.cpp | 1269 +---------------- src/cascadia/TerminalApp/App.h | 118 +- src/cascadia/TerminalApp/App.idl | 13 +- .../TerminalApp/AppActionHandlers.cpp | 80 +- src/cascadia/TerminalApp/TerminalPage.cpp | 1246 +++++++++++++++- src/cascadia/TerminalApp/TerminalPage.h | 132 ++ src/cascadia/TerminalApp/TerminalPage.idl | 6 + src/cascadia/WindowsTerminal/AppHost.cpp | 12 +- src/cascadia/WindowsTerminal/AppHost.h | 6 +- 9 files changed, 1531 insertions(+), 1351 deletions(-) diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index d8720d21bbf..421aa4577e1 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -6,18 +6,14 @@ #include #include "App.g.cpp" -#include "TerminalPage.h" -#include "Utils.h" using namespace winrt::Windows::ApplicationModel::DataTransfer; using namespace winrt::Windows::UI::Xaml; -using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Core; using namespace winrt::Windows::System; using namespace winrt::Microsoft::Terminal; using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::TerminalControl; -using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace ::TerminalApp; namespace winrt @@ -52,7 +48,7 @@ static const std::array settingsLoadErrorsLabels { // Return Value: // - the localized string for the given type, if it exists. template -static winrt::hstring _GetMessageText(uint32_t index, std::array keys, ScopedResourceLoader loader) +static winrt::hstring _GetMessageText(uint32_t index, std::array keys, ScopedResourceLoader& loader) { if (index < keys.size()) { @@ -70,7 +66,7 @@ static winrt::hstring _GetMessageText(uint32_t index, std::array(warning), settingsLoadWarningsLabels, loader); } @@ -84,7 +80,7 @@ static winrt::hstring _GetWarningText(::TerminalApp::SettingsLoadWarnings warnin // - loader: the ScopedResourceLoader to use to look up the localized string. // Return Value: // - localized text for the given error -static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error, ScopedResourceLoader loader) +static winrt::hstring _GetErrorText(::TerminalApp::SettingsLoadErrors error, ScopedResourceLoader& loader) { return _GetMessageText(static_cast(error), settingsLoadErrorsLabels, loader); } @@ -117,12 +113,9 @@ static Documents::Run _BuildErrorRun(const winrt::hstring& text, const ResourceD namespace winrt::TerminalApp::implementation { App::App() : - _settings{}, - _tabs{}, - _loadedInitialSettings{ false }, - _settingsLoadedResult{ S_OK }, _dialogLock{}, - _resourceLoader{ L"TerminalApp/Resources" } + _loadedInitialSettings{ false }, + _settingsLoadedResult{ S_OK } { // For your own sanity, it's better to do setup outside the ctor. // If you do any setup in the ctor that ends up throwing an exception, @@ -132,6 +125,13 @@ namespace winrt::TerminalApp::implementation // Initialize will become protected or be deleted when GH#1339 (workaround for MSFT:22116519) are fixed. Initialize(); + + _resourceLoader = std::make_shared(L"TerminalApp/Resources"); + + // The TerminalPage has to be constructed during our construction, to + // make sure that there's a terminal page for callers of + // SetTitleBarContent + _root = winrt::make_self(_resourceLoader); } // Method Description: @@ -149,47 +149,11 @@ namespace winrt::TerminalApp::implementation // this as a MTA, before the app is Create()'d WINRT_ASSERT(_loadedInitialSettings); - /* !!! TODO - This is not the correct way to host a XAML page. This exists today because we valued - getting a .xaml over tearing out all of the terminal logic and splitting it across App - and Page. - The work to clarify the boundary between app global state and "terminal page" state - is tracked in GH#1878. - */ - auto terminalPage = winrt::make_self(); - _root = terminalPage.as(); - _tabContent = terminalPage->TabContent(); - _tabRow = terminalPage->TabRow(); - _tabView = _tabRow.TabView(); - _newTabButton = _tabRow.NewTabButton(); - - if (_settings->GlobalSettings().GetShowTabsInTitlebar()) - { - // Remove the TabView from the page. We'll hang on to it, we need to - // put it in the titlebar. - uint32_t index = 0; - if (terminalPage->Root().Children().IndexOf(_tabRow, index)) - { - terminalPage->Root().Children().RemoveAt(index); - } - - // Inform the host that our titlebar content has changed. - _setTitleBarContentHandlers(*this, _tabRow); - } - - // Event Bindings (Early) - _newTabButton.Click([this](auto&&, auto&&) { - this->_OpenNewTab(std::nullopt); - }); - _tabView.SelectionChanged({ this, &App::_OnTabSelectionChanged }); - _tabView.TabClosing({ this, &App::_OnTabClosing }); - _tabView.Items().VectorChanged({ this, &App::_OnTabItemsChanged }); - _root.Loaded({ this, &App::_OnLoaded }); + _root->ShowDialog({ this, &App::_ShowDialog }); - _CreateNewTabFlyout(); - _OpenNewTab(std::nullopt); - - _tabContent.SizeChanged({ this, &App::_OnContentSizeChanged }); + _root->SetSettings(_settings, false); + _root->Loaded({ this, &App::_OnLoaded }); + _root->Create(); _ApplyTheme(_settings->GlobalSettings().GetRequestedTheme()); @@ -209,12 +173,9 @@ namespace winrt::TerminalApp::implementation // - Only one dialog can be visible at a time. If another dialog is visible // when this is called, nothing happens. // Arguments: - // - titleElement: the element to use as the title of this ContentDialog - // - contentElement: the element to use as the content of this ContentDialog - // - closeButtonText: The string to use on the close button - fire_and_forget App::_ShowDialog(const IInspectable& titleElement, - const IInspectable& contentElement, - const winrt::hstring& closeButtonText) + // sender: unused + // dialog: the dialog object that is going to show up + fire_and_forget App::_ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog) { // DON'T release this lock in a wil::scope_exit. The scope_exit will get // called when we await, which is not what we want. @@ -225,15 +186,10 @@ namespace winrt::TerminalApp::implementation return; } - Controls::ContentDialog dialog; - dialog.Title(titleElement); - dialog.Content(contentElement); - dialog.CloseButtonText(closeButtonText); - // IMPORTANT: This is necessary as documented in the ContentDialog MSDN docs. // Since we're hosting the dialog in a Xaml island, we need to connect it to the // xaml tree somehow. - dialog.XamlRoot(_root.XamlRoot()); + dialog.XamlRoot(_root->XamlRoot()); // IMPORTANT: Set the requested theme of the dialog, because the // PopupRoot isn't directly in the Xaml tree of our root. So the dialog @@ -244,25 +200,7 @@ namespace winrt::TerminalApp::implementation Controls::ContentDialogResult result = co_await dialog.ShowAsync(Controls::ContentDialogPlacement::Popup); // After the dialog is dismissed, the dialog lock (held by `lock`) will - // be released so another can be shown. - } - - // Method Description: - // - Show a ContentDialog with a single "Ok" button to dismiss. Looks up the - // the title and text from our Resources using the provided keys. - // - Only one dialog can be visible at a time. If another dialog is visible - // when this is called, nothing happens. See _ShowDialog for details - // Arguments: - // - titleKey: The key to use to lookup the title text from our resources. - // - contentKey: The key to use to lookup the content text from our resources. - void App::_ShowOkDialog(const winrt::hstring& titleKey, - const winrt::hstring& contentKey) - { - auto title = _resourceLoader.GetLocalizedString(titleKey); - auto message = _resourceLoader.GetLocalizedString(contentKey); - auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); - - _ShowDialog(winrt::box_value(title), winrt::box_value(message), buttonText); + // be released so another can be shown } // Method Description: @@ -276,10 +214,11 @@ namespace winrt::TerminalApp::implementation // - titleKey: The key to use to lookup the title text from our resources. // - contentKey: The key to use to lookup the content text from our resources. void App::_ShowLoadErrorsDialog(const winrt::hstring& titleKey, - const winrt::hstring& contentKey) + const winrt::hstring& contentKey, + HRESULT settingsLoadedResult) { - auto title = _resourceLoader.GetLocalizedString(titleKey); - auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); + auto title = _resourceLoader->GetLocalizedString(titleKey); + auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); Controls::TextBlock warningsTextBlock; // Make sure you can copy-paste @@ -288,11 +227,11 @@ namespace winrt::TerminalApp::implementation warningsTextBlock.TextWrapping(TextWrapping::Wrap); winrt::Windows::UI::Xaml::Documents::Run errorRun; - const auto errorLabel = _resourceLoader.GetLocalizedString(contentKey); + const auto errorLabel = _resourceLoader->GetLocalizedString(contentKey); errorRun.Text(errorLabel); warningsTextBlock.Inlines().Append(errorRun); - if (FAILED(_settingsLoadedResult)) + if (FAILED(settingsLoadedResult)) { if (!_settingsLoadExceptionText.empty()) { @@ -302,11 +241,16 @@ namespace winrt::TerminalApp::implementation // Add a note that we're using the default settings in this case. winrt::Windows::UI::Xaml::Documents::Run usingDefaultsRun; - const auto usingDefaultsText = _resourceLoader.GetLocalizedString(L"UsingDefaultSettingsText"); + const auto usingDefaultsText = _resourceLoader->GetLocalizedString(L"UsingDefaultSettingsText"); usingDefaultsRun.Text(usingDefaultsText); warningsTextBlock.Inlines().Append(usingDefaultsRun); - _ShowDialog(winrt::box_value(title), warningsTextBlock, buttonText); + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(warningsTextBlock)); + dialog.CloseButtonText(buttonText); + + _ShowDialog(nullptr, dialog); } // Method Description: @@ -317,8 +261,8 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details void App::_ShowLoadWarningsDialog() { - auto title = _resourceLoader.GetLocalizedString(L"SettingsValidateErrorTitle"); - auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); + auto title = _resourceLoader->GetLocalizedString(L"SettingsValidateErrorTitle"); + auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); Controls::TextBlock warningsTextBlock; // Make sure you can copy-paste @@ -330,83 +274,19 @@ namespace winrt::TerminalApp::implementation for (const auto& warning : warnings) { // Try looking up the warning message key for each warning. - const auto warningText = _GetWarningText(warning, _resourceLoader); + const auto warningText = _GetWarningText(warning, *_resourceLoader); if (!warningText.empty()) { warningsTextBlock.Inlines().Append(_BuildErrorRun(warningText, Resources())); } } - _ShowDialog(winrt::box_value(title), warningsTextBlock, buttonText); - } + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(warningsTextBlock)); + dialog.CloseButtonText(buttonText); - // Method Description: - // - Show a dialog with "About" information. Displays the app's Display - // Name, version, getting started link, documentation link, and release - // Notes link. - void App::_ShowAboutDialog() - { - const auto title = _resourceLoader.GetLocalizedString(L"AboutTitleText"); - const auto versionLabel = _resourceLoader.GetLocalizedString(L"VersionLabelText"); - const auto gettingStartedLabel = _resourceLoader.GetLocalizedString(L"GettingStartedLabelText"); - const auto documentationLabel = _resourceLoader.GetLocalizedString(L"DocumentationLabelText"); - const auto releaseNotesLabel = _resourceLoader.GetLocalizedString(L"ReleaseNotesLabelText"); - const auto gettingStartedUriValue = _resourceLoader.GetLocalizedString(L"GettingStartedUriValue"); - const auto documentationUriValue = _resourceLoader.GetLocalizedString(L"DocumentationUriValue"); - const auto releaseNotesUriValue = _resourceLoader.GetLocalizedString(L"ReleaseNotesUriValue"); - const auto package = winrt::Windows::ApplicationModel::Package::Current(); - const auto packageName = package.DisplayName(); - const auto version = package.Id().Version(); - winrt::Windows::UI::Xaml::Documents::Run about; - winrt::Windows::UI::Xaml::Documents::Run gettingStarted; - winrt::Windows::UI::Xaml::Documents::Run documentation; - winrt::Windows::UI::Xaml::Documents::Run releaseNotes; - winrt::Windows::UI::Xaml::Documents::Hyperlink gettingStartedLink; - winrt::Windows::UI::Xaml::Documents::Hyperlink documentationLink; - winrt::Windows::UI::Xaml::Documents::Hyperlink releaseNotesLink; - std::wstringstream aboutTextStream; - - gettingStarted.Text(gettingStartedLabel); - documentation.Text(documentationLabel); - releaseNotes.Text(releaseNotesLabel); - - winrt::Windows::Foundation::Uri gettingStartedUri{ gettingStartedUriValue }; - winrt::Windows::Foundation::Uri documentationUri{ documentationUriValue }; - winrt::Windows::Foundation::Uri releaseNotesUri{ releaseNotesUriValue }; - - gettingStartedLink.NavigateUri(gettingStartedUri); - documentationLink.NavigateUri(documentationUri); - releaseNotesLink.NavigateUri(releaseNotesUri); - - gettingStartedLink.Inlines().Append(gettingStarted); - documentationLink.Inlines().Append(documentation); - releaseNotesLink.Inlines().Append(releaseNotes); - - // Format our about text. It will look like the following: - // - // Version: ... - // Getting Started - // Documentation - // Release Notes - - aboutTextStream << packageName.c_str() << L"\n"; - - aboutTextStream << versionLabel.c_str() << L" "; - aboutTextStream << version.Major << L"." << version.Minor << L"." << version.Build << L"." << version.Revision << L"\n"; - - winrt::hstring aboutText{ aboutTextStream.str() }; - about.Text(aboutText); - - const auto buttonText = _resourceLoader.GetLocalizedString(L"Ok"); - - Controls::TextBlock aboutTextBlock; - aboutTextBlock.Inlines().Append(about); - aboutTextBlock.Inlines().Append(gettingStartedLink); - aboutTextBlock.Inlines().Append(documentationLink); - aboutTextBlock.Inlines().Append(releaseNotesLink); - aboutTextBlock.IsTextSelectionEnabled(true); - - _ShowDialog(winrt::box_value(title), aboutTextBlock, buttonText); + _ShowDialog(nullptr, dialog); } // Method Description: @@ -424,7 +304,7 @@ namespace winrt::TerminalApp::implementation { const winrt::hstring titleKey = L"InitialJsonParseErrorTitle"; const winrt::hstring textKey = L"InitialJsonParseErrorText"; - _ShowLoadErrorsDialog(titleKey, textKey); + _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); } else if (_settingsLoadedResult == S_FALSE) { @@ -469,215 +349,6 @@ namespace winrt::TerminalApp::implementation return _settings->GlobalSettings().GetShowTabsInTitlebar(); } - // Method Description: - // - Builds the flyout (dropdown) attached to the new tab button, and - // attaches it to the button. Populates the flyout with one entry per - // Profile, displaying the profile's name. Clicking each flyout item will - // open a new tab with that profile. - // Below the profiles are the static menu items: settings, feedback - void App::_CreateNewTabFlyout() - { - auto newTabFlyout = Controls::MenuFlyout{}; - auto keyBindings = _settings->GetKeybindings(); - - const GUID defaultProfileGuid = _settings->GlobalSettings().GetDefaultProfile(); - // the number of profiles should not change in the loop for this to work - auto const profileCount = gsl::narrow_cast(_settings->GetProfiles().size()); - for (int profileIndex = 0; profileIndex < profileCount; profileIndex++) - { - const auto& profile = _settings->GetProfiles()[profileIndex]; - auto profileMenuItem = Controls::MenuFlyoutItem{}; - - // add the keyboard shortcuts for the first 9 profiles - if (profileIndex < 9) - { - // enum value for ShortcutAction::NewTabProfileX; 0==NewTabProfile0 - const auto action = static_cast(profileIndex + static_cast(ShortcutAction::NewTabProfile0)); - auto profileKeyChord = keyBindings.GetKeyBinding(action); - - // make sure we find one to display - if (profileKeyChord) - { - _SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord); - } - } - - auto profileName = profile.GetName(); - winrt::hstring hName{ profileName }; - profileMenuItem.Text(hName); - - // If there's an icon set for this profile, set it as the icon for - // this flyout item. - if (profile.HasIcon()) - { - profileMenuItem.Icon(_GetIconFromProfile(profile)); - } - - if (profile.GetGuid() == defaultProfileGuid) - { - // Contrast the default profile with others in font weight. - profileMenuItem.FontWeight(FontWeights::Bold()); - } - - profileMenuItem.Click([this, profileIndex](auto&&, auto&&) { - this->_OpenNewTab({ profileIndex }); - }); - newTabFlyout.Items().Append(profileMenuItem); - } - - // add menu separator - auto separatorItem = Controls::MenuFlyoutSeparator{}; - newTabFlyout.Items().Append(separatorItem); - - // add static items - { - // Create the settings button. - auto settingsItem = Controls::MenuFlyoutItem{}; - settingsItem.Text(_resourceLoader.GetLocalizedString(L"SettingsMenuItem")); - - Controls::SymbolIcon ico{}; - ico.Symbol(Controls::Symbol::Setting); - settingsItem.Icon(ico); - - settingsItem.Click({ this, &App::_SettingsButtonOnClick }); - newTabFlyout.Items().Append(settingsItem); - - auto settingsKeyChord = keyBindings.GetKeyBinding(ShortcutAction::OpenSettings); - if (settingsKeyChord) - { - _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord); - } - - // Create the feedback button. - auto feedbackFlyout = Controls::MenuFlyoutItem{}; - feedbackFlyout.Text(_resourceLoader.GetLocalizedString(L"FeedbackMenuItem")); - - Controls::FontIcon feedbackIco{}; - feedbackIco.Glyph(L"\xE939"); - feedbackIco.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); - feedbackFlyout.Icon(feedbackIco); - - feedbackFlyout.Click({ this, &App::_FeedbackButtonOnClick }); - newTabFlyout.Items().Append(feedbackFlyout); - - // Create the about button. - auto aboutFlyout = Controls::MenuFlyoutItem{}; - aboutFlyout.Text(_resourceLoader.GetLocalizedString(L"AboutMenuItem")); - - Controls::SymbolIcon aboutIco{}; - aboutIco.Symbol(Controls::Symbol::Help); - aboutFlyout.Icon(aboutIco); - - aboutFlyout.Click({ this, &App::_AboutButtonOnClick }); - newTabFlyout.Items().Append(aboutFlyout); - } - - _newTabButton.Flyout(newTabFlyout); - } - - // Function Description: - // Called when the openNewTabDropdown keybinding is used. - // Adds the flyout show option to left-align the dropdown with the split button. - // Shows the dropdown flyout. - void App::_OpenNewTabDropdown() - { - Controls::Primitives::FlyoutShowOptions options{}; - options.Placement(Controls::Primitives::FlyoutPlacementMode::BottomEdgeAlignedLeft); - _newTabButton.Flyout().ShowAt(_newTabButton, options); - } - - // Function Description: - // - Called when the settings button is clicked. ShellExecutes the settings - // file, as to open it in the default editor for .json files. Does this in - // a background thread, as to not hang/crash the UI thread. - fire_and_forget LaunchSettings() - { - // This will switch the execution of the function to a background (not - // UI) thread. This is IMPORTANT, because the Windows.Storage API's - // (used for retrieving the path to the file) will crash on the UI - // thread, because the main thread is a STA. - co_await winrt::resume_background(); - - const auto settingsPath = CascadiaSettings::GetSettingsPath(); - - HINSTANCE res = ShellExecute(nullptr, nullptr, settingsPath.c_str(), nullptr, nullptr, SW_SHOW); - if (static_cast(reinterpret_cast(res)) <= 32) - { - ShellExecute(nullptr, nullptr, L"notepad", settingsPath.c_str(), nullptr, SW_SHOW); - } - } - - // Method Description: - // - Called when the settings button is clicked. Launches a background - // thread to open the settings file in the default JSON editor. - // Arguments: - // - - // Return Value: - // - - void App::_SettingsButtonOnClick(const IInspectable&, - const RoutedEventArgs&) - { - LaunchSettings(); - } - - // Method Description: - // - Called when the feedback button is clicked. Launches github in your - // default browser, navigated to the "issues" page of the Terminal repo. - void App::_FeedbackButtonOnClick(const IInspectable&, - const RoutedEventArgs&) - { - const auto feedbackUriValue = _resourceLoader.GetLocalizedString(L"FeedbackUriValue"); - - winrt::Windows::System::Launcher::LaunchUriAsync({ feedbackUriValue }); - } - - // Method Description: - // - Called when the about button is clicked. See _ShowAboutDialog for more info. - // Arguments: - // - - // Return Value: - // - - void App::_AboutButtonOnClick(const IInspectable&, - const RoutedEventArgs&) - { - _ShowAboutDialog(); - } - - // Method Description: - // - Register our event handlers with the given keybindings object. This - // should be done regardless of what the events are actually bound to - - // this simply ensures the AppKeyBindings object will call us correctly - // for each event. - // Arguments: - // - bindings: A AppKeyBindings object to wire up with our event handlers - void App::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept - { - // Hook up the KeyBinding object's events to our handlers. - // They should all be hooked up here, regardless of whether or not - // there's an actual keychord for them. - - bindings.NewTab({ this, &App::_HandleNewTab }); - bindings.OpenNewTabDropdown({ this, &App::_HandleOpenNewTabDropdown }); - bindings.DuplicateTab({ this, &App::_HandleDuplicateTab }); - bindings.CloseTab({ this, &App::_HandleCloseTab }); - bindings.ClosePane({ this, &App::_HandleClosePane }); - bindings.ScrollUp({ this, &App::_HandleScrollUp }); - bindings.ScrollDown({ this, &App::_HandleScrollDown }); - bindings.NextTab({ this, &App::_HandleNextTab }); - bindings.PrevTab({ this, &App::_HandlePrevTab }); - bindings.SplitVertical({ this, &App::_HandleSplitVertical }); - bindings.SplitHorizontal({ this, &App::_HandleSplitHorizontal }); - bindings.ScrollUpPage({ this, &App::_HandleScrollUpPage }); - bindings.ScrollDownPage({ this, &App::_HandleScrollDownPage }); - bindings.OpenSettings({ this, &App::_HandleOpenSettings }); - bindings.PasteText({ this, &App::_HandlePasteText }); - bindings.NewTabWithProfile({ this, &App::_HandleNewTabWithProfile }); - bindings.SwitchToTab({ this, &App::_HandleSwitchToTab }); - bindings.ResizePane({ this, &App::_HandleResizePane }); - bindings.MoveFocus({ this, &App::_HandleMoveFocus }); - bindings.CopyText({ this, &App::_HandleCopyText }); - } - // Method Description: // - Attempt to load the settings. If we fail for any reason, returns an error. // Return Value: @@ -702,7 +373,7 @@ namespace winrt::TerminalApp::implementation catch (const ::TerminalApp::SettingsException& ex) { hr = E_INVALIDARG; - _settingsLoadExceptionText = _GetErrorText(ex.Error(), _resourceLoader); + _settingsLoadExceptionText = _GetErrorText(ex.Error(), *_resourceLoader); } catch (...) { @@ -737,8 +408,6 @@ namespace winrt::TerminalApp::implementation _settings->CreateDefaults(); } - _HookupKeyBindings(_settings->GetKeybindings()); - _loadedInitialSettings = true; // Register for directory change notification. @@ -813,17 +482,17 @@ namespace winrt::TerminalApp::implementation if (FAILED(_settingsLoadedResult)) { - _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + _root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { const winrt::hstring titleKey = L"ReloadJsonParseErrorTitle"; const winrt::hstring textKey = L"ReloadJsonParseErrorText"; - _ShowLoadErrorsDialog(titleKey, textKey); + _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); }); return; } else if (_settingsLoadedResult == S_FALSE) { - _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + _root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { _ShowLoadWarningsDialog(); }); } @@ -831,83 +500,15 @@ namespace winrt::TerminalApp::implementation // Here, we successfully reloaded the settings, and created a new // TerminalSettings object. - // Re-wire the keybindings to their handlers, as we'll have created a - // new AppKeyBindings object. - _HookupKeyBindings(_settings->GetKeybindings()); - - // Refresh UI elements - - auto profiles = _settings->GetProfiles(); - for (auto& profile : profiles) - { - const GUID profileGuid = profile.GetGuid(); - TerminalSettings settings = _settings->MakeSettings(profileGuid); - - for (auto& tab : _tabs) - { - // Attempt to reload the settings of any panes with this profile - tab->UpdateSettings(settings, profileGuid); - } - } - - // Update the icon of the tab for the currently focused profile in that tab. - for (auto& tab : _tabs) - { - _UpdateTabIcon(tab); - _UpdateTitle(tab); - } + // Update the settings in TerminalPage + _root->SetSettings(_settings, true); - _root.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + _root->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { // Refresh the UI theme _ApplyTheme(_settings->GlobalSettings().GetRequestedTheme()); - - // repopulate the new tab button's flyout with entries for each - // profile, which might have changed - _CreateNewTabFlyout(); }); } - // Method Description: - // - Get the icon of the currently focused terminal control, and set its - // tab's icon to that icon. - // Arguments: - // - tab: the Tab to update the title for. - void App::_UpdateTabIcon(std::shared_ptr tab) - { - const auto lastFocusedProfileOpt = tab->GetFocusedProfile(); - if (lastFocusedProfileOpt.has_value()) - { - const auto lastFocusedProfile = lastFocusedProfileOpt.value(); - const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile); - if (matchingProfile) - { - tab->UpdateIcon(matchingProfile->GetExpandedIconPath()); - } - else - { - tab->UpdateIcon({}); - } - } - } - - // Method Description: - // - Get the title of the currently focused terminal control, and set it's - // tab's text to that text. If this tab is the focused tab, then also - // bubble this title to any listeners of our TitleChanged event. - // Arguments: - // - tab: the Tab to update the title for. - void App::_UpdateTitle(std::shared_ptr tab) - { - auto newTabTitle = tab->GetFocusedTitle(); - tab->SetTabText(newTabTitle); - - if (_settings->GlobalSettings().GetShowTitleInTitlebar() && - tab->IsFocused()) - { - _titleChangeHandlers(newTabTitle); - } - } - // Method Description: // - Update the current theme of the application. This will trigger our // RequestedThemeChanged event, to have our host change the theme of the @@ -922,417 +523,7 @@ namespace winrt::TerminalApp::implementation UIElement App::GetRoot() noexcept { - return _root; - } - - void App::_SetFocusedTabIndex(int tabIndex) - { - // GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex) - // sometimes set focus to an incorrect tab after removing some tabs - auto tab = _tabs.at(tabIndex); - _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tab, this]() { - auto tabViewItem = tab->GetTabViewItem(); - _tabView.SelectedItem(tabViewItem); - }); - } - - // Method Description: - // - Handle changes in tab layout. - void App::_UpdateTabView() - { - // Show tabs when there's more than 1, or the user has chosen to always - // show the tab bar. - const bool isVisible = _settings->GlobalSettings().GetShowTabsInTitlebar() || - (_tabs.size() > 1) || - _settings->GlobalSettings().GetAlwaysShowTabs(); - - // collapse/show the tabs themselves - _tabView.Visibility(isVisible ? Visibility::Visible : Visibility::Collapsed); - - // collapse/show the row that the tabs are in. - // NaN is the special value XAML uses for "Auto" sizing. - _tabRow.Height(isVisible ? NAN : 0); - } - - // Method Description: - // - Open a new tab. This will create the TerminalControl hosting the - // terminal, and add a new Tab to our list of tabs. The method can - // optionally be provided a profile index, which will be used to create - // a tab using the profile in that index. - // If no index is provided, the default profile will be used. - // Arguments: - // - profileIndex: an optional index into the list of profiles to use to - // initialize this tab up with. - void App::_OpenNewTab(std::optional profileIndex) - { - GUID profileGuid; - - if (profileIndex) - { - const auto realIndex = profileIndex.value(); - const auto profiles = _settings->GetProfiles(); - - // If we don't have that many profiles, then do nothing. - if (realIndex >= gsl::narrow(profiles.size())) - { - return; - } - - const auto& selectedProfile = profiles[realIndex]; - profileGuid = selectedProfile.GetGuid(); - } - else - { - // Getting Guid for default profile - const auto globalSettings = _settings->GlobalSettings(); - profileGuid = globalSettings.GetDefaultProfile(); - } - - TerminalSettings settings = _settings->MakeSettings(profileGuid); - _CreateNewTabFromSettings(profileGuid, settings); - - const int tabCount = static_cast(_tabs.size()); - TraceLoggingWrite( - g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider - "TabInformation", - TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"), - TraceLoggingInt32(tabCount, "TabCount", "Count of tabs curently opened in TerminalApp"), - TraceLoggingBool(profileIndex.has_value(), "ProfileSpecified", "Whether the new tab specified a profile explicitly"), - TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); - } - - // Function Description: - // - Copies and processes the text data from the Windows Clipboard. - // Does some of this in a background thread, as to not hang/crash the UI thread. - // Arguments: - // - eventArgs: the PasteFromClipboard event sent from the TermControl - fire_and_forget PasteFromClipboard(PasteFromClipboardEventArgs eventArgs) - { - const DataPackageView data = Clipboard::GetContent(); - - // This will switch the execution of the function to a background (not - // UI) thread. This is IMPORTANT, because the getting the clipboard data - // will crash on the UI thread, because the main thread is a STA. - co_await winrt::resume_background(); - - hstring text = L""; - if (data.Contains(StandardDataFormats::Text())) - { - text = co_await data.GetTextAsync(); - } - eventArgs.HandleClipboardData(text); - } - - // Method Description: - // - Connects event handlers to the TermControl for events that we want to - // handle. This includes: - // * the Copy and Paste events, for setting and retrieving clipboard data - // on the right thread - // * the TitleChanged event, for changing the text of the tab - // * the GotFocus event, for changing the title/icon in the tab when a new - // control is focused - // Arguments: - // - term: The newly created TermControl to connect the events for - // - hostingTab: The Tab that's hosting this TermControl instance - void App::_RegisterTerminalEvents(TermControl term, std::shared_ptr hostingTab) - { - // Add an event handler when the terminal's selection wants to be copied. - // When the text buffer data is retrieved, we'll copy the data into the Clipboard - term.CopyToClipboard({ this, &App::_CopyToClipboardHandler }); - - // Add an event handler when the terminal wants to paste data from the Clipboard. - term.PasteFromClipboard({ this, &App::_PasteFromClipboardHandler }); - - // Don't capture a strong ref to the tab. If the tab is removed as this - // is called, we don't really care anymore about handling the event. - std::weak_ptr weakTabPtr = hostingTab; - term.TitleChanged([this, weakTabPtr](auto newTitle) { - auto tab = weakTabPtr.lock(); - if (!tab) - { - return; - } - // The title of the control changed, but not necessarily the title - // of the tab. Get the title of the focused pane of the tab, and set - // the tab's text to the focused panes' text. - _UpdateTitle(tab); - }); - - term.GotFocus([this, weakTabPtr](auto&&, auto&&) { - auto tab = weakTabPtr.lock(); - if (!tab) - { - return; - } - // Update the focus of the tab's panes - tab->UpdateFocus(); - - // Possibly update the title of the tab, window to match the newly - // focused pane. - _UpdateTitle(tab); - - // Possibly update the icon of the tab. - _UpdateTabIcon(tab); - }); - } - - // Method Description: - // - Creates a new tab with the given settings. If the tab bar is not being - // currently displayed, it will be shown. - // Arguments: - // - settings: the TerminalSettings object to use to create the TerminalControl with. - void App::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings) - { - // Initialize the new tab - - // Create a connection based on the values in our settings object. - const auto connection = _CreateConnectionFromSettings(profileGuid, settings); - - TermControl term{ settings, connection }; - - // Add the new tab to the list of our tabs. - auto newTab = _tabs.emplace_back(std::make_shared(profileGuid, term)); - - const auto* const profile = _settings->FindProfile(profileGuid); - - // Hookup our event handlers to the new terminal - _RegisterTerminalEvents(term, newTab); - - auto tabViewItem = newTab->GetTabViewItem(); - _tabView.Items().Append(tabViewItem); - - // Set this profile's tab to the icon the user specified - if (profile != nullptr && profile->HasIcon()) - { - newTab->UpdateIcon(profile->GetExpandedIconPath()); - } - - tabViewItem.PointerPressed({ this, &App::_OnTabClick }); - - // When the tab is closed, remove it from our list of tabs. - newTab->Closed([tabViewItem, this]() { - _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tabViewItem, this]() { - _RemoveTabViewItem(tabViewItem); - }); - }); - - // This is one way to set the tab's selected background color. - // tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), a Brush?); - - // This kicks off TabView::SelectionChanged, in response to which we'll attach the terminal's - // Xaml control to the Xaml root. - _tabView.SelectedItem(tabViewItem); - } - - // Method Description: - // - Returns the index in our list of tabs of the currently focused tab. If - // no tab is currently selected, returns -1. - // Return Value: - // - the index of the currently focused tab if there is one, else -1 - int App::_GetFocusedTabIndex() const - { - // GH#1117: This is a workaround because _tabView.SelectedIndex() - // sometimes return incorrect result after removing some tabs - uint32_t focusedIndex; - if (_tabView.Items().IndexOf(_tabView.SelectedItem(), focusedIndex)) - { - return focusedIndex; - } - return -1; - } - - void App::_OpenSettings() - { - LaunchSettings(); - } - - // Method Description: - // - Close the currently focused tab. Focus will move to the left, if possible. - void App::_CloseFocusedTab() - { - int focusedTabIndex = _GetFocusedTabIndex(); - std::shared_ptr focusedTab{ _tabs[focusedTabIndex] }; - _RemoveTabViewItem(focusedTab->GetTabViewItem()); - } - - // Method Description: - // - Close the currently focused pane. If the pane is the last pane in the - // tab, the tab will also be closed. This will happen when we handle the - // tab's Closed event. - void App::_CloseFocusedPane() - { - int focusedTabIndex = _GetFocusedTabIndex(); - std::shared_ptr focusedTab{ _tabs[focusedTabIndex] }; - focusedTab->ClosePane(); - } - - // Method Description: - // - Move the viewport of the terminal of the currently focused tab up or - // down a number of lines. Negative values of `delta` will move the - // view up, and positive values will move the viewport down. - // Arguments: - // - delta: a number of lines to move the viewport relative to the current viewport. - void App::_Scroll(int delta) - { - int focusedTabIndex = _GetFocusedTabIndex(); - _tabs[focusedTabIndex]->Scroll(delta); - } - - // Method Description: - // - Move the viewport of the terminal of the currently focused tab up or - // down a page. The page length will be dependent on the terminal view height. - // Negative values of `delta` will move the view up by one page, and positive values - // will move the viewport down by one page. - // Arguments: - // - delta: The direction to move the view relative to the current viewport(it - // is clamped between -1 and 1) - void App::_ScrollPage(int delta) - { - delta = std::clamp(delta, -1, 1); - const auto focusedTabIndex = _GetFocusedTabIndex(); - const auto control = _GetFocusedControl(); - const auto termHeight = control.GetViewHeight(); - _tabs[focusedTabIndex]->Scroll(termHeight * delta); - } - - // Method Description: - // - Attempt to move a separator between panes, as to resize each child on - // either size of the separator. See Pane::ResizePane for details. - // - Moves a separator on the currently focused tab. - // Arguments: - // - direction: The direction to move the separator in. - // Return Value: - // - - void App::_ResizePane(const Direction& direction) - { - const auto focusedTabIndex = _GetFocusedTabIndex(); - _tabs[focusedTabIndex]->ResizePane(direction); - } - - // Method Description: - // - Attempt to move focus between panes, as to focus the child on - // the other side of the separator. See Pane::NavigateFocus for details. - // - Moves the focus of the currently focused tab. - // Arguments: - // - direction: The direction to move the focus in. - // Return Value: - // - - void App::_MoveFocus(const Direction& direction) - { - const auto focusedTabIndex = _GetFocusedTabIndex(); - _tabs[focusedTabIndex]->NavigateFocus(direction); - } - - // Method Description: - // - Copy text from the focused terminal to the Windows Clipboard - // Arguments: - // - trimTrailingWhitespace: enable removing any whitespace from copied selection - // and get text to appear on separate lines. - // Return Value: - // - true iff we we able to copy text (if a selection was active) - bool App::_CopyText(const bool trimTrailingWhitespace) - { - const auto control = _GetFocusedControl(); - return control.CopySelectionToClipboard(trimTrailingWhitespace); - } - - // Method Description: - // - Paste text from the Windows Clipboard to the focused terminal - void App::_PasteText() - { - const auto control = _GetFocusedControl(); - control.PasteTextFromClipboard(); - } - - // Method Description: - // - Sets focus to the tab to the right or left the currently selected tab. - void App::_SelectNextTab(const bool bMoveRight) - { - int focusedTabIndex = _GetFocusedTabIndex(); - auto tabCount = _tabs.size(); - // Wraparound math. By adding tabCount and then calculating modulo tabCount, - // we clamp the values to the range [0, tabCount) while still supporting moving - // leftward from 0 to tabCount - 1. - _SetFocusedTabIndex( - static_cast((tabCount + focusedTabIndex + (bMoveRight ? 1 : -1)) % tabCount)); - } - - // Method Description: - // - Sets focus to the desired tab. Returns false if the provided tabIndex - // is greater than the number of tabs we have. - // Return Value: - // true iff we were able to select that tab index, false otherwise - bool App::_SelectTab(const int tabIndex) - { - if (tabIndex >= 0 && tabIndex < gsl::narrow_cast(_tabs.size())) - { - _SetFocusedTabIndex(tabIndex); - return true; - } - return false; - } - - // Method Description: - // - Responds to the TabView control's Selection Changed event (to move a - // new terminal control into focus.) - // Arguments: - // - sender: the control that originated this event - // - eventArgs: the event's constituent arguments - void App::_OnTabSelectionChanged(const IInspectable& sender, const Controls::SelectionChangedEventArgs& eventArgs) - { - auto tabView = sender.as(); - auto selectedIndex = tabView.SelectedIndex(); - - // Unfocus all the tabs. - for (auto tab : _tabs) - { - tab->SetFocused(false); - } - - if (selectedIndex >= 0) - { - try - { - auto tab = _tabs.at(selectedIndex); - - _tabContent.Children().Clear(); - _tabContent.Children().Append(tab->GetRootElement()); - - tab->SetFocused(true); - _titleChangeHandlers(GetTitle()); - } - CATCH_LOG(); - } - } - - // Method Description: - // - Responds to the TabView control's Tab Closing event by removing - // the indicated tab from the set and focusing another one. - // The event is cancelled so App maintains control over the - // items in the tabview. - // Arguments: - // - sender: the control that originated this event - // - eventArgs: the event's constituent arguments - void App::_OnTabClosing(const IInspectable& sender, const MUX::Controls::TabViewTabClosingEventArgs& eventArgs) - { - const auto tabViewItem = eventArgs.Item(); - _RemoveTabViewItem(tabViewItem); - - // If we don't cancel the event, the TabView will remove the item itself. - eventArgs.Cancel(true); - } - - // Method Description: - // - Responds to changes in the TabView's item list by changing the tabview's - // visibility. - // Arguments: - // - sender: the control that originated this event - // - eventArgs: the event's constituent arguments - void App::_OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs) - { - _UpdateTabView(); + return _root.as(); } // Method Description: @@ -1342,370 +533,62 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - the title of the focused control if there is one, else "Windows Terminal" - hstring App::GetTitle() + hstring App::Title() { - if (_settings->GlobalSettings().GetShowTitleInTitlebar()) + if (_root) { - auto selectedIndex = _tabView.SelectedIndex(); - if (selectedIndex >= 0) - { - try - { - if (auto focusedControl{ _GetFocusedControl() }) - { - return focusedControl.Title(); - } - } - CATCH_LOG(); - } + return _root->Title(); } return { L"Windows Terminal" }; } // Method Description: - // - Additional responses to clicking on a TabView's item. Currently, just remove tab with middle click - // Arguments: - // - sender: the control that originated this event (TabViewItem) - // - eventArgs: the event's constituent arguments - void App::_OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs) - { - if (eventArgs.GetCurrentPoint(_root).Properties().IsMiddleButtonPressed()) - { - _RemoveTabViewItem(sender); - eventArgs.Handled(true); - } - } - - // Method Description: - // - Duplicates the current focused tab - void App::_DuplicateTabViewItem() - { - const int& focusedTabIndex = _GetFocusedTabIndex(); - const auto& _tab = _tabs.at(focusedTabIndex); - - const auto& profileGuid = _tab->GetFocusedProfile(); - const auto& settings = _settings->MakeSettings(profileGuid); - - _CreateNewTabFromSettings(profileGuid.value(), settings); - } - - // Method Description: - // - Removes the tab (both TerminalControl and XAML) - // Arguments: - // - tabViewItem: the TabViewItem in the TabView that is being removed. - void App::_RemoveTabViewItem(const IInspectable& tabViewItem) - { - // To close the window here, we need to close the hosting window. - if (_tabs.size() == 1) - { - _lastTabClosedHandlers(); - } - uint32_t tabIndexFromControl = 0; - _tabView.Items().IndexOf(tabViewItem, tabIndexFromControl); - auto focusedTabIndex = _GetFocusedTabIndex(); - - // Removing the tab from the collection will destroy its control and disconnect its connection. - _tabs.erase(_tabs.begin() + tabIndexFromControl); - _tabView.Items().RemoveAt(tabIndexFromControl); - - if (tabIndexFromControl == focusedTabIndex) - { - auto const tabCount = gsl::narrow_cast(_tabs.size()); - if (focusedTabIndex >= tabCount) - { - focusedTabIndex = tabCount - 1; - } - else if (focusedTabIndex < 0) - { - focusedTabIndex = 0; - } - - _SelectTab(focusedTabIndex); - } - } - - // Method Description: - // - Gets a colored IconElement for the profile in question. If the profile - // has an `icon` set in the settings, this will return an icon with that - // image in it. Otherwise it will return a nullptr-initialized - // IconElement. - // Arguments: - // - profile: the profile to get the icon from - // Return Value: - // - an IconElement for the profile's icon, if it has one. - Controls::IconElement App::_GetIconFromProfile(const Profile& profile) - { - return profile.HasIcon() ? GetColoredIcon(profile.GetExpandedIconPath()) : Controls::IconElement{ nullptr }; - } - - winrt::Microsoft::Terminal::TerminalControl::TermControl App::_GetFocusedControl() - { - int focusedTabIndex = _GetFocusedTabIndex(); - auto focusedTab = _tabs[focusedTabIndex]; - return focusedTab->GetFocusedTerminalControl(); - } - - // Method Description: - // - Vertically split the focused pane, and place the given TermControl into - // the newly created pane. - // Arguments: - // - profile: The profile GUID to associate with the newly created pane. If - // this is nullopt, use the default profile. - void App::_SplitVertical(const std::optional& profileGuid) - { - _SplitPane(Pane::SplitState::Vertical, profileGuid); - } - - // Method Description: - // - Horizontally split the focused pane and place the given TermControl - // into the newly created pane. - // Arguments: - // - profile: The profile GUID to associate with the newly created pane. If - // this is nullopt, use the default profile. - void App::_SplitHorizontal(const std::optional& profileGuid) - { - _SplitPane(Pane::SplitState::Horizontal, profileGuid); - } - - // Method Description: - // - Split the focused pane either horizontally or vertically, and place the - // given TermControl into the newly created pane. - // - If splitType == SplitState::None, this method does nothing. - // Arguments: - // - splitType: one value from the Pane::SplitState enum, indicating how the - // new pane should be split from its parent. - // - profile: The profile GUID to associate with the newly created pane. If - // this is nullopt, use the default profile. - void App::_SplitPane(const Pane::SplitState splitType, const std::optional& profileGuid) - { - // Do nothing if we're requesting no split. - if (splitType == Pane::SplitState::None) - { - return; - } - - const auto realGuid = profileGuid ? profileGuid.value() : - _settings->GlobalSettings().GetDefaultProfile(); - const auto controlSettings = _settings->MakeSettings(realGuid); - - const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings); - - const int focusedTabIndex = _GetFocusedTabIndex(); - auto focusedTab = _tabs[focusedTabIndex]; - - const auto canSplit = focusedTab->CanSplitPane(splitType); - - if (!canSplit) - { - return; - } - - TermControl newControl{ controlSettings, controlConnection }; - - // Hookup our event handlers to the new terminal - _RegisterTerminalEvents(newControl, focusedTab); - - focusedTab->SplitPane(splitType, realGuid, newControl); - } - - // Method Description: - // - Called when our tab content size changes. This updates each tab with - // the new size, so they have a chance to update each of their panes with - // the new size. + // - Used to tell the app that the titlebar has been clicked. The App won't + // actually recieve any clicks in the titlebar area, so this is a helper + // to clue the app in that a click has happened. The App will use this as + // a indicator that it needs to dismiss any open flyouts. // Arguments: - // - e: the SizeChangedEventArgs with the new size of the tab content area. + // - // Return Value: // - - void App::_OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e) + void App::TitlebarClicked() { - const auto newSize = e.NewSize(); - for (auto& tab : _tabs) + if (_root) { - tab->ResizeContent(newSize); + _root->TitlebarClicked(); } } - // Method Description: - // - Place `copiedData` into the clipboard as text. Triggered when a - // terminal control raises it's CopyToClipboard event. - // Arguments: - // - copiedData: the new string content to place on the clipboard. - void App::_CopyToClipboardHandler(const IInspectable& /*sender*/, - const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData) + // Methods that proxy typed event handlers through TerminalPage + winrt::event_token App::SetTitleBarContent(Windows::Foundation::TypedEventHandler const& handler) { - _root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() { - DataPackage dataPack = DataPackage(); - dataPack.RequestedOperation(DataPackageOperation::Copy); - - // copy text to dataPack - dataPack.SetText(copiedData.Text()); - - // copy html to dataPack - const auto htmlData = copiedData.Html(); - if (!htmlData.empty()) - { - dataPack.SetHtmlFormat(htmlData); - } - - try - { - Clipboard::SetContent(dataPack); - Clipboard::Flush(); - } - CATCH_LOG(); - }); + return _root->SetTitleBarContent(handler); } - - // Method Description: - // - Fires an async event to get data from the clipboard, and paste it to - // the terminal. Triggered when the Terminal Control requests clipboard - // data with it's PasteFromClipboard event. - // Arguments: - // - eventArgs: the PasteFromClipboard event sent from the TermControl - void App::_PasteFromClipboardHandler(const IInspectable& /*sender*/, - const PasteFromClipboardEventArgs& eventArgs) + void App::SetTitleBarContent(winrt::event_token const& token) noexcept { - _root.Dispatcher().RunAsync(CoreDispatcherPriority::High, [eventArgs]() { - PasteFromClipboard(eventArgs); - }); + return _root->SetTitleBarContent(token); } - // Method Description: - // - Handles the special case of providing a text override for the UI shortcut due to VK_OEM issue. - // Looks at the flags from the KeyChord modifiers and provides a concatenated string value of all - // in the same order that XAML would put them as well. - // Return Value: - // - a string representation of the key modifiers for the shortcut - //NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then - static std::wstring _FormatOverrideShortcutText(Settings::KeyModifiers modifiers) + winrt::event_token App::TitleChanged(Windows::Foundation::TypedEventHandler const& handler) { - std::wstring buffer{ L"" }; - - if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl)) - { - buffer += L"Ctrl+"; - } - if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift)) - { - buffer += L"Shift+"; - } - if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt)) - { - buffer += L"Alt+"; - } - - return buffer; + return _root->TitleChanged(handler); } - - // Method Description: - // - Takes a MenuFlyoutItem and a corresponding KeyChord value and creates the accelerator for UI display. - // Takes into account a special case for an error condition for a comma - // Arguments: - // - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator - void App::_SetAcceleratorForMenuItem(Controls::MenuFlyoutItem& menuItem, - const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord) + void App::TitleChanged(winrt::event_token const& token) noexcept { -#ifdef DEP_MICROSOFT_UI_XAML_708_FIXED - // work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA - if (keyChord.Vkey() != VK_OEM_COMMA) - { - // use the XAML shortcut to give us the automatic capabilities - auto menuShortcut = Windows::UI::Xaml::Input::KeyboardAccelerator{}; - - // TODO: Modify this when https://github.com/microsoft/terminal/issues/877 is resolved - menuShortcut.Key(static_cast(keyChord.Vkey())); - - // inspect the modifiers from the KeyChord and set the flags int he XAML value - auto modifiers = AppKeyBindings::ConvertVKModifiers(keyChord.Modifiers()); - - // add the modifiers to the shortcut - menuShortcut.Modifiers(modifiers); - - // add to the menu - menuItem.KeyboardAccelerators().Append(menuShortcut); - } - else // we've got a comma, so need to just use the alternate method -#endif - { - // extract the modifier and key to a nice format - auto overrideString = _FormatOverrideShortcutText(keyChord.Modifiers()); - auto mappedCh = MapVirtualKeyW(keyChord.Vkey(), MAPVK_VK_TO_CHAR); - if (mappedCh != 0) - { - menuItem.KeyboardAcceleratorTextOverride(overrideString + gsl::narrow_cast(mappedCh)); - } - } + return _root->TitleChanged(token); } - // Method Description: - // - Creates a new connection based on the profile settings - // Arguments: - // - the profile GUID we want the settings from - // - the terminal settings - // Return value: - // - the desired connection - TerminalConnection::ITerminalConnection App::_CreateConnectionFromSettings(GUID profileGuid, - winrt::Microsoft::Terminal::Settings::TerminalSettings settings) + winrt::event_token App::LastTabClosed(Windows::Foundation::TypedEventHandler const& handler) { - const auto* const profile = _settings->FindProfile(profileGuid); - TerminalConnection::ITerminalConnection connection{ nullptr }; - - GUID connectionType{ 0 }; - if (profile->HasConnectionType()) - { - connectionType = profile->GetConnectionType(); - } - - if (profile->HasConnectionType() && - profile->GetConnectionType() == AzureConnectionType && - TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) - { - connection = TerminalConnection::AzureConnection(settings.InitialRows(), - settings.InitialCols()); - } - else - { - connection = TerminalConnection::ConhostConnection(settings.Commandline(), - settings.StartingDirectory(), - settings.StartingTitle(), - settings.InitialRows(), - settings.InitialCols(), - winrt::guid()); - } - - TraceLoggingWrite( - g_hTerminalAppProvider, - "ConnectionCreated", - TraceLoggingDescription("Event emitted upon the creation of a connection"), - TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"), - TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), - TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); - - return connection; + return _root->LastTabClosed(handler); } - - // Method Description: - // - Used to tell the app that the titlebar has been clicked. The App won't - // actually recieve any clicks in the titlebar area, so this is a helper - // to clue the app in that a click has happened. The App will use this as - // a indicator that it needs to dismiss any open flyouts. - // Arguments: - // - - // Return Value: - // - - void App::TitlebarClicked() + void App::LastTabClosed(winrt::event_token const& token) noexcept { - if (_newTabButton && _newTabButton.Flyout()) - { - _newTabButton.Flyout().Hide(); - } + return _root->LastTabClosed(token); } // -------------------------------- WinRT Events --------------------------------- // Winrt events need a method for adding a callback to the event and removing the callback. // These macros will define them both for you. - DEFINE_EVENT(App, TitleChanged, _titleChangeHandlers, TerminalControl::TitleChangedEventArgs); - DEFINE_EVENT(App, LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs); - DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, UIElement); - DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, ElementTheme); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(App, RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme); } diff --git a/src/cascadia/TerminalApp/App.h b/src/cascadia/TerminalApp/App.h index 47350c76056..8d901b756e4 100644 --- a/src/cascadia/TerminalApp/App.h +++ b/src/cascadia/TerminalApp/App.h @@ -5,9 +5,9 @@ #include "Tab.h" #include "CascadiaSettings.h" +#include "TerminalPage.h" #include "App.g.h" #include "App.base.h" -#include "ScopedResourceLoader.h" #include "../../cascadia/inc/cppwinrt_utils.h" #include @@ -25,8 +25,7 @@ namespace winrt::TerminalApp::implementation { public: App(); - - Windows::UI::Xaml::UIElement GetRoot() noexcept; + ~App() = default; void Create(); void LoadSettings(); @@ -34,15 +33,15 @@ namespace winrt::TerminalApp::implementation Windows::Foundation::Point GetLaunchDimensions(uint32_t dpi); bool GetShowTabsInTitlebar(); - ~App() = default; + Windows::UI::Xaml::UIElement GetRoot() noexcept; - hstring GetTitle(); + hstring Title(); void TitlebarClicked(); // -------------------------------- WinRT Events --------------------------------- - DECLARE_EVENT(TitleChanged, _titleChangeHandlers, winrt::Microsoft::Terminal::TerminalControl::TitleChangedEventArgs); - DECLARE_EVENT(LastTabClosed, _lastTabClosedHandlers, winrt::TerminalApp::LastTabClosedEventArgs); - DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::UIElement); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(RequestedThemeChanged, _requestedThemeChangedHandlers, TerminalApp::App, winrt::Windows::UI::Xaml::ElementTheme); private: @@ -50,96 +49,37 @@ namespace winrt::TerminalApp::implementation // the ctor, you're going to have a bad time. It'll mysteriously fail to // activate the app. // ALSO: If you add any UIElements as roots here, make sure they're - // updated in _ApplyTheme. The two roots currently are _root and _tabRow - // (which is a root when the tabs are in the titlebar.) - Windows::UI::Xaml::Controls::Control _root{ nullptr }; - Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr }; - TerminalApp::TabRowControl _tabRow{ nullptr }; - Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr }; - Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr }; + // updated in _ApplyTheme. The root currently is _root. + winrt::com_ptr _root{ nullptr }; - std::vector> _tabs; + std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr }; - std::unique_ptr<::TerminalApp::CascadiaSettings> _settings; + std::shared_ptr _resourceLoader{ nullptr }; HRESULT _settingsLoadedResult; winrt::hstring _settingsLoadExceptionText{}; bool _loadedInitialSettings; - std::shared_mutex _dialogLock; - - ScopedResourceLoader _resourceLoader; wil::unique_folder_change_reader_nothrow _reader; - std::atomic _settingsReloadQueued{ false }; + std::shared_mutex _dialogLock; - void _CreateNewTabFlyout(); - void _OpenNewTabDropdown(); + std::atomic _settingsReloadQueued{ false }; - fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& titleElement, - const winrt::Windows::Foundation::IInspectable& contentElement, - const winrt::hstring& closeButtonText); - void _ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); - void _ShowAboutDialog(); + fire_and_forget _ShowDialog(const winrt::Windows::Foundation::IInspectable& sender, winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); + void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); void _ShowLoadWarningsDialog(); - void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); + + void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); [[nodiscard]] HRESULT _TryLoadSettings() noexcept; void _LoadSettings(); void _OpenSettings(); - - void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept; - void _RegisterSettingsChange(); fire_and_forget _DispatchReloadSettings(); void _ReloadSettings(); - void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - - void _UpdateTabView(); - void _UpdateTabIcon(std::shared_ptr tab); - void _UpdateTitle(std::shared_ptr tab); - - void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr hostingTab); - - void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings); - winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings); - - void _OpenNewTab(std::optional profileIndex); - void _DuplicateTabViewItem(); - void _CloseFocusedTab(); - void _CloseFocusedPane(); - void _SelectNextTab(const bool bMoveRight); - bool _SelectTab(const int tabIndex); - - void _SetFocusedTabIndex(int tabIndex); - int _GetFocusedTabIndex() const; - - void _Scroll(int delta); - bool _CopyText(const bool trimTrailingWhitespace); - void _PasteText(); - void _SplitVertical(const std::optional& profileGuid); - void _SplitHorizontal(const std::optional& profileGuid); - void _SplitPane(const Pane::SplitState splitType, const std::optional& profileGuid); - - // Todo: add more event implementations here - // MSFT:20641986: Add keybindings for New Window - void _ScrollPage(int delta); - void _ResizePane(const Direction& direction); - void _MoveFocus(const Direction& direction); - - void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs); - void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs); - void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs); - void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs); - void _OnContentSizeChanged(const IInspectable& sender, Windows::UI::Xaml::SizeChangedEventArgs const& e); - - void _RemoveTabViewItem(const IInspectable& tabViewItem); - void _ApplyTheme(const Windows::UI::Xaml::ElementTheme& newTheme); static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile); @@ -150,30 +90,6 @@ namespace winrt::TerminalApp::implementation void _PasteFromClipboardHandler(const IInspectable& sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs); static void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord); - -#pragma region ActionHandlers - // These are all defined in AppActionHandlers.cpp - void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); - void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); -#pragma endregion }; } diff --git a/src/cascadia/TerminalApp/App.idl b/src/cascadia/TerminalApp/App.idl index 3d954850ef7..9e8981b1d33 100644 --- a/src/cascadia/TerminalApp/App.idl +++ b/src/cascadia/TerminalApp/App.idl @@ -21,16 +21,15 @@ namespace TerminalApp Windows.UI.Xaml.UIElement GetRoot(); + String Title { get; }; + Windows.Foundation.Point GetLaunchDimensions(UInt32 dpi); Boolean GetShowTabsInTitlebar(); + void TitlebarClicked(); - event Microsoft.Terminal.TerminalControl.TitleChangedEventArgs TitleChanged; - event LastTabClosedEventArgs LastTabClosed; - event Windows.Foundation.TypedEventHandler SetTitleBarContent; + event Windows.Foundation.TypedEventHandler SetTitleBarContent; + event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler LastTabClosed; event Windows.Foundation.TypedEventHandler RequestedThemeChanged; - - String GetTitle(); - - void TitlebarClicked(); } } diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index cec06ee8f0f..1abce6291d3 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -26,112 +26,112 @@ namespace winrt namespace winrt::TerminalApp::implementation { - void App::_HandleNewTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleNewTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _OpenNewTab(std::nullopt); args.Handled(true); } - void App::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleOpenNewTabDropdown(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _OpenNewTabDropdown(); args.Handled(true); } - void App::_HandleDuplicateTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleDuplicateTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _DuplicateTabViewItem(); args.Handled(true); } - void App::_HandleCloseTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleCloseTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _CloseFocusedTab(); args.Handled(true); } - void App::_HandleClosePane(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleClosePane(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _CloseFocusedPane(); args.Handled(true); } - void App::_HandleScrollUp(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleScrollUp(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _Scroll(-1); args.Handled(true); } - void App::_HandleScrollDown(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleScrollDown(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _Scroll(1); args.Handled(true); } - void App::_HandleNextTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleNextTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _SelectNextTab(true); args.Handled(true); } - void App::_HandlePrevTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandlePrevTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _SelectNextTab(false); args.Handled(true); } - void App::_HandleSplitVertical(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleSplitVertical(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _SplitVertical(std::nullopt); args.Handled(true); } - void App::_HandleSplitHorizontal(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleSplitHorizontal(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _SplitHorizontal(std::nullopt); args.Handled(true); } - void App::_HandleScrollUpPage(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleScrollUpPage(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _ScrollPage(-1); args.Handled(true); } - void App::_HandleScrollDownPage(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleScrollDownPage(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _ScrollPage(1); args.Handled(true); } - void App::_HandleOpenSettings(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleOpenSettings(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _OpenSettings(); args.Handled(true); } - void App::_HandlePasteText(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandlePasteText(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { _PasteText(); args.Handled(true); } - void App::_HandleNewTabWithProfile(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleNewTabWithProfile(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { if (const auto& realArgs = args.ActionArgs().try_as()) { @@ -140,8 +140,8 @@ namespace winrt::TerminalApp::implementation } } - void App::_HandleSwitchToTab(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleSwitchToTab(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { if (const auto& realArgs = args.ActionArgs().try_as()) { @@ -150,8 +150,8 @@ namespace winrt::TerminalApp::implementation } } - void App::_HandleResizePane(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleResizePane(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { if (const auto& realArgs = args.ActionArgs().try_as()) { @@ -160,8 +160,8 @@ namespace winrt::TerminalApp::implementation } } - void App::_HandleMoveFocus(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleMoveFocus(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { if (const auto& realArgs = args.ActionArgs().try_as()) { @@ -170,8 +170,8 @@ namespace winrt::TerminalApp::implementation } } - void App::_HandleCopyText(const IInspectable& /*sender*/, - const TerminalApp::ActionEventArgs& args) + void TerminalPage::_HandleCopyText(const IInspectable& /*sender*/, + const TerminalApp::ActionEventArgs& args) { if (const auto& realArgs = args.ActionArgs().try_as()) { diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 5aa3a100a80..1fe835defec 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -3,16 +3,1258 @@ #include "pch.h" #include "TerminalPage.h" +#include "Utils.h" #include "TerminalPage.g.cpp" +#include using namespace winrt; -using namespace Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::System; +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::UI::Text; +using namespace winrt::Microsoft::Terminal; +using namespace winrt::Microsoft::Terminal::TerminalControl; +using namespace winrt::Microsoft::Terminal::TerminalConnection; +using namespace winrt::Microsoft::Terminal::Settings; +using namespace ::TerminalApp; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} namespace winrt::TerminalApp::implementation { - TerminalPage::TerminalPage() + TerminalPage::TerminalPage() {} + + TerminalPage::TerminalPage(std::shared_ptr resourceLoader) : + _tabs{} { InitializeComponent(); + + _resourceLoader = resourceLoader; + } + + void TerminalPage::SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI) + { + _settings = settings; + if (needRefreshUI) + { + _RefreshUIForSettingsReload(); + } + } + + void TerminalPage::Create() + { + // Hookup the key bindings + _HookupKeyBindings(_settings->GetKeybindings()); + + _tabContent = this->TabContent(); + _tabRow = this->TabRow(); + _tabView = _tabRow.TabView(); + _newTabButton = _tabRow.NewTabButton(); + + if (_settings->GlobalSettings().GetShowTabsInTitlebar()) + { + // Remove the TabView from the page. We'll hang on to it, we need to + // put it in the titlebar. + uint32_t index = 0; + if (this->Root().Children().IndexOf(_tabRow, index)) + { + this->Root().Children().RemoveAt(index); + } + + // Inform the host that our titlebar content has changed. + _setTitleBarContentHandlers(*this, _tabRow); + } + + //Event Bindings (Early) + _newTabButton.Click([this](auto&&, auto&&) { + this->_OpenNewTab(std::nullopt); + }); + _tabView.SelectionChanged({ this, &TerminalPage::_OnTabSelectionChanged }); + _tabView.Items().VectorChanged({ this, &TerminalPage::_OnTabItemsChanged }); + _tabView.TabClosing({ this, &TerminalPage::_OnTabClosing }); + + _CreateNewTabFlyout(); + _OpenNewTab(std::nullopt); + + _tabContent.SizeChanged({ this, &TerminalPage::_OnContentSizeChanged }); + } + + // Method Description: + // - Show a ContentDialog with a single "Ok" button to dismiss. Looks up the + // the title and text from our Resources using the provided keys. + // - Only one dialog can be visible at a time. If another dialog is visible + // when this is called, nothing happens. See _ShowDialog for details + // Arguments: + // - titleKey: The key to use to lookup the title text from our resources. + // - contentKey: The key to use to lookup the content text from our resources. + void TerminalPage::ShowOkDialog(const winrt::hstring& titleKey, + const winrt::hstring& contentKey) + { + auto title = _resourceLoader->GetLocalizedString(titleKey); + auto message = _resourceLoader->GetLocalizedString(contentKey); + auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(winrt::box_value(message)); + dialog.CloseButtonText(buttonText); + + _showDialogHandlers(*this, dialog); + } + + // Method Description: + // - Show a dialog with "About" information. Displays the app's Display + // Name, version, getting started link, documentation link, and release + // Notes link. + void TerminalPage::_ShowAboutDialog() + { + const auto title = _resourceLoader->GetLocalizedString(L"AboutTitleText"); + const auto versionLabel = _resourceLoader->GetLocalizedString(L"VersionLabelText"); + const auto gettingStartedLabel = _resourceLoader->GetLocalizedString(L"GettingStartedLabelText"); + const auto documentationLabel = _resourceLoader->GetLocalizedString(L"DocumentationLabelText"); + const auto releaseNotesLabel = _resourceLoader->GetLocalizedString(L"ReleaseNotesLabelText"); + const auto gettingStartedUriValue = _resourceLoader->GetLocalizedString(L"GettingStartedUriValue"); + const auto documentationUriValue = _resourceLoader->GetLocalizedString(L"DocumentationUriValue"); + const auto releaseNotesUriValue = _resourceLoader->GetLocalizedString(L"ReleaseNotesUriValue"); + const auto package = winrt::Windows::ApplicationModel::Package::Current(); + const auto packageName = package.DisplayName(); + const auto version = package.Id().Version(); + winrt::Windows::UI::Xaml::Documents::Run about; + winrt::Windows::UI::Xaml::Documents::Run gettingStarted; + winrt::Windows::UI::Xaml::Documents::Run documentation; + winrt::Windows::UI::Xaml::Documents::Run releaseNotes; + winrt::Windows::UI::Xaml::Documents::Hyperlink gettingStartedLink; + winrt::Windows::UI::Xaml::Documents::Hyperlink documentationLink; + winrt::Windows::UI::Xaml::Documents::Hyperlink releaseNotesLink; + std::wstringstream aboutTextStream; + + gettingStarted.Text(gettingStartedLabel); + documentation.Text(documentationLabel); + releaseNotes.Text(releaseNotesLabel); + + winrt::Windows::Foundation::Uri gettingStartedUri{ gettingStartedUriValue }; + winrt::Windows::Foundation::Uri documentationUri{ documentationUriValue }; + winrt::Windows::Foundation::Uri releaseNotesUri{ releaseNotesUriValue }; + + gettingStartedLink.NavigateUri(gettingStartedUri); + documentationLink.NavigateUri(documentationUri); + releaseNotesLink.NavigateUri(releaseNotesUri); + + gettingStartedLink.Inlines().Append(gettingStarted); + documentationLink.Inlines().Append(documentation); + releaseNotesLink.Inlines().Append(releaseNotes); + + // Format our about text. It will look like the following: + // + // Version: ... + // Getting Started + // Documentation + // Release Notes + + aboutTextStream << packageName.c_str() << L"\n"; + + aboutTextStream << versionLabel.c_str() << L" "; + aboutTextStream << version.Major << L"." << version.Minor << L"." << version.Build << L"." << version.Revision << L"\n"; + + winrt::hstring aboutText{ aboutTextStream.str() }; + about.Text(aboutText); + + const auto buttonText = _resourceLoader->GetLocalizedString(L"Ok"); + + Controls::TextBlock aboutTextBlock; + aboutTextBlock.Inlines().Append(about); + aboutTextBlock.Inlines().Append(gettingStartedLink); + aboutTextBlock.Inlines().Append(documentationLink); + aboutTextBlock.Inlines().Append(releaseNotesLink); + aboutTextBlock.IsTextSelectionEnabled(true); + + Controls::ContentDialog dialog; + dialog.Title(winrt::box_value(title)); + dialog.Content(aboutTextBlock); + dialog.CloseButtonText(buttonText); + + _showDialogHandlers(*this, dialog); + } + + // Method Description: + // - Builds the flyout (dropdown) attached to the new tab button, and + // attaches it to the button. Populates the flyout with one entry per + // Profile, displaying the profile's name. Clicking each flyout item will + // open a new tab with that profile. + // Below the profiles are the static menu items: settings, feedback + void TerminalPage::_CreateNewTabFlyout() + { + auto newTabFlyout = Controls::MenuFlyout{}; + auto keyBindings = _settings->GetKeybindings(); + + const GUID defaultProfileGuid = _settings->GlobalSettings().GetDefaultProfile(); + // the number of profiles should not change in the loop for this to work + auto const profileCount = gsl::narrow_cast(_settings->GetProfiles().size()); + for (int profileIndex = 0; profileIndex < profileCount; profileIndex++) + { + const auto& profile = _settings->GetProfiles()[profileIndex]; + auto profileMenuItem = Controls::MenuFlyoutItem{}; + + // add the keyboard shortcuts for the first 9 profiles + if (profileIndex < 9) + { + // enum value for ShortcutAction::NewTabProfileX; 0==NewTabProfile0 + const auto action = static_cast(profileIndex + static_cast(ShortcutAction::NewTabProfile0)); + auto profileKeyChord = keyBindings.GetKeyBinding(action); + + // make sure we find one to display + if (profileKeyChord) + { + _SetAcceleratorForMenuItem(profileMenuItem, profileKeyChord); + } + } + + auto profileName = profile.GetName(); + winrt::hstring hName{ profileName }; + profileMenuItem.Text(hName); + + // If there's an icon set for this profile, set it as the icon for + // this flyout item. + if (profile.HasIcon()) + { + profileMenuItem.Icon(_GetIconFromProfile(profile)); + } + + if (profile.GetGuid() == defaultProfileGuid) + { + // Contrast the default profile with others in font weight. + profileMenuItem.FontWeight(FontWeights::Bold()); + } + + profileMenuItem.Click([this, profileIndex](auto&&, auto&&) { + this->_OpenNewTab({ profileIndex }); + }); + newTabFlyout.Items().Append(profileMenuItem); + } + + // add menu separator + auto separatorItem = Controls::MenuFlyoutSeparator{}; + newTabFlyout.Items().Append(separatorItem); + + // add static items + { + // Create the settings button. + auto settingsItem = Controls::MenuFlyoutItem{}; + settingsItem.Text(_resourceLoader->GetLocalizedString(L"SettingsMenuItem")); + + Controls::SymbolIcon ico{}; + ico.Symbol(Controls::Symbol::Setting); + settingsItem.Icon(ico); + + settingsItem.Click({ this, &TerminalPage::_SettingsButtonOnClick }); + newTabFlyout.Items().Append(settingsItem); + + auto settingsKeyChord = keyBindings.GetKeyBinding(ShortcutAction::OpenSettings); + if (settingsKeyChord) + { + _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord); + } + + // Create the feedback button. + auto feedbackFlyout = Controls::MenuFlyoutItem{}; + feedbackFlyout.Text(_resourceLoader->GetLocalizedString(L"FeedbackMenuItem")); + + Controls::FontIcon feedbackIco{}; + feedbackIco.Glyph(L"\xE939"); + feedbackIco.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + feedbackFlyout.Icon(feedbackIco); + + feedbackFlyout.Click({ this, &TerminalPage::_FeedbackButtonOnClick }); + newTabFlyout.Items().Append(feedbackFlyout); + + // Create the about button. + auto aboutFlyout = Controls::MenuFlyoutItem{}; + aboutFlyout.Text(_resourceLoader->GetLocalizedString(L"AboutMenuItem")); + + Controls::SymbolIcon aboutIco{}; + aboutIco.Symbol(Controls::Symbol::Help); + aboutFlyout.Icon(aboutIco); + + aboutFlyout.Click({ this, &TerminalPage::_AboutButtonOnClick }); + newTabFlyout.Items().Append(aboutFlyout); + } + + _newTabButton.Flyout(newTabFlyout); + } + + // Function Description: + // Called when the openNewTabDropdown keybinding is used. + // Adds the flyout show option to left-align the dropdown with the split button. + // Shows the dropdown flyout. + void TerminalPage::_OpenNewTabDropdown() + { + Controls::Primitives::FlyoutShowOptions options{}; + options.Placement(Controls::Primitives::FlyoutPlacementMode::BottomEdgeAlignedLeft); + _newTabButton.Flyout().ShowAt(_newTabButton, options); + } + + // Method Description: + // - Open a new tab. This will create the TerminalControl hosting the + // terminal, and add a new Tab to our list of tabs. The method can + // optionally be provided a profile index, which will be used to create + // a tab using the profile in that index. + // If no index is provided, the default profile will be used. + // Arguments: + // - profileIndex: an optional index into the list of profiles to use to + // initialize this tab up with. + void TerminalPage::_OpenNewTab(std::optional profileIndex) + { + GUID profileGuid; + + if (profileIndex) + { + const auto realIndex = profileIndex.value(); + const auto profiles = _settings->GetProfiles(); + + // If we don't have that many profiles, then do nothing. + if (realIndex >= gsl::narrow(profiles.size())) + { + return; + } + + const auto& selectedProfile = profiles[realIndex]; + profileGuid = selectedProfile.GetGuid(); + } + else + { + // Getting Guid for default profile + const auto globalSettings = _settings->GlobalSettings(); + profileGuid = globalSettings.GetDefaultProfile(); + } + + TerminalSettings settings = _settings->MakeSettings(profileGuid); + _CreateNewTabFromSettings(profileGuid, settings); + + const int tabCount = static_cast(_tabs.size()); + TraceLoggingWrite( + g_hTerminalAppProvider, // handle to TerminalApp tracelogging provider + "TabInformation", + TraceLoggingDescription("Event emitted upon new tab creation in TerminalApp"), + TraceLoggingInt32(tabCount, "TabCount", "Count of tabs curently opened in TerminalApp"), + TraceLoggingBool(profileIndex.has_value(), "ProfileSpecified", "Whether the new tab specified a profile explicitly"), + TraceLoggingGuid(profileGuid, "ProfileGuid", "The GUID of the profile spawned in the new tab"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + } + + // Method Description: + // - Creates a new tab with the given settings. If the tab bar is not being + // currently displayed, it will be shown. + // Arguments: + // - settings: the TerminalSettings object to use to create the TerminalControl with. + void TerminalPage::_CreateNewTabFromSettings(GUID profileGuid, TerminalSettings settings) + { + // Initialize the new tab + + // Create a connection based on the values in our settings object. + const auto connection = _CreateConnectionFromSettings(profileGuid, settings); + + TermControl term{ settings, connection }; + + // Add the new tab to the list of our tabs. + auto newTab = _tabs.emplace_back(std::make_shared(profileGuid, term)); + + const auto* const profile = _settings->FindProfile(profileGuid); + + // Hookup our event handlers to the new terminal + _RegisterTerminalEvents(term, newTab); + + auto tabViewItem = newTab->GetTabViewItem(); + _tabView.Items().Append(tabViewItem); + + // Set this profile's tab to the icon the user specified + if (profile != nullptr && profile->HasIcon()) + { + newTab->UpdateIcon(profile->GetExpandedIconPath()); + } + + tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick }); + + // When the tab is closed, remove it from our list of tabs. + newTab->Closed([tabViewItem, this]() { + _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tabViewItem, this]() { + _RemoveTabViewItem(tabViewItem); + }); + }); + + // This is one way to set the tab's selected background color. + // tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), a Brush?); + + // This kicks off TabView::SelectionChanged, in response to which we'll attach the terminal's + // Xaml control to the Xaml root. + _tabView.SelectedItem(tabViewItem); + } + + // Method Description: + // - Creates a new connection based on the profile settings + // Arguments: + // - the profile GUID we want the settings from + // - the terminal settings + // Return value: + // - the desired connection + TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(GUID profileGuid, + winrt::Microsoft::Terminal::Settings::TerminalSettings settings) + { + const auto* const profile = _settings->FindProfile(profileGuid); + + TerminalConnection::ITerminalConnection connection{ nullptr }; + + GUID connectionType{ 0 }; + + if (profile->HasConnectionType()) + { + connectionType = profile->GetConnectionType(); + } + + if (profile->HasConnectionType() && + profile->GetConnectionType() == AzureConnectionType && + TerminalConnection::AzureConnection::IsAzureConnectionAvailable()) + { + connection = TerminalConnection::AzureConnection(settings.InitialRows(), + settings.InitialCols()); + } + + else + { + connection = TerminalConnection::ConhostConnection(settings.Commandline(), + settings.StartingDirectory(), + settings.StartingTitle(), + settings.InitialRows(), + settings.InitialCols(), + winrt::guid()); + } + + TraceLoggingWrite( + g_hTerminalAppProvider, + "ConnectionCreated", + TraceLoggingDescription("Event emitted upon the creation of a connection"), + TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + + return connection; + } + + // Method Description: + // - Called when the settings button is clicked. Launches a background + // thread to open the settings file in the default JSON editor. + // Arguments: + // - + // Return Value: + // - + void TerminalPage::_SettingsButtonOnClick(const IInspectable&, + const RoutedEventArgs&) + { + LaunchSettings(); + } + + // Method Description: + // - Called when the feedback button is clicked. Launches github in your + // default browser, navigated to the "issues" page of the Terminal repo. + void TerminalPage::_FeedbackButtonOnClick(const IInspectable&, + const RoutedEventArgs&) + { + const auto feedbackUriValue = _resourceLoader->GetLocalizedString(L"FeedbackUriValue"); + + winrt::Windows::System::Launcher::LaunchUriAsync({ feedbackUriValue }); + } + + // Method Description: + // - Called when the about button is clicked. See _ShowAboutDialog for more info. + // Arguments: + // - + // Return Value: + // - + void TerminalPage::_AboutButtonOnClick(const IInspectable&, + const RoutedEventArgs&) + { + _ShowAboutDialog(); + } + + // Method Description: + // - Register our event handlers with the given keybindings object. This + // should be done regardless of what the events are actually bound to - + // this simply ensures the AppKeyBindings object will call us correctly + // for each event. + // Arguments: + // - bindings: A AppKeyBindings object to wire up with our event handlers + void TerminalPage::_HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept + { + // Hook up the KeyBinding object's events to our handlers. + // They should all be hooked up here, regardless of whether or not + // there's an actual keychord for them. + + bindings.NewTab({ this, &TerminalPage::_HandleNewTab }); + bindings.OpenNewTabDropdown({ this, &TerminalPage::_HandleOpenNewTabDropdown }); + bindings.DuplicateTab({ this, &TerminalPage::_HandleDuplicateTab }); + bindings.CloseTab({ this, &TerminalPage::_HandleCloseTab }); + bindings.ClosePane({ this, &TerminalPage::_HandleClosePane }); + bindings.ScrollUp({ this, &TerminalPage::_HandleScrollUp }); + bindings.ScrollDown({ this, &TerminalPage::_HandleScrollDown }); + bindings.NextTab({ this, &TerminalPage::_HandleNextTab }); + bindings.PrevTab({ this, &TerminalPage::_HandlePrevTab }); + bindings.SplitVertical({ this, &TerminalPage::_HandleSplitVertical }); + bindings.SplitHorizontal({ this, &TerminalPage::_HandleSplitHorizontal }); + bindings.ScrollUpPage({ this, &TerminalPage::_HandleScrollUpPage }); + bindings.ScrollDownPage({ this, &TerminalPage::_HandleScrollDownPage }); + bindings.OpenSettings({ this, &TerminalPage::_HandleOpenSettings }); + bindings.PasteText({ this, &TerminalPage::_HandlePasteText }); + bindings.NewTabWithProfile({ this, &TerminalPage::_HandleNewTabWithProfile }); + bindings.SwitchToTab({ this, &TerminalPage::_HandleSwitchToTab }); + bindings.ResizePane({ this, &TerminalPage::_HandleResizePane }); + bindings.MoveFocus({ this, &TerminalPage::_HandleMoveFocus }); + bindings.CopyText({ this, &TerminalPage::_HandleCopyText }); + } + + // Method Description: + // - Get the title of the currently focused terminal control, and set it's + // tab's text to that text. If this tab is the focused tab, then also + // bubble this title to any listeners of our TitleChanged event. + // Arguments: + // - tab: the Tab to update the title for. + void TerminalPage::_UpdateTitle(std::shared_ptr tab) + { + auto newTabTitle = tab->GetFocusedTitle(); + tab->SetTabText(newTabTitle); + + if (_settings->GlobalSettings().GetShowTitleInTitlebar() && + tab->IsFocused()) + { + _titleChangeHandlers(*this, newTabTitle); + } + } + + // Method Description: + // - Get the icon of the currently focused terminal control, and set its + // tab's icon to that icon. + // Arguments: + // - tab: the Tab to update the title for. + void TerminalPage::_UpdateTabIcon(std::shared_ptr tab) + { + const auto lastFocusedProfileOpt = tab->GetFocusedProfile(); + if (lastFocusedProfileOpt.has_value()) + { + const auto lastFocusedProfile = lastFocusedProfileOpt.value(); + const auto* const matchingProfile = _settings->FindProfile(lastFocusedProfile); + if (matchingProfile) + { + tab->UpdateIcon(matchingProfile->GetExpandedIconPath()); + } + else + { + tab->UpdateIcon({}); + } + } + } + + // Method Description: + // - Handle changes in tab layout. + void TerminalPage::_UpdateTabView() + { + // Show tabs when there's more than 1, or the user has chosen to always + // show the tab bar. + const bool isVisible = _settings->GlobalSettings().GetShowTabsInTitlebar() || + (_tabs.size() > 1) || + _settings->GlobalSettings().GetAlwaysShowTabs(); + + // collapse/show the tabs themselves + _tabView.Visibility(isVisible ? Visibility::Visible : Visibility::Collapsed); + + // collapse/show the row that the tabs are in. + // NaN is the special value XAML uses for "Auto" sizing. + _tabRow.Height(isVisible ? NAN : 0); + } + + // Method Description: + // - Duplicates the current focused tab + void TerminalPage::_DuplicateTabViewItem() + { + const int& focusedTabIndex = _GetFocusedTabIndex(); + const auto& _tab = _tabs.at(focusedTabIndex); + + const auto& profileGuid = _tab->GetFocusedProfile(); + const auto& settings = _settings->MakeSettings(profileGuid); + + _CreateNewTabFromSettings(profileGuid.value(), settings); + } + + // Method Description: + // - Removes the tab (both TerminalControl and XAML) + // Arguments: + // - tabViewItem: the TabViewItem in the TabView that is being removed. + void TerminalPage::_RemoveTabViewItem(const IInspectable& tabViewItem) + { + // To close the window here, we need to close the hosting window. + if (_tabs.size() == 1) + { + _lastTabClosedHandlers(*this, nullptr); + } + uint32_t tabIndexFromControl = 0; + _tabView.Items().IndexOf(tabViewItem, tabIndexFromControl); + auto focusedTabIndex = _GetFocusedTabIndex(); + + // Removing the tab from the collection will destroy its control and disconnect its connection. + _tabs.erase(_tabs.begin() + tabIndexFromControl); + _tabView.Items().RemoveAt(tabIndexFromControl); + + if (tabIndexFromControl == focusedTabIndex) + { + auto const tabCount = gsl::narrow_cast(_tabs.size()); + if (focusedTabIndex >= tabCount) + { + focusedTabIndex = tabCount - 1; + } + else if (focusedTabIndex < 0) + { + focusedTabIndex = 0; + } + + _SelectTab(focusedTabIndex); + } + } + + // Method Description: + // - Connects event handlers to the TermControl for events that we want to + // handle. This includes: + // * the Copy and Paste events, for setting and retrieving clipboard data + // on the right thread + // * the TitleChanged event, for changing the text of the tab + // * the GotFocus event, for changing the title/icon in the tab when a new + // control is focused + // Arguments: + // - term: The newly created TermControl to connect the events for + // - hostingTab: The Tab that's hosting this TermControl instance + void TerminalPage::_RegisterTerminalEvents(TermControl term, std::shared_ptr hostingTab) + { + // Add an event handler when the terminal's selection wants to be copied. + // When the text buffer data is retrieved, we'll copy the data into the Clipboard + term.CopyToClipboard({ this, &TerminalPage::_CopyToClipboardHandler }); + + // Add an event handler when the terminal wants to paste data from the Clipboard. + term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); + + // Don't capture a strong ref to the tab. If the tab is removed as this + // is called, we don't really care anymore about handling the event. + std::weak_ptr weakTabPtr = hostingTab; + term.TitleChanged([this, weakTabPtr](auto newTitle) { + auto tab = weakTabPtr.lock(); + if (!tab) + { + return; + } + // The title of the control changed, but not necessarily the title + // of the tab. Get the title of the focused pane of the tab, and set + // the tab's text to the focused panes' text. + _UpdateTitle(tab); + }); + + term.GotFocus([this, weakTabPtr](auto&&, auto&&) { + auto tab = weakTabPtr.lock(); + if (!tab) + { + return; + } + // Update the focus of the tab's panes + tab->UpdateFocus(); + + // Possibly update the title of the tab, window to match the newly + // focused pane. + _UpdateTitle(tab); + + // Possibly update the icon of the tab. + _UpdateTabIcon(tab); + }); + } + + // Method Description: + // - Sets focus to the tab to the right or left the currently selected tab. + void TerminalPage::_SelectNextTab(const bool bMoveRight) + { + int focusedTabIndex = _GetFocusedTabIndex(); + auto tabCount = _tabs.size(); + // Wraparound math. By adding tabCount and then calculating modulo tabCount, + // we clamp the values to the range [0, tabCount) while still supporting moving + // leftward from 0 to tabCount - 1. + _SetFocusedTabIndex( + static_cast((tabCount + focusedTabIndex + (bMoveRight ? 1 : -1)) % tabCount)); + } + + // Method Description: + // - Sets focus to the desired tab. Returns false if the provided tabIndex + // is greater than the number of tabs we have. + // Return Value: + // true iff we were able to select that tab index, false otherwise + bool TerminalPage::_SelectTab(const int tabIndex) + { + if (tabIndex >= 0 && tabIndex < gsl::narrow_cast(_tabs.size())) + { + _SetFocusedTabIndex(tabIndex); + return true; + } + return false; + } + + // Method Description: + // - Attempt to move focus between panes, as to focus the child on + // the other side of the separator. See Pane::NavigateFocus for details. + // - Moves the focus of the currently focused tab. + // Arguments: + // - direction: The direction to move the focus in. + // Return Value: + // - + void TerminalPage::_MoveFocus(const Direction& direction) + { + const auto focusedTabIndex = _GetFocusedTabIndex(); + _tabs[focusedTabIndex]->NavigateFocus(direction); + } + + winrt::Microsoft::Terminal::TerminalControl::TermControl TerminalPage::_GetFocusedControl() + { + int focusedTabIndex = _GetFocusedTabIndex(); + auto focusedTab = _tabs[focusedTabIndex]; + return focusedTab->GetFocusedTerminalControl(); + } + + // Method Description: + // - Returns the index in our list of tabs of the currently focused tab. If + // no tab is currently selected, returns -1. + // Return Value: + // - the index of the currently focused tab if there is one, else -1 + int TerminalPage::_GetFocusedTabIndex() const + { + // GH#1117: This is a workaround because _tabView.SelectedIndex() + // sometimes return incorrect result after removing some tabs + uint32_t focusedIndex; + if (_tabView.Items().IndexOf(_tabView.SelectedItem(), focusedIndex)) + { + return focusedIndex; + } + return -1; + } + + void TerminalPage::_SetFocusedTabIndex(int tabIndex) + { + // GH#1117: This is a workaround because _tabView.SelectedIndex(tabIndex) + // sometimes set focus to an incorrect tab after removing some tabs + auto tab = _tabs.at(tabIndex); + _tabView.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [tab, this]() { + auto tabViewItem = tab->GetTabViewItem(); + _tabView.SelectedItem(tabViewItem); + }); + } + + // Method Description: + // - Close the currently focused tab. Focus will move to the left, if possible. + void TerminalPage::_CloseFocusedTab() + { + int focusedTabIndex = _GetFocusedTabIndex(); + std::shared_ptr focusedTab{ _tabs[focusedTabIndex] }; + _RemoveTabViewItem(focusedTab->GetTabViewItem()); + } + + // Method Description: + // - Close the currently focused pane. If the pane is the last pane in the + // tab, the tab will also be closed. This will happen when we handle the + // tab's Closed event. + void TerminalPage::_CloseFocusedPane() + { + int focusedTabIndex = _GetFocusedTabIndex(); + std::shared_ptr focusedTab{ _tabs[focusedTabIndex] }; + focusedTab->ClosePane(); + } + + // Method Description: + // - Move the viewport of the terminal of the currently focused tab up or + // down a number of lines. Negative values of `delta` will move the + // view up, and positive values will move the viewport down. + // Arguments: + // - delta: a number of lines to move the viewport relative to the current viewport. + void TerminalPage::_Scroll(int delta) + { + int focusedTabIndex = _GetFocusedTabIndex(); + _tabs[focusedTabIndex]->Scroll(delta); + } + + // Method Description: + // - Vertically split the focused pane, and place the given TermControl into + // the newly created pane. + // Arguments: + // - profile: The profile GUID to associate with the newly created pane. If + // this is nullopt, use the default profile. + void TerminalPage::_SplitVertical(const std::optional& profileGuid) + { + _SplitPane(Pane::SplitState::Vertical, profileGuid); + } + + // Method Description: + // - Horizontally split the focused pane and place the given TermControl + // into the newly created pane. + // Arguments: + // - profile: The profile GUID to associate with the newly created pane. If + // this is nullopt, use the default profile. + void TerminalPage::_SplitHorizontal(const std::optional& profileGuid) + { + _SplitPane(Pane::SplitState::Horizontal, profileGuid); + } + + // Method Description: + // - Split the focused pane either horizontally or vertically, and place the + // given TermControl into the newly created pane. + // - If splitType == SplitState::None, this method does nothing. + // Arguments: + // - splitType: one value from the Pane::SplitState enum, indicating how the + // new pane should be split from its parent. + // - profile: The profile GUID to associate with the newly created pane. If + // this is nullopt, use the default profile. + void TerminalPage::_SplitPane(const Pane::SplitState splitType, const std::optional& profileGuid) + { + // Do nothing if we're requesting no split. + if (splitType == Pane::SplitState::None) + { + return; + } + + const auto realGuid = profileGuid ? profileGuid.value() : + _settings->GlobalSettings().GetDefaultProfile(); + const auto controlSettings = _settings->MakeSettings(realGuid); + + const auto controlConnection = _CreateConnectionFromSettings(realGuid, controlSettings); + + const int focusedTabIndex = _GetFocusedTabIndex(); + auto focusedTab = _tabs[focusedTabIndex]; + + const auto canSplit = focusedTab->CanSplitPane(splitType); + + if (!canSplit) + { + return; + } + + TermControl newControl{ controlSettings, controlConnection }; + + // Hookup our event handlers to the new terminal + _RegisterTerminalEvents(newControl, focusedTab); + + focusedTab->SplitPane(splitType, realGuid, newControl); + } + + // Method Description: + // - Attempt to move a separator between panes, as to resize each child on + // either size of the separator. See Pane::ResizePane for details. + // - Moves a separator on the currently focused tab. + // Arguments: + // - direction: The direction to move the separator in. + // Return Value: + // - + void TerminalPage::_ResizePane(const Direction& direction) + { + const auto focusedTabIndex = _GetFocusedTabIndex(); + _tabs[focusedTabIndex]->ResizePane(direction); + } + + // Method Description: + // - Move the viewport of the terminal of the currently focused tab up or + // down a page. The page length will be dependent on the terminal view height. + // Negative values of `delta` will move the view up by one page, and positive values + // will move the viewport down by one page. + // Arguments: + // - delta: The direction to move the view relative to the current viewport(it + // is clamped between -1 and 1) + void TerminalPage::_ScrollPage(int delta) + { + delta = std::clamp(delta, -1, 1); + const auto focusedTabIndex = _GetFocusedTabIndex(); + const auto control = _GetFocusedControl(); + const auto termHeight = control.GetViewHeight(); + _tabs[focusedTabIndex]->Scroll(termHeight * delta); + } + + // Method Description: + // - Gets a colored IconElement for the profile in question. If the profile + // has an `icon` set in the settings, this will return an icon with that + // image in it. Otherwise it will return a nullptr-initialized + // IconElement. + // Arguments: + // - profile: the profile to get the icon from + // Return Value: + // - an IconElement for the profile's icon, if it has one. + Controls::IconElement TerminalPage::_GetIconFromProfile(const Profile& profile) + { + return profile.HasIcon() ? GetColoredIcon(profile.GetExpandedIconPath()) : Controls::IconElement{ nullptr }; + } + + // Method Description: + // - Gets the title of the currently focused terminal control. If there + // isn't a control selected for any reason, returns "Windows Terminal" + // Arguments: + // - + // Return Value: + // - the title of the focused control if there is one, else "Windows Terminal" + hstring TerminalPage::Title() + { + if (_settings->GlobalSettings().GetShowTitleInTitlebar()) + { + auto selectedIndex = _tabView.SelectedIndex(); + if (selectedIndex >= 0) + { + try + { + if (auto focusedControl{ _GetFocusedControl() }) + { + return focusedControl.Title(); + } + } + CATCH_LOG(); + } + } + return { L"Windows Terminal" }; + } + + // Method Description: + // - Handles the special case of providing a text override for the UI shortcut due to VK_OEM issue. + // Looks at the flags from the KeyChord modifiers and provides a concatenated string value of all + // in the same order that XAML would put them as well. + // Return Value: + // - a string representation of the key modifiers for the shortcut + //NOTE: This needs to be localized with https://github.com/microsoft/terminal/issues/794 if XAML framework issue not resolved before then + static std::wstring _FormatOverrideShortcutText(Settings::KeyModifiers modifiers) + { + std::wstring buffer{ L"" }; + + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Ctrl)) + { + buffer += L"Ctrl+"; + } + + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Shift)) + { + buffer += L"Shift+"; + } + + if (WI_IsFlagSet(modifiers, Settings::KeyModifiers::Alt)) + { + buffer += L"Alt+"; + } + + return buffer; + } + + // Method Description: + // - Takes a MenuFlyoutItem and a corresponding KeyChord value and creates the accelerator for UI display. + // Takes into account a special case for an error condition for a comma + // Arguments: + // - MenuFlyoutItem that will be displayed, and a KeyChord to map an accelerator + void TerminalPage::_SetAcceleratorForMenuItem(Controls::MenuFlyoutItem& menuItem, + const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord) + { +#ifdef DEP_MICROSOFT_UI_XAML_708_FIXED + // work around https://github.com/microsoft/microsoft-ui-xaml/issues/708 in case of VK_OEM_COMMA + if (keyChord.Vkey() != VK_OEM_COMMA) + { + // use the XAML shortcut to give us the automatic capabilities + auto menuShortcut = Windows::UI::Xaml::Input::KeyboardAccelerator{}; + + // TODO: Modify this when https://github.com/microsoft/terminal/issues/877 is resolved + menuShortcut.Key(static_cast(keyChord.Vkey())); + + // inspect the modifiers from the KeyChord and set the flags int he XAML value + auto modifiers = AppKeyBindings::ConvertVKModifiers(keyChord.Modifiers()); + + // add the modifiers to the shortcut + menuShortcut.Modifiers(modifiers); + + // add to the menu + menuItem.KeyboardAccelerators().Append(menuShortcut); + } + else // we've got a comma, so need to just use the alternate method +#endif + { + // extract the modifier and key to a nice format + auto overrideString = _FormatOverrideShortcutText(keyChord.Modifiers()); + auto mappedCh = MapVirtualKeyW(keyChord.Vkey(), MAPVK_VK_TO_CHAR); + if (mappedCh != 0) + { + menuItem.KeyboardAcceleratorTextOverride(overrideString + gsl::narrow_cast(mappedCh)); + } + } + } + + // Method Description: + // - Place `copiedData` into the clipboard as text. Triggered when a + // terminal control raises it's CopyToClipboard event. + // Arguments: + // - copiedData: the new string content to place on the clipboard. + void TerminalPage::_CopyToClipboardHandler(const IInspectable& /*sender*/, + const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData) + { + this->Dispatcher().RunAsync(CoreDispatcherPriority::High, [copiedData]() { + DataPackage dataPack = DataPackage(); + dataPack.RequestedOperation(DataPackageOperation::Copy); + + // copy text to dataPack + dataPack.SetText(copiedData.Text()); + + // copy html to dataPack + const auto htmlData = copiedData.Html(); + if (!htmlData.empty()) + { + dataPack.SetHtmlFormat(htmlData); + } + + try + { + Clipboard::SetContent(dataPack); + Clipboard::Flush(); + } + CATCH_LOG(); + }); + } + + // Method Description: + // - Fires an async event to get data from the clipboard, and paste it to + // the terminal. Triggered when the Terminal Control requests clipboard + // data with it's PasteFromClipboard event. + // Arguments: + // - eventArgs: the PasteFromClipboard event sent from the TermControl + void TerminalPage::_PasteFromClipboardHandler(const IInspectable& /*sender*/, + const PasteFromClipboardEventArgs& eventArgs) + { + this->Dispatcher().RunAsync(CoreDispatcherPriority::High, [eventArgs]() { + TerminalPage::PasteFromClipboard(eventArgs); + }); } + + // Function Description: + // - Copies and processes the text data from the Windows Clipboard. + // Does some of this in a background thread, as to not hang/crash the UI thread. + // Arguments: + // - eventArgs: the PasteFromClipboard event sent from the TermControl + fire_and_forget TerminalPage::PasteFromClipboard(PasteFromClipboardEventArgs eventArgs) + { + const DataPackageView data = Clipboard::GetContent(); + + // This will switch the execution of the function to a background (not + // UI) thread. This is IMPORTANT, because the getting the clipboard data + // will crash on the UI thread, because the main thread is a STA. + co_await winrt::resume_background(); + + hstring text = L""; + if (data.Contains(StandardDataFormats::Text())) + { + text = co_await data.GetTextAsync(); + } + eventArgs.HandleClipboardData(text); + } + + // Method Description: + // - Copy text from the focused terminal to the Windows Clipboard + // Arguments: + // - trimTrailingWhitespace: enable removing any whitespace from copied selection + // and get text to appear on separate lines. + // Return Value: + // - true iff we we able to copy text (if a selection was active) + bool TerminalPage::_CopyText(const bool trimTrailingWhitespace) + { + const auto control = _GetFocusedControl(); + return control.CopySelectionToClipboard(trimTrailingWhitespace); + } + + // Method Description: + // - Paste text from the Windows Clipboard to the focused terminal + void TerminalPage::_PasteText() + { + const auto control = _GetFocusedControl(); + control.PasteTextFromClipboard(); + } + + void TerminalPage::_OpenSettings() + { + LaunchSettings(); + } + + // Function Description: + // - Called when the settings button is clicked. ShellExecutes the settings + // file, as to open it in the default editor for .json files. Does this in + // a background thread, as to not hang/crash the UI thread. + fire_and_forget TerminalPage::LaunchSettings() + { + // This will switch the execution of the function to a background (not + // UI) thread. This is IMPORTANT, because the Windows.Storage API's + // (used for retrieving the path to the file) will crash on the UI + // thread, because the main thread is a STA. + co_await winrt::resume_background(); + + const auto settingsPath = CascadiaSettings::GetSettingsPath(); + + HINSTANCE res = ShellExecute(nullptr, nullptr, settingsPath.c_str(), nullptr, nullptr, SW_SHOW); + if (static_cast(reinterpret_cast(res)) <= 32) + { + ShellExecute(nullptr, nullptr, L"notepad", settingsPath.c_str(), nullptr, SW_SHOW); + } + } + + // Method Description: + // - Responds to changes in the TabView's item list by changing the tabview's + // visibility. + // Arguments: + // - sender: the control that originated this event + // - eventArgs: the event's constituent arguments + void TerminalPage::_OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs) + { + _UpdateTabView(); + } + + // Method Description: + // - Additional responses to clicking on a TabView's item. Currently, just remove tab with middle click + // Arguments: + // - sender: the control that originated this event (TabViewItem) + // - eventArgs: the event's constituent arguments + void TerminalPage::_OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs) + { + if (eventArgs.GetCurrentPoint(*this).Properties().IsMiddleButtonPressed()) + { + _RemoveTabViewItem(sender); + eventArgs.Handled(true); + } + } + + // Method Description: + // - Responds to the TabView control's Selection Changed event (to move a + // new terminal control into focus.) + // Arguments: + // - sender: the control that originated this event + // - eventArgs: the event's constituent arguments + void TerminalPage::_OnTabSelectionChanged(const IInspectable& sender, const Controls::SelectionChangedEventArgs& eventArgs) + { + auto tabView = sender.as(); + auto selectedIndex = tabView.SelectedIndex(); + + // Unfocus all the tabs. + for (auto tab : _tabs) + { + tab->SetFocused(false); + } + + if (selectedIndex >= 0) + { + try + { + auto tab = _tabs.at(selectedIndex); + + _tabContent.Children().Clear(); + _tabContent.Children().Append(tab->GetRootElement()); + + tab->SetFocused(true); + _titleChangeHandlers(*this, Title()); + } + CATCH_LOG(); + } + } + + // Method Description: + // - Called when our tab content size changes. This updates each tab with + // the new size, so they have a chance to update each of their panes with + // the new size. + // Arguments: + // - e: the SizeChangedEventArgs with the new size of the tab content area. + // Return Value: + // - + void TerminalPage::_OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e) + { + const auto newSize = e.NewSize(); + for (auto& tab : _tabs) + { + tab->ResizeContent(newSize); + } + } + + // Method Description: + // - Responds to the TabView control's Tab Closing event by removing + // the indicated tab from the set and focusing another one. + // The event is cancelled so App maintains control over the + // items in the tabview. + // Arguments: + // - sender: the control that originated this event + // - eventArgs: the event's constituent arguments + void TerminalPage::_OnTabClosing(const IInspectable& sender, const MUX::Controls::TabViewTabClosingEventArgs& eventArgs) + { + const auto tabViewItem = eventArgs.Item(); + _RemoveTabViewItem(tabViewItem); + + // If we don't cancel the event, the TabView will remove the item itself. + eventArgs.Cancel(true); + } + + // Method Description: + // - Hook up keybindings, and refresh the UI of the terminal. + // This includes update the settings of all the tabs according + // to their profiles, update the title and icon of each tab, and + // finally create the tab flyout + void TerminalPage::_RefreshUIForSettingsReload() + { + // Re-wire the keybindings to their handlers, as we'll have created a + // new AppKeyBindings object. + _HookupKeyBindings(_settings->GetKeybindings()); + + // Refresh UI elements + auto profiles = _settings->GetProfiles(); + for (auto& profile : profiles) + { + const GUID profileGuid = profile.GetGuid(); + TerminalSettings settings = _settings->MakeSettings(profileGuid); + + for (auto& tab : _tabs) + { + // Attempt to reload the settings of any panes with this profile + tab->UpdateSettings(settings, profileGuid); + } + } + + // Update the icon of the tab for the currently focused profile in that tab. + for (auto& tab : _tabs) + { + _UpdateTabIcon(tab); + _UpdateTitle(tab); + } + + this->Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [this]() { + // repopulate the new tab button's flyout with entries for each + // profile, which might have changed + _CreateNewTabFlyout(); + }); + } + + // Method Description: + // - This is the method that App will call when the titlebar + // has been clicked. It dismisses any open flyouts. + // Arguments: + // - + // Return Value: + // - + void TerminalPage::TitlebarClicked() + { + if (_newTabButton && _newTabButton.Flyout()) + { + _newTabButton.Flyout().Hide(); + } + } + + // -------------------------------- WinRT Events --------------------------------- + // Winrt events need a method for adding a callback to the event and removing the callback. + // These macros will define them both for you. + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, UIElement); + DEFINE_EVENT_WITH_TYPED_EVENT_HANDLER(TerminalPage, ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog); } diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index c272b41a1a9..244e8e25ddb 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -6,12 +6,144 @@ #include "winrt/Microsoft.UI.Xaml.Controls.h" #include "TerminalPage.g.h" +#include "Tab.h" +#include "CascadiaSettings.h" +#include "Profile.h" +#include "ScopedResourceLoader.h" + +#include +#include +#include +#include +#include namespace winrt::TerminalApp::implementation { struct TerminalPage : TerminalPageT { + public: TerminalPage(); + + TerminalPage(std::shared_ptr resourceLoader); + + void SetSettings(std::shared_ptr<::TerminalApp::CascadiaSettings> settings, bool needRefreshUI); + + void Create(); + + hstring Title(); + + void ShowOkDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey); + + void TitlebarClicked(); + + // -------------------------------- WinRT Events --------------------------------- + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(TitleChanged, _titleChangeHandlers, winrt::Windows::Foundation::IInspectable, winrt::hstring); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(LastTabClosed, _lastTabClosedHandlers, winrt::Windows::Foundation::IInspectable, winrt::TerminalApp::LastTabClosedEventArgs); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(SetTitleBarContent, _setTitleBarContentHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::UIElement); + DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(ShowDialog, _showDialogHandlers, winrt::Windows::Foundation::IInspectable, winrt::Windows::UI::Xaml::Controls::ContentDialog); + + private: + // If you add controls here, but forget to null them either here or in + // the ctor, you're going to have a bad time. It'll mysteriously fail to + // activate the app. + // ALSO: If you add any UIElements as roots here, make sure they're + // updated in App::_ApplyTheme. The roots currently is _tabRow + // (which is a root when the tabs are in the titlebar.) + Microsoft::UI::Xaml::Controls::TabView _tabView{ nullptr }; + TerminalApp::TabRowControl _tabRow{ nullptr }; + Windows::UI::Xaml::Controls::Grid _tabContent{ nullptr }; + Windows::UI::Xaml::Controls::SplitButton _newTabButton{ nullptr }; + + std::shared_ptr<::TerminalApp::CascadiaSettings> _settings{ nullptr }; + + std::vector> _tabs; + + std::shared_ptr _resourceLoader{ nullptr }; + + void _ShowAboutDialog(); + + void _CreateNewTabFlyout(); + void _OpenNewTabDropdown(); + void _OpenNewTab(std::optional profileIndex); + void _CreateNewTabFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings); + winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(GUID profileGuid, winrt::Microsoft::Terminal::Settings::TerminalSettings settings); + + void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + + void _HookupKeyBindings(TerminalApp::AppKeyBindings bindings) noexcept; + + void _UpdateTitle(std::shared_ptr tab); + void _UpdateTabIcon(std::shared_ptr tab); + void _UpdateTabView(); + void _DuplicateTabViewItem(); + void _RemoveTabViewItem(const IInspectable& tabViewItem); + + void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, std::shared_ptr hostingTab); + + void _SelectNextTab(const bool bMoveRight); + bool _SelectTab(const int tabIndex); + void _MoveFocus(const Direction& direction); + + winrt::Microsoft::Terminal::TerminalControl::TermControl _GetFocusedControl(); + int _GetFocusedTabIndex() const; + void _SetFocusedTabIndex(int tabIndex); + void _CloseFocusedTab(); + void _CloseFocusedPane(); + + // Todo: add more event implementations here + // MSFT:20641986: Add keybindings for New Window + void _Scroll(int delta); + void _SplitVertical(const std::optional& profileGuid); + void _SplitHorizontal(const std::optional& profileGuid); + void _SplitPane(const Pane::SplitState splitType, const std::optional& profileGuid); + void _ResizePane(const Direction& direction); + void _ScrollPage(int delta); + static Windows::UI::Xaml::Controls::IconElement _GetIconFromProfile(const ::TerminalApp::Profile& profile); + void _SetAcceleratorForMenuItem(Windows::UI::Xaml::Controls::MenuFlyoutItem& menuItem, const winrt::Microsoft::Terminal::Settings::KeyChord& keyChord); + + void _CopyToClipboardHandler(const IInspectable& sender, const winrt::Microsoft::Terminal::TerminalControl::CopyToClipboardEventArgs& copiedData); + void _PasteFromClipboardHandler(const IInspectable& sender, + const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs& eventArgs); + bool _CopyText(const bool trimTrailingWhitespace); + void _PasteText(); + static fire_and_forget PasteFromClipboard(winrt::Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs); + + void _OpenSettings(); + fire_and_forget LaunchSettings(); + + void _OnTabClick(const IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& eventArgs); + void _OnTabSelectionChanged(const IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& eventArgs); + void _OnTabItemsChanged(const IInspectable& sender, const Windows::Foundation::Collections::IVectorChangedEventArgs& eventArgs); + void _OnContentSizeChanged(const IInspectable& /*sender*/, Windows::UI::Xaml::SizeChangedEventArgs const& e); + void _OnTabClosing(const IInspectable& sender, const Microsoft::UI::Xaml::Controls::TabViewTabClosingEventArgs& eventArgs); + + void _RefreshUIForSettingsReload(); + +#pragma region ActionHandlers + // These are all defined in AppActionHandlers.cpp + void _HandleNewTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleDuplicateTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleCloseTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleClosePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollUp(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollDown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleNextTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandlePrevTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSplitVertical(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSplitHorizontal(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollUpPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleScrollDownPage(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleOpenSettings(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandlePasteText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleNewTabWithProfile(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleSwitchToTab(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleResizePane(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleMoveFocus(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); + void _HandleCopyText(const IInspectable& sender, const TerminalApp::ActionEventArgs& args); +#pragma endregion }; } diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index ea012620078..62f05fec87b 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -1,10 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import "..\App.idl"; namespace TerminalApp { [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page { TerminalPage(); + + event Windows.Foundation.TypedEventHandler TitleChanged; + event Windows.Foundation.TypedEventHandler LastTabClosed; + event Windows.Foundation.TypedEventHandler SetTitleBarContent; + event Windows.Foundation.TypedEventHandler ShowDialog; } } diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index e2a72ce5594..40439f4c106 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -81,7 +81,7 @@ void AppHost::Initialize() _app.TitleChanged({ this, &AppHost::AppTitleChanged }); _app.LastTabClosed({ this, &AppHost::LastTabClosed }); - AppTitleChanged(_app.GetTitle()); + _window->UpdateTitle(_app.Title()); // Set up the content of the application. If the app has a custom titlebar, // set that content as well. @@ -93,10 +93,11 @@ void AppHost::Initialize() // - Called when the app's title changes. Fires off a window message so we can // update the window's title on the main thread. // Arguments: +// - sender: unused // - newTitle: the string to use as the new window title // Return Value: // - -void AppHost::AppTitleChanged(winrt::hstring newTitle) +void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle) { _window->UpdateTitle(newTitle.c_str()); } @@ -104,10 +105,11 @@ void AppHost::AppTitleChanged(winrt::hstring newTitle) // Method Description: // - Called when no tab is remaining to close the window. // Arguments: -// - +// - sender: unused +// - LastTabClosedEventArgs: unused // Return Value: // - -void AppHost::LastTabClosed() +void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args) { _window->Close(); } @@ -209,7 +211,7 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) // - arg: the UIElement to use as the new Titlebar content. // Return Value: // - -void AppHost::_UpdateTitleBarContent(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::UIElement& arg) +void AppHost::_UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Xaml::UIElement& arg) { if (_useNonClientArea) { diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index 6c926ae3b6b..a66cd93369c 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -14,8 +14,8 @@ class AppHost AppHost() noexcept; virtual ~AppHost(); - void AppTitleChanged(winrt::hstring newTitle); - void LastTabClosed(); + void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle); + void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args); void Initialize(); private: @@ -25,7 +25,7 @@ class AppHost winrt::TerminalApp::App _app; void _HandleCreateWindow(const HWND hwnd, const RECT proposedRect); - void _UpdateTitleBarContent(const winrt::TerminalApp::App& sender, + void _UpdateTitleBarContent(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::UIElement& arg); void _UpdateTheme(const winrt::TerminalApp::App&, const winrt::Windows::UI::Xaml::ElementTheme& arg); From d0c207bc9c3ec7209642d5a0b48aa4a87a58db13 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 4 Sep 2019 15:45:22 -0700 Subject: [PATCH 133/154] fix remaining issues that appeared on merge. --- src/types/ScreenInfoUiaProviderBase.cpp | 10 ++++++---- src/types/ScreenInfoUiaProviderBase.h | 2 +- src/types/utils.cpp | 14 +++++++------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 3e201d30731..3f0baf3e43a 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -10,14 +10,16 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Types::ScreenInfoUiaProviderTracing; // A helper function to create a SafeArray Version of an int array of a specified length -SAFEARRAY* BuildIntSafeArray(std::basic_string_view data) noexcept +SAFEARRAY* BuildIntSafeArray(std::basic_string_view data) { SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, gsl::narrow(data.size())); if (psa != nullptr) { - for (long i = 0; i < data.size(); i++) + for (size_t i = 0; i < data.size(); i++) { - if (FAILED(SafeArrayPutElement(psa, &i, (void*)&(data.at(i))))) + LONG lIndex = 0; + if (FAILED(SizeTToLong(i, &lIndex) || + FAILED(SafeArrayPutElement(psa, &lIndex, (void*)&(data.at(i)))))) { SafeArrayDestroy(psa); psa = nullptr; @@ -241,7 +243,7 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::get_HostRawElementProvider(_COM_Outptr #pragma region IRawElementProviderFragment -IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) noexcept +IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) { // TODO GitHub #1914: Re-attach Tracing to UIA Tree //Tracing::s_TraceUia(this, ApiCall::GetRuntimeId, nullptr); diff --git a/src/types/ScreenInfoUiaProviderBase.h b/src/types/ScreenInfoUiaProviderBase.h index 7aebf5a56e6..5ffe24b5b01 100644 --- a/src/types/ScreenInfoUiaProviderBase.h +++ b/src/types/ScreenInfoUiaProviderBase.h @@ -65,7 +65,7 @@ namespace Microsoft::Console::Types // IRawElementProviderFragment methods virtual IFACEMETHODIMP Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) = 0; - IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) noexcept override; + IFACEMETHODIMP GetRuntimeId(_Outptr_result_maybenull_ SAFEARRAY** ppRuntimeId) override; virtual IFACEMETHODIMP get_BoundingRectangle(_Out_ UiaRect* pRect) = 0; IFACEMETHODIMP GetEmbeddedFragmentRoots(_Outptr_result_maybenull_ SAFEARRAY** ppRoots) noexcept override; IFACEMETHODIMP SetFocus() override; diff --git a/src/types/utils.cpp b/src/types/utils.cpp index 44a39908e1e..bfc3ffe6556 100644 --- a/src/types/utils.cpp +++ b/src/types/utils.cpp @@ -78,7 +78,7 @@ std::string Utils::ColorToHexString(const COLORREF color) COLORREF Utils::ColorFromHexString(const std::string str) { THROW_HR_IF(E_INVALIDARG, str.size() != 7 && str.size() != 4); - THROW_HR_IF(E_INVALIDARG, str[0] != '#'); + THROW_HR_IF(E_INVALIDARG, str.at(0) != '#'); std::string rStr; std::string gStr; @@ -86,15 +86,15 @@ COLORREF Utils::ColorFromHexString(const std::string str) if (str.size() == 4) { - rStr = std::string(2, str[1]); - gStr = std::string(2, str[2]); - bStr = std::string(2, str[3]); + rStr = std::string(2, str.at(1)); + gStr = std::string(2, str.at(2)); + bStr = std::string(2, str.at(3)); } else { - rStr = std::string(&str[1], 2); - gStr = std::string(&str[3], 2); - bStr = std::string(&str[5], 2); + rStr = std::string(&str.at(1), 2); + gStr = std::string(&str.at(3), 2); + bStr = std::string(&str.at(5), 2); } const BYTE r = gsl::narrow_cast(std::stoul(rStr, nullptr, 16)); From 886d018bb4032018ab69829705ea991a486c871e Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 4 Sep 2019 16:43:45 -0700 Subject: [PATCH 134/154] warnings as errors for cppwinrt projects, then fix the warnings (#2660) Fixes #1155. --- src/cascadia/TerminalApp/CascadiaSettings.cpp | 2 +- .../TerminalConnection/AzureConnection.cpp | 58 ++++++++++--------- src/cascadia/TerminalControl/TermControl.cpp | 2 +- .../TermControlAutomationPeer.cpp | 2 +- src/cascadia/WindowsTerminal/AppHost.cpp | 4 +- src/cascadia/WindowsTerminal/IslandWindow.h | 4 +- .../WindowsTerminal/WindowUiaProvider.cpp | 6 +- src/cppwinrt.build.pre.props | 1 + 8 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/cascadia/TerminalApp/CascadiaSettings.cpp b/src/cascadia/TerminalApp/CascadiaSettings.cpp index 823c7693c2d..3a98b5c9b85 100644 --- a/src/cascadia/TerminalApp/CascadiaSettings.cpp +++ b/src/cascadia/TerminalApp/CascadiaSettings.cpp @@ -756,7 +756,7 @@ void CascadiaSettings::_ValidateNoDuplicateProfiles() // Try collecting all the unique guids. If we ever encounter a guid that's // already in the set, then we need to delete that profile. - for (int i = 0; i < _profiles.size(); i++) + for (size_t i = 0; i < _profiles.size(); i++) { if (!uniqueGuids.insert(_profiles.at(i).GetGuid()).second) { diff --git a/src/cascadia/TerminalConnection/AzureConnection.cpp b/src/cascadia/TerminalConnection/AzureConnection.cpp index 029e2986acb..b93529d6706 100644 --- a/src/cascadia/TerminalConnection/AzureConnection.cpp +++ b/src/cascadia/TerminalConnection/AzureConnection.cpp @@ -574,37 +574,41 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation // - S_OK otherwise HRESULT AzureConnection::_TenantChoiceHelper() { - const auto tenantListAsArray = _tenantList.as_array(); - _maxSize = tenantListAsArray.size(); - for (int i = 0; i < _maxSize; i++) - { - const auto& tenant = tenantListAsArray.at(i); - const auto [tenantId, tenantDisplayName] = _crackTenant(tenant); - _outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str())); - } - _outputHandlers(winrt::to_hstring(enterTenant)); - // Use a lock to wait for the user to input a valid number - std::unique_lock tenantNumberLock{ _commonMutex }; - _canProceed.wait(tenantNumberLock, [=]() { - return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _closing.load(); - }); - // User might have closed the tab while we waited for input - if (_closing.load()) + try { - return E_FAIL; - } + const auto tenantListAsArray = _tenantList.as_array(); + _maxSize = gsl::narrow(tenantListAsArray.size()); + for (int i = 0; i < _maxSize; i++) + { + const auto& tenant = tenantListAsArray.at(i); + const auto [tenantId, tenantDisplayName] = _crackTenant(tenant); + _outputHandlers(_StrFormatHelper(ithTenant, i, tenantDisplayName.c_str(), tenantId.c_str())); + } + _outputHandlers(winrt::to_hstring(enterTenant)); + // Use a lock to wait for the user to input a valid number + std::unique_lock tenantNumberLock{ _commonMutex }; + _canProceed.wait(tenantNumberLock, [=]() { + return (_tenantNumber >= 0 && _tenantNumber < _maxSize) || _closing.load(); + }); + // User might have closed the tab while we waited for input + if (_closing.load()) + { + return E_FAIL; + } - const auto& chosenTenant = tenantListAsArray.at(_tenantNumber); - std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant); + const auto& chosenTenant = tenantListAsArray.at(_tenantNumber); + std::tie(_tenantID, _displayName) = _crackTenant(chosenTenant); - // We have to refresh now that we have the tenantID - const auto refreshResponse = _RefreshTokens(); - _accessToken = refreshResponse.at(L"access_token").as_string(); - _refreshToken = refreshResponse.at(L"refresh_token").as_string(); - _expiry = std::stoi(refreshResponse.at(L"expires_on").as_string()); + // We have to refresh now that we have the tenantID + const auto refreshResponse = _RefreshTokens(); + _accessToken = refreshResponse.at(L"access_token").as_string(); + _refreshToken = refreshResponse.at(L"refresh_token").as_string(); + _expiry = std::stoi(refreshResponse.at(L"expires_on").as_string()); - _state = State::StoreTokens; - return S_OK; + _state = State::StoreTokens; + return S_OK; + } + CATCH_RETURN(); } // Method description: diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 5628d037330..910365c0dbe 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1443,7 +1443,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } // send data up for clipboard - auto copyArgs = winrt::make_self(winrt::hstring(textData.data(), textData.size()), winrt::to_hstring(htmlData)); + auto copyArgs = winrt::make_self(winrt::hstring(textData.data(), gsl::narrow(textData.size())), winrt::to_hstring(htmlData)); _clipboardCopyHandlers(*this, *copyArgs); return true; } diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index 585eb32cfa4..18d9f6abb44 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -136,7 +136,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation { // transfer ownership of UiaTextRanges to this new vector auto providers = SafeArrayToOwningVector<::Microsoft::Terminal::UiaTextRange>(textRanges); - int count = providers.size(); + int count = gsl::narrow(providers.size()); std::vector vec; vec.reserve(count); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 40439f4c106..2c09bec71f4 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -97,7 +97,7 @@ void AppHost::Initialize() // - newTitle: the string to use as the new window title // Return Value: // - -void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle) +void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& /*sender*/, winrt::hstring newTitle) { _window->UpdateTitle(newTitle.c_str()); } @@ -109,7 +109,7 @@ void AppHost::AppTitleChanged(const winrt::Windows::Foundation::IInspectable& se // - LastTabClosedEventArgs: unused // Return Value: // - -void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args) +void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::TerminalApp::LastTabClosedEventArgs& /*args*/) { _window->Close(); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 1b95b3308f4..98eb76db35b 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -36,7 +36,7 @@ class IslandWindow : void UpdateTheme(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); #pragma region IUiaWindow - void ChangeViewport(const SMALL_RECT NewWindow) + void ChangeViewport(const SMALL_RECT /*NewWindow*/) { // TODO GitHub #1352: Hook up ScreenInfoUiaProvider to WindowUiaProvider // Relevant comment from zadjii-msft: @@ -57,7 +57,7 @@ class IslandWindow : return BaseWindow::GetHandle(); }; - [[nodiscard]] HRESULT SignalUia(_In_ EVENTID id) override { return E_NOTIMPL; }; + [[nodiscard]] HRESULT SignalUia(_In_ EVENTID /*id*/) override { return E_NOTIMPL; }; [[nodiscard]] HRESULT UiaSetTextAreaFocus() override { return E_NOTIMPL; }; RECT GetWindowRect() const noexcept override diff --git a/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp index d8f51c51414..176c692b9bb 100644 --- a/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp +++ b/src/cascadia/WindowsTerminal/WindowUiaProvider.cpp @@ -107,7 +107,7 @@ WindowUiaProvider* WindowUiaProvider::Create(Microsoft::Console::Types::IUiaWind #pragma region IRawElementProviderFragment -IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection direction, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +IFACEMETHODIMP WindowUiaProvider::Navigate(_In_ NavigateDirection /*direction*/, _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) { RETURN_IF_FAILED(_EnsureValidHwnd()); *ppProvider = nullptr; @@ -139,7 +139,7 @@ IFACEMETHODIMP WindowUiaProvider::SetFocus() IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, _In_ double /*y*/, - _COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) + _COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/) { RETURN_IF_FAILED(_EnsureValidHwnd()); @@ -151,7 +151,7 @@ IFACEMETHODIMP WindowUiaProvider::ElementProviderFromPoint(_In_ double /*x*/, return S_OK; } -IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** ppProvider) +IFACEMETHODIMP WindowUiaProvider::GetFocus(_COM_Outptr_result_maybenull_ IRawElementProviderFragment** /*ppProvider*/) { RETURN_IF_FAILED(_EnsureValidHwnd()); // TODO GitHub #2447: Hook up ScreenInfoUiaProvider to WindowUiaProvider diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 264b59e9ecc..58c8f3aee90 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -94,6 +94,7 @@ pch.h $(IntDir)pch.pch Level4 + true %(AdditionalOptions) /permissive- /bigobj /Zc:twoPhase- /std:c++17 28204 _WINRT_DLL;%(PreprocessorDefinitions) From 96cc7727bc5dccc8695a632cd3588c7256cea9d1 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 5 Sep 2019 11:14:37 -0700 Subject: [PATCH 135/154] Add GH issue IDs to all the suppress/disables that I left behind as they were a bit too challenging to solve with this giant PR --- src/buffer/out/CharRowCellReference.cpp | 2 +- src/buffer/out/OutputCellIterator.cpp | 1 + src/buffer/out/textBuffer.cpp | 2 +- src/inc/DefaultSettings.h | 1 + src/types/GlyphWidth.cpp | 1 + 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/buffer/out/CharRowCellReference.cpp b/src/buffer/out/CharRowCellReference.cpp index 41357320f52..0f8a2e1a574 100644 --- a/src/buffer/out/CharRowCellReference.cpp +++ b/src/buffer/out/CharRowCellReference.cpp @@ -93,7 +93,7 @@ CharRowCellReference::const_iterator CharRowCellReference::begin() const // - end iterator of the glyph data #pragma warning(push) #pragma warning(disable : 26481) -// TODO: eliminate using pointers raw as begin/end markers in this class +// TODO GH 2672: eliminate using pointers raw as begin/end markers in this class CharRowCellReference::const_iterator CharRowCellReference::end() const { if (_cellData().DbcsAttr().IsGlyphStored()) diff --git a/src/buffer/out/OutputCellIterator.cpp b/src/buffer/out/OutputCellIterator.cpp index ebc7e5e0045..dbd9fb8bb7a 100644 --- a/src/buffer/out/OutputCellIterator.cpp +++ b/src/buffer/out/OutputCellIterator.cpp @@ -120,6 +120,7 @@ OutputCellIterator::OutputCellIterator(const std::wstring_view utf16Text, const #pragma warning(suppress : 26490) // Suppresses reinterpret_cast. We're only doing this because Windows doesn't understand the type difference between wchar_t and DWORD. // It is not worth trying to separate that out further or risking performance over this particular warning here. +// TODO GH 2673 - Investigate real wchar_t flag in Windows and resolve this audit issue OutputCellIterator::OutputCellIterator(const std::basic_string_view legacyAttrs, const bool /*unused*/) noexcept : _mode(Mode::LegacyAttr), _currentView(s_GenerateViewLegacyAttr(legacyAttrs.at(0))), diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index d61d25d0bc6..0372dc5903f 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1003,7 +1003,7 @@ const TextBuffer::TextAndColor TextBuffer::GetTextForClipboard(const bool lineSe } } #pragma warning(suppress : 26444) - // TODO: figure out why there's custom construction/destruction happening here + // TODO GH 2675: figure out why there's custom construction/destruction happening here it++; } diff --git a/src/inc/DefaultSettings.h b/src/inc/DefaultSettings.h index 41c8f416c44..ed6d8f2a2f7 100644 --- a/src/inc/DefaultSettings.h +++ b/src/inc/DefaultSettings.h @@ -29,6 +29,7 @@ constexpr short DEFAULT_HISTORY_SIZE = 9001; #pragma warning(push) #pragma warning(disable : 26426) +// TODO GH 2674, don't disable this warning, move to std::wstring_view or something like that. const std::wstring DEFAULT_FONT_FACE{ L"Consolas" }; constexpr int DEFAULT_FONT_SIZE = 12; diff --git a/src/types/GlyphWidth.cpp b/src/types/GlyphWidth.cpp index 2a7d5113e6c..f90c79f9481 100644 --- a/src/types/GlyphWidth.cpp +++ b/src/types/GlyphWidth.cpp @@ -6,6 +6,7 @@ #include "inc/GlyphWidth.hpp" #pragma warning(suppress : 26426) +// TODO GH 2676 - remove warning suppression and decide what to do re: singleton instance of CodepointWidthDetector static CodepointWidthDetector widthDetector; // Function Description: From 689c21e802fa3d630fefec0b7ccec03fabc0599c Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 5 Sep 2019 11:17:13 -0700 Subject: [PATCH 136/154] PR feedback. --- src/renderer/dx/DxRenderer.cpp | 4 ++-- src/types/inc/UTF8OutPipeReader.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 982e666a8de..2effe1b4af7 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -1266,8 +1266,8 @@ enum class CursorPaintType [[nodiscard]] Viewport DxEngine::GetViewportInCharacters(const Viewport& viewInPixels) noexcept { - const short widthInChars = gsl::narrow(viewInPixels.Width() / _glyphCell.cx); - const short heightInChars = gsl::narrow(viewInPixels.Height() / _glyphCell.cy); + const short widthInChars = gsl::narrow_cast(viewInPixels.Width() / _glyphCell.cx); + const short heightInChars = gsl::narrow_cast(viewInPixels.Height() / _glyphCell.cy); return Viewport::FromDimensions(viewInPixels.Origin(), { widthInChars, heightInChars }); } diff --git a/src/types/inc/UTF8OutPipeReader.hpp b/src/types/inc/UTF8OutPipeReader.hpp index 3f0d84f79f6..93c1b5975ff 100644 --- a/src/types/inc/UTF8OutPipeReader.hpp +++ b/src/types/inc/UTF8OutPipeReader.hpp @@ -46,7 +46,7 @@ class UTF8OutPipeReader final }; // array of bitmasks - constexpr const static std::array _cmpMasks{ + constexpr static std::array _cmpMasks{ 0, // unused _Utf8BitMasks::MaskContinuationByte, _Utf8BitMasks::MaskLeadByteTwoByteSequence, @@ -54,7 +54,7 @@ class UTF8OutPipeReader final }; // array of values for the comparisons - constexpr const static std::array _cmpOperands{ + constexpr static std::array _cmpOperands{ 0, // unused _Utf8BitMasks::IsAsciiByte, // intentionally conflicts with MaskContinuationByte _Utf8BitMasks::IsLeadByteTwoByteSequence, From fc81adf32f42fbdcf03301d2d5b23114af0049ee Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 5 Sep 2019 13:09:36 -0700 Subject: [PATCH 137/154] use the array size for the read bounds. using extent on the newly-converted-to-array type doesn't give the correct value. --- src/types/UTF8OutPipeReader.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/UTF8OutPipeReader.cpp b/src/types/UTF8OutPipeReader.cpp index c35530b8b62..1e69046c675 100644 --- a/src/types/UTF8OutPipeReader.cpp +++ b/src/types/UTF8OutPipeReader.cpp @@ -33,7 +33,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) noexcept : // in case of early escaping _buffer.at(0) = 0; - strView = std::string_view{ &_buffer.at(0), 0 }; + strView = std::string_view{ _buffer.data(), 0 }; // copy UTF-8 code units that were remaining from the previously read chunk (if any) if (_dwPartialsLen != 0) @@ -42,7 +42,7 @@ UTF8OutPipeReader::UTF8OutPipeReader(HANDLE outPipe) noexcept : } // try to read data - fSuccess = !!ReadFile(_outPipe, &_buffer.at(_dwPartialsLen), std::extent::value - _dwPartialsLen, &dwRead, nullptr); + fSuccess = !!ReadFile(_outPipe, &_buffer.at(_dwPartialsLen), gsl::narrow(_buffer.size()) - _dwPartialsLen, &dwRead, nullptr); dwRead += _dwPartialsLen; _dwPartialsLen = 0; From c58033cda2d34367b7c9d2c1d67810c790d342a9 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Sep 2019 15:37:27 -0500 Subject: [PATCH 138/154] Don't crash when restore-down'ing the alt buffer (#2666) ## Summary of the Pull Request When a user had "Disable Scroll Forward" enabled and switched to the alt buffer and maximized the console, then restored down, we'd crash. Now we don't. ## References ## PR Checklist * [x] Closes #1206 * [x] I work here * [x] Tests added/passed ## Detailed Description of the Pull Request / Additional comments The problem is that we'd previously try to "anchor" the viewport to the virtual bottom when resizing like this. This would also cause us to move the top of the viewport down, into the buffer. However, if the alt buffer is getting smaller, we don't want to do this - if we anchor to the old _virtualBottom, the bottom of the viewport will actually be outside the current buffer. This could theoretically happen with the main buffer too, but it's much easier to repro with the alt buffer. --- src/host/screenInfo.cpp | 7 ++- src/host/ut_host/ScreenBufferTests.cpp | 66 ++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 07233506777..3fabea0bae6 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -1231,10 +1231,13 @@ void SCREEN_INFORMATION::_InternalSetViewportSize(const COORD* const pcoordSize, // See MSFT:19917443 // If we're in terminal scrolling mode, and we've changed the height of the - // viewport, the new viewport's bottom to the _virtualBottom + // viewport, the new viewport's bottom to the _virtualBottom. + // GH#1206 - Only do this if the viewport is _growing_ in height. This can + // cause unexpected behavior if we try to anchor the _virtualBottom to a + // position that will be greater than the height of the buffer. const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); auto newViewport = Viewport::FromInclusive(srNewViewport); - if (gci.IsTerminalScrolling() && newViewport.Height() != _viewport.Height()) + if (gci.IsTerminalScrolling() && newViewport.Height() >= _viewport.Height()) { const short newTop = static_cast(std::max(0, _virtualBottom - (newViewport.Height() - 1))); diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 94d77faaff9..d8caf73577f 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -170,6 +170,8 @@ class ScreenBufferTests TEST_METHOD(SetOriginMode); TEST_METHOD(HardResetBuffer); + + TEST_METHOD(RestoreDownAltBufferWithTerminalScrolling); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -3581,3 +3583,67 @@ void ScreenBufferTests::HardResetBuffer() VERIFY_ARE_EQUAL(COORD({ 0, 0 }), viewport.Origin()); VERIFY_ARE_EQUAL(COORD({ 0, 0 }), cursor.GetPosition()); } + +void ScreenBufferTests::RestoreDownAltBufferWithTerminalScrolling() +{ + // This is a test for microsoft/terminal#1206. Refer to that issue for more + // context + + CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + gci.SetTerminalScrolling(true); + gci.LockConsole(); // Lock must be taken to manipulate buffer. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + auto& siMain = gci.GetActiveOutputBuffer(); + COORD const coordFontSize = siMain.GetScreenFontSize(); + siMain._virtualBottom = siMain._viewport.BottomInclusive(); + + auto originalView = siMain._viewport; + + VERIFY_IS_NULL(siMain._psiMainBuffer); + VERIFY_IS_NULL(siMain._psiAlternateBuffer); + + Log::Comment(L"Create an alternate buffer"); + if (VERIFY_IS_TRUE(NT_SUCCESS(siMain.UseAlternateScreenBuffer()))) + { + VERIFY_IS_NOT_NULL(siMain._psiAlternateBuffer); + auto& altBuffer = *siMain._psiAlternateBuffer; + VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top()); + VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); + + const COORD originalSize = originalView.Dimensions(); + const COORD doubledSize = { originalSize.X * 2, originalSize.Y * 2 }; + + // Create some RECTs, which are dimensions in pixels, because + // ProcessResizeWindow needs to work on rects in screen _pixel_ + // dimensions, not character sizes. + RECT originalClientRect{ 0 }, maximizedClientRect{ 0 }; + + originalClientRect.right = originalSize.X * coordFontSize.X; + originalClientRect.bottom = originalSize.Y * coordFontSize.Y; + + maximizedClientRect.right = doubledSize.X * coordFontSize.X; + maximizedClientRect.bottom = doubledSize.Y * coordFontSize.Y; + + Log::Comment(NoThrowString().Format( + L"Emulate a maximize")); + // Note that just calling _InternalSetViewportSize does not hit the + // exceptional case here. There's other logic farther down the stack + // that triggers it. + altBuffer.ProcessResizeWindow(&maximizedClientRect, &originalClientRect); + + VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top()); + VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); + + Log::Comment(NoThrowString().Format( + L"Emulate a restore down")); + + altBuffer.ProcessResizeWindow(&originalClientRect, &maximizedClientRect); + + // Before the bugfix, this would fail, with the top being roughly 80, + // halfway into the buffer, with the bottom being anchored to the old + // size. + VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top()); + VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); + } +} From 125e1771aed0a7b4e516e7fba1934c78f18c749b Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 5 Sep 2019 15:38:42 -0500 Subject: [PATCH 139/154] Add some logging around startup, connection start timing (#2544) Adds a number of TL events we can use to track startup time better. Adds events for: * Initial exe start * Time the window is created * time we start loading settings * time we finish loading setings * time when a connection recieves its first byte Also updates our `ConnectionCreated` event to include the session GUID, so that we can correlate that with the connection's `RecievedFirstByte` event. --- src/cascadia/TerminalApp/App.cpp | 20 ++++++++++++ src/cascadia/TerminalApp/TerminalPage.cpp | 17 ++++++---- .../TerminalConnection/ConhostConnection.cpp | 17 ++++++++++ .../TerminalConnection/ConhostConnection.h | 2 ++ .../TerminalConnection.vcxproj | 1 + src/cascadia/TerminalConnection/init.cpp | 31 +++++++++++++++++++ src/cascadia/TerminalConnection/pch.h | 4 +++ src/cascadia/WindowsTerminal/AppHost.cpp | 7 +++++ src/cascadia/WindowsTerminal/main.cpp | 17 ++++++++++ src/cascadia/WindowsTerminal/pch.h | 7 +++++ 10 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 src/cascadia/TerminalConnection/init.cpp diff --git a/src/cascadia/TerminalApp/App.cpp b/src/cascadia/TerminalApp/App.cpp index 421aa4577e1..dc156bef5c5 100644 --- a/src/cascadia/TerminalApp/App.cpp +++ b/src/cascadia/TerminalApp/App.cpp @@ -392,6 +392,15 @@ namespace winrt::TerminalApp::implementation // happening during startup, it'll need to happen on a background thread. void App::LoadSettings() { + auto start = std::chrono::high_resolution_clock::now(); + + TraceLoggingWrite( + g_hTerminalAppProvider, + "SettingsLoadStarted", + TraceLoggingDescription("Event emitted before loading the settings"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + // Attempt to load the settings. // If it fails, // - use Default settings, @@ -408,6 +417,17 @@ namespace winrt::TerminalApp::implementation _settings->CreateDefaults(); } + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration delta = end - start; + + TraceLoggingWrite( + g_hTerminalAppProvider, + "SettingsLoadComplete", + TraceLoggingDescription("Event emitted when loading the settings is finished"), + TraceLoggingFloat64(delta.count(), "Duration"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + _loadedInitialSettings = true; // Register for directory change notification. diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 1fe835defec..c26e3282582 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -411,6 +411,7 @@ namespace winrt::TerminalApp::implementation TerminalConnection::ITerminalConnection connection{ nullptr }; GUID connectionType{ 0 }; + GUID sessionGuid{ 0 }; if (profile->HasConnectionType()) { @@ -427,12 +428,14 @@ namespace winrt::TerminalApp::implementation else { - connection = TerminalConnection::ConhostConnection(settings.Commandline(), - settings.StartingDirectory(), - settings.StartingTitle(), - settings.InitialRows(), - settings.InitialCols(), - winrt::guid()); + auto conhostConn = TerminalConnection::ConhostConnection(settings.Commandline(), + settings.StartingDirectory(), + settings.StartingTitle(), + settings.InitialRows(), + settings.InitialCols(), + winrt::guid()); + sessionGuid = conhostConn.Guid(); + connection = conhostConn; } TraceLoggingWrite( @@ -440,6 +443,8 @@ namespace winrt::TerminalApp::implementation "ConnectionCreated", TraceLoggingDescription("Event emitted upon the creation of a connection"), TraceLoggingGuid(connectionType, "ConnectionTypeGuid", "The type of the connection"), + TraceLoggingGuid(profileGuid, "ProfileGuid", "The profile's GUID"), + TraceLoggingGuid(sessionGuid, "SessionGuid", "The WT_SESSION's GUID"), TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); diff --git a/src/cascadia/TerminalConnection/ConhostConnection.cpp b/src/cascadia/TerminalConnection/ConhostConnection.cpp index 3da9d43f913..5c2280251eb 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.cpp +++ b/src/cascadia/TerminalConnection/ConhostConnection.cpp @@ -108,6 +108,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation si, extraEnvVars)); + _startTime = std::chrono::high_resolution_clock::now(); + // Create our own output handling thread // This must be done after the pipes are populated. // Each connection needs to make sure to drain the output from its backing host. @@ -209,6 +211,21 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation return 0; } + if (!_recievedFirstByte) + { + auto now = std::chrono::high_resolution_clock::now(); + std::chrono::duration delta = now - _startTime; + + TraceLoggingWrite(g_hTerminalConnectionProvider, + "RecievedFirstByte", + TraceLoggingDescription("An event emitted when the connection recieves the first byte"), + TraceLoggingGuid(_guid, "SessionGuid", "The WT_SESSION's GUID"), + TraceLoggingFloat64(delta.count(), "Duration"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + _recievedFirstByte = true; + } + // Convert buffer to hstring auto hstr{ winrt::to_hstring(strView) }; diff --git a/src/cascadia/TerminalConnection/ConhostConnection.h b/src/cascadia/TerminalConnection/ConhostConnection.h index dc5893f0094..93aeada8d5a 100644 --- a/src/cascadia/TerminalConnection/ConhostConnection.h +++ b/src/cascadia/TerminalConnection/ConhostConnection.h @@ -35,6 +35,8 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation bool _connected{}; std::atomic _closing{ false }; + bool _recievedFirstByte{ false }; + std::chrono::high_resolution_clock::time_point _startTime{}; wil::unique_hfile _inPipe; // The pipe for writing input to wil::unique_hfile _outPipe; // The pipe for reading output from diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index 65ca2bacad8..cf277ae6486 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -35,6 +35,7 @@ + diff --git a/src/cascadia/TerminalConnection/init.cpp b/src/cascadia/TerminalConnection/init.cpp new file mode 100644 index 00000000000..6d3521c11e9 --- /dev/null +++ b/src/cascadia/TerminalConnection/init.cpp @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" + +// Note: Generate GUID using TlgGuid.exe tool +TRACELOGGING_DEFINE_PROVIDER( + g_hTerminalConnectionProvider, + "Microsoft.Windows.Terminal.Connection", + // {e912fe7b-eeb6-52a5-c628-abe388e5f792} + (0xe912fe7b, 0xeeb6, 0x52a5, 0xc6, 0x28, 0xab, 0xe3, 0x88, 0xe5, 0xf7, 0x92), + TraceLoggingOptionMicrosoftTelemetry()); + +BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID /*reserved*/) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDll); + TraceLoggingRegister(g_hTerminalConnectionProvider); + break; + case DLL_PROCESS_DETACH: + if (g_hTerminalConnectionProvider) + { + TraceLoggingUnregister(g_hTerminalConnectionProvider); + } + break; + } + + return TRUE; +} diff --git a/src/cascadia/TerminalConnection/pch.h b/src/cascadia/TerminalConnection/pch.h index 5878290603a..eff5c531e89 100644 --- a/src/cascadia/TerminalConnection/pch.h +++ b/src/cascadia/TerminalConnection/pch.h @@ -19,3 +19,7 @@ #include "winrt/Windows.Security.Credentials.h" #include "winrt/Windows.Foundation.Collections.h" #include + +#include +TRACELOGGING_DECLARE_PROVIDER(g_hTerminalConnectionProvider); +#include diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 2c09bec71f4..78ef79f34e5 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -201,6 +201,13 @@ void AppHost::_HandleCreateWindow(const HWND hwnd, const RECT proposedRect) // If we can't resize the window, that's really okay. We can just go on with // the originally proposed window size. LOG_LAST_ERROR_IF(!succeeded); + + TraceLoggingWrite( + g_hWindowsTerminalProvider, + "WindowCreated", + TraceLoggingDescription("Event emitted upon creating the application window"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); } // Method Description: diff --git a/src/cascadia/WindowsTerminal/main.cpp b/src/cascadia/WindowsTerminal/main.cpp index 3b357f9c43b..6b4e7384779 100644 --- a/src/cascadia/WindowsTerminal/main.cpp +++ b/src/cascadia/WindowsTerminal/main.cpp @@ -11,6 +11,15 @@ using namespace Windows::UI::Composition; using namespace Windows::UI::Xaml::Hosting; using namespace Windows::Foundation::Numerics; +// Note: Generate GUID using TlgGuid.exe tool - seriously, it won't work if you +// just generate an arbitrary GUID +TRACELOGGING_DEFINE_PROVIDER( + g_hWindowsTerminalProvider, + "Microsoft.Windows.Terminal.Win32Host", + // {56c06166-2e2e-5f4d-7ff3-74f4b78c87d6} + (0x56c06166, 0x2e2e, 0x5f4d, 0x7f, 0xf3, 0x74, 0xf4, 0xb7, 0x8c, 0x87, 0xd6), + TraceLoggingOptionMicrosoftTelemetry()); + // Routine Description: // - Retrieves the string resource from the current module with the given ID // from the resources files. See resource.h and the .rc definitions for valid IDs. @@ -94,6 +103,14 @@ static void EnsureNativeArchitecture() int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) { + TraceLoggingRegister(g_hWindowsTerminalProvider); + TraceLoggingWrite( + g_hWindowsTerminalProvider, + "ExecutableStarted", + TraceLoggingDescription("Event emitted immediately on startup"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance)); + // Block the user from starting if they launched the incorrect architecture version of the project. // This should only be applicable to developer versions. The package installation process // should choose and install the correct one from the bundle. diff --git a/src/cascadia/WindowsTerminal/pch.h b/src/cascadia/WindowsTerminal/pch.h index 0ae28bc43b8..b6d9e77abdc 100644 --- a/src/cascadia/WindowsTerminal/pch.h +++ b/src/cascadia/WindowsTerminal/pch.h @@ -52,3 +52,10 @@ Module Name: #include #include + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); +#include +#include From d8ff47a0d3534e8ddfd7cfd12b3c43738b51257f Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 5 Sep 2019 17:16:31 -0700 Subject: [PATCH 140/154] Some of the PR feedback. --- src/buffer/out/OutputCellView.cpp | 1 + src/buffer/out/textBuffer.cpp | 2 +- src/buffer/out/textBufferCellIterator.cpp | 2 +- src/buffer/out/textBufferTextIterator.cpp | 2 ++ src/renderer/dx/CustomTextRenderer.cpp | 9 +++---- src/renderer/dx/DxRenderer.cpp | 29 +++++++---------------- src/renderer/dx/DxRenderer.hpp | 11 ++++++++- src/types/ScreenInfoUiaProviderBase.cpp | 6 ++--- 8 files changed, 29 insertions(+), 33 deletions(-) diff --git a/src/buffer/out/OutputCellView.cpp b/src/buffer/out/OutputCellView.cpp index 57a94498893..cd1f16a7646 100644 --- a/src/buffer/out/OutputCellView.cpp +++ b/src/buffer/out/OutputCellView.cpp @@ -28,6 +28,7 @@ OutputCellView::OutputCellView(const std::wstring_view view, // Return Value: // - Reference to UTF-16 character data // C26445 - suppressed to enable the `TextBufferTextIterator::operator->` method which needs a non-temporary memory location holding the wstring_view. +// TODO: GH 2681 - remove this suppression by reconciling the probably bad design of the iterators that leads to this being required. [[gsl::suppress(26445)]] const std::wstring_view& OutputCellView::Chars() const noexcept { return _view; diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 0372dc5903f..a64406f074b 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1065,7 +1065,7 @@ std::string TextBuffer::GenHTML(const TextAndColor& rows, const int fontHeightPo { try { - // TODO: the font name needs to be passed and stored around as an actual bounded type, not an implicit bounds on LF_FACESIZE + // TODO: GH 602 the font name needs to be passed and stored around as an actual bounded type, not an implicit bounds on LF_FACESIZE const auto faceLength = wcsnlen_s(fontFaceName, LF_FACESIZE); const std::wstring_view faceNameView{ fontFaceName, faceLength }; diff --git a/src/buffer/out/textBufferCellIterator.cpp b/src/buffer/out/textBufferCellIterator.cpp index 999370cfa7d..285a4ec2047 100644 --- a/src/buffer/out/textBufferCellIterator.cpp +++ b/src/buffer/out/textBufferCellIterator.cpp @@ -212,7 +212,7 @@ void TextBufferCellIterator::_SetPos(const COORD newPos) if (newPos.X != _pos.X) { - const ptrdiff_t diff = gsl::narrow_cast(newPos.X) - gsl::narrow_cast(_pos.X); + const auto diff = gsl::narrow_cast(newPos.X) - gsl::narrow_cast(_pos.X); _attrIter += diff; } diff --git a/src/buffer/out/textBufferTextIterator.cpp b/src/buffer/out/textBufferTextIterator.cpp index c0cc3ad0882..9687e317584 100644 --- a/src/buffer/out/textBufferTextIterator.cpp +++ b/src/buffer/out/textBufferTextIterator.cpp @@ -25,6 +25,7 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data +// TODO GH 2682, fix design so this doesn't have to be suppressed. [[gsl::suppress(26434)]] const std::wstring_view TextBufferTextIterator::operator*() const noexcept { return _view.Chars(); @@ -34,6 +35,7 @@ TextBufferTextIterator::TextBufferTextIterator(const TextBufferCellIterator& cel // - Returns the text information from the text buffer position addressed by this iterator. // Return Value: // - Read only UTF-16 text data +// TODO GH 2682, fix design so this doesn't have to be suppressed. [[gsl::suppress(26434)]] const std::wstring_view* TextBufferTextIterator::operator->() const noexcept { return &_view.Chars(); diff --git a/src/renderer/dx/CustomTextRenderer.cpp b/src/renderer/dx/CustomTextRenderer.cpp index b071f69e195..c0e545faf20 100644 --- a/src/renderer/dx/CustomTextRenderer.cpp +++ b/src/renderer/dx/CustomTextRenderer.cpp @@ -257,7 +257,7 @@ using namespace Microsoft::Console::Render; // Then make a copy for the baseline origin (which is part way down the left side of the text, not the top or bottom). // We'll use this baseline Origin for drawing the actual text. - const D2D1_POINT_2F baselineOrigin = { origin.x, origin.y + drawingContext->spacing.baseline }; + const D2D1_POINT_2F baselineOrigin{ origin.x, origin.y + drawingContext->spacing.baseline }; ::Microsoft::WRL::ComPtr d2dContext; RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf())); @@ -270,10 +270,7 @@ using namespace Microsoft::Console::Render; rect.right = rect.left; const auto advancesSpan = gsl::make_span(glyphRun->glyphAdvances, glyphRun->glyphCount); - for (const auto& advance : advancesSpan) - { - rect.right += advance; - } + rect.right = std::accumulate(advancesSpan.cbegin(), advancesSpan.cend(), rect.right); d2dContext->FillRectangle(rect, drawingContext->backgroundBrush); @@ -377,7 +374,7 @@ using namespace Microsoft::Console::Render; // This run is solid-color outlines, either from non-color // glyphs or from COLR glyph layers. Use Direct2D to draw them. - ID2D1Brush* layerBrush = nullptr; + ID2D1Brush* layerBrush{ nullptr }; // The rule is "if 0xffff, use current brush." See: // https://docs.microsoft.com/en-us/windows/desktop/api/dwrite_2/ns-dwrite_2-dwrite_color_glyph_run if (colorRun->paletteIndex == 0xFFFF) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 2effe1b4af7..a3d5eab2701 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -25,7 +25,7 @@ using namespace Microsoft::Console::Types; // - Constructs a DirectX-based renderer for console text // which primarily uses DirectWrite on a Direct2D surface #pragma warning(suppress : 26455) -// TODO: The default constructor should not throw. +// TODO GH 2683: The default constructor should not throw. DxEngine::DxEngine() : RenderEngineBase(), _isInvalidUsed{ false }, @@ -149,12 +149,11 @@ DxEngine::~DxEngine() // D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED; - std::array FeatureLevels; - FeatureLevels.at(0) = D3D_FEATURE_LEVEL_11_1; - FeatureLevels.at(1) = D3D_FEATURE_LEVEL_11_0; - FeatureLevels.at(2) = D3D_FEATURE_LEVEL_10_1; - FeatureLevels.at(3) = D3D_FEATURE_LEVEL_10_0; - FeatureLevels.at(4) = D3D_FEATURE_LEVEL_9_1; + const std::array FeatureLevels{ D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_1 }; // Trying hardware first for maximum performance, then trying WARP (software) renderer second // in case we're running inside a downlevel VM where hardware passthrough isn't enabled like @@ -164,7 +163,7 @@ DxEngine::~DxEngine() nullptr, DeviceFlags, FeatureLevels.data(), - gsl::narrow(FeatureLevels.size()), + gsl::narrow_cast(FeatureLevels.size()), D3D11_SDK_VERSION, &_d3dDevice, nullptr, @@ -177,7 +176,7 @@ DxEngine::~DxEngine() nullptr, DeviceFlags, FeatureLevels.data(), - gsl::narrow(FeatureLevels.size()), + gsl::narrow_cast(FeatureLevels.size()), D3D11_SDK_VERSION, &_d3dDevice, nullptr, @@ -1132,6 +1131,7 @@ enum class CursorPaintType { // Enforce min/max cursor height ULONG ulHeight = std::clamp(options.ulCursorHeightPercent, s_ulMinCursorHeightPercent, s_ulMaxCursorHeightPercent); + ulHeight = gsl::narrow((_glyphCell.cy * ulHeight) / 100); rect.top = rect.bottom - ulHeight; break; @@ -1794,14 +1794,3 @@ float DxEngine::GetScaling() const noexcept FAIL_FAST_HR(E_NOTIMPL); } } - -// Routine Description: -// - Helps convert a Direct2D ColorF into a DXGI RGBA -// Arguments: -// - color - Direct2D Color F -// Return Value: -// - DXGI RGBA -[[nodiscard]] constexpr DXGI_RGBA DxEngine::s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept -{ - return { color.r, color.g, color.b, color.a }; -} diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index 2f238ad9080..f0960120316 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -213,6 +213,15 @@ namespace Microsoft::Console::Render [[nodiscard]] D2D1_COLOR_F _ColorFFromColorRef(const COLORREF color) noexcept; - [[nodiscard]] static constexpr DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept; + // Routine Description: + // - Helps convert a Direct2D ColorF into a DXGI RGBA + // Arguments: + // - color - Direct2D Color F + // Return Value: + // - DXGI RGBA + [[nodiscard]] constexpr DXGI_RGBA s_RgbaFromColorF(const D2D1_COLOR_F color) noexcept + { + return { color.r, color.g, color.b, color.a }; + } }; } diff --git a/src/types/ScreenInfoUiaProviderBase.cpp b/src/types/ScreenInfoUiaProviderBase.cpp index 3f0baf3e43a..27cf40c14e0 100644 --- a/src/types/ScreenInfoUiaProviderBase.cpp +++ b/src/types/ScreenInfoUiaProviderBase.cpp @@ -253,11 +253,9 @@ IFACEMETHODIMP ScreenInfoUiaProviderBase::GetRuntimeId(_Outptr_result_maybenull_ *ppRuntimeId = nullptr; // AppendRuntimeId is a magic Number that tells UIAutomation to Append its own Runtime ID(From the HWND) - std::array rId; - rId.at(0) = UiaAppendRuntimeId; - rId.at(1) = -1; + const std::array rId{ UiaAppendRuntimeId, -1 }; - const auto span = std::basic_string_view(rId.data(), rId.size()); + const std::basic_string_view span{ rId.data(), rId.size() }; // BuildIntSafeArray is a custom function to hide the SafeArray creation *ppRuntimeId = BuildIntSafeArray(span); RETURN_IF_NULL_ALLOC(*ppRuntimeId); From fecddafad5eade30e4ed4a6cfba7cdaba071aac1 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Fri, 6 Sep 2019 06:55:13 -0700 Subject: [PATCH 141/154] Changed feedback hub request rule (#2680) We were using a tag to trigger the bot for the verbose feedback hub response. But... 1. We have run into several instances of the bot aggressively replying multiple times before the tag is removed. 2. We asked for a "comment contains" function in the bot and the Fabric Bot team obliged. So I've changed it to `/duplicate` from the tag trigger and will remove the tag. --- doc/bot.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/bot.md b/doc/bot.md index b96c7e233e4..73ba489c0fe 100644 --- a/doc/bot.md +++ b/doc/bot.md @@ -76,10 +76,9 @@ We'll be using tags, primarily, to help us understand what needs attention, what - Then close the issue automatically informing the opener that they can resolve the problem and reopen the issue. (See Bug/Feature templates for example situations.) #### Help ask for Feedback Hub -- If an issue is tagged `Needs-Feedback-Hub` -- Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link. -- And remove the `Needs-Feedback-Hub` tag -- And add the `Needs-Author-Feedback` tag +- When a comment on the thread says `/feedback`... +1. Then reply to the issue with a bit of text on asking the author to send us data with Feedback Hub and give us the link. +1. And add the `Needs-Author-Feedback` tag #### Remove Help Wanted from In PR issues - If an issue gets the `In-PR` tag when a new PR is created, we will remove the `Help-Wanted` tag to avoid someone trying to work on an issue where another person has already submitted a proposed fix. From badbbc43a42016dfc1426ee3db543428d15001c7 Mon Sep 17 00:00:00 2001 From: Martin Lopes <54248166+martin389@users.noreply.github.com> Date: Mon, 9 Sep 2019 03:20:17 +0100 Subject: [PATCH 142/154] doc: amend docs procedure for `Running a Different Shell` (#2605) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Amends user-docs procedure Amends docs procedure for `Running a Different Shell`: * Adds an overview sentence. * Adds some light rephrasing. * Proposes using the countersink arrow `⌵` to depict the `down` GUI element. * Adds link to WSL installation guide --- doc/user-docs/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/user-docs/index.md b/doc/user-docs/index.md index 9229aa780e9..5379c017079 100644 --- a/doc/user-docs/index.md +++ b/doc/user-docs/index.md @@ -36,12 +36,14 @@ default shell is displayed (default shortcut `Ctrl+Shift+1`). ## Running a Different Shell -Note: The following text assumes you have WSL installed. +Note: This section assumes you already have _Windows Subsystem for Linux_ (WSL) installed. For more information, see [the installation guide](https://docs.microsoft.com/en-us/windows/wsl/install-win10). -To choose a different shell (e.g. `cmd.exe` or WSL `bash`) then +Windows Terminal uses PowerShell as its default shell. You can also use Windows Terminal to launch other shells, such as `cmd.exe` or WSL's `bash`: -1. Select the `down` button next to the `+` in the tab bar -2. Choose your new shell from the list (more on how to extend the list in the config section) +1. In the tab bar, click the `⌵` button to view the available shells. +2. Choose your shell from the dropdown list. The new shell session will open in a new tab. + +To customize the shell list, see the _Configuring Windows Terminal_ section below. ## Starting a new PowerShell tab with admin privilege From ce34c7320cbfbc2e63b49e43e3a2a47cc68ec188 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 9 Sep 2019 09:45:05 -0500 Subject: [PATCH 143/154] Prevent "Options" propsheet from reverting cursor shape settings (#2663) * this actually fixes #1219 * the terminal page should check the checkbox on the options page * Discard these changes from #2651 * Add comments, pull function out to helper --- src/propsheet/OptionsPage.cpp | 41 +++++++++++++++++++++++----------- src/propsheet/OptionsPage.h | 3 ++- src/propsheet/TerminalPage.cpp | 13 +++++++++++ src/propsheet/console.rc | 4 ++++ src/propsheet/dialogs.h | 1 + src/propsheet/globals.cpp | 1 + src/propsheet/globals.h | 1 + 7 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/propsheet/OptionsPage.cpp b/src/propsheet/OptionsPage.cpp index 8ce4a638af1..27561a4fdd4 100644 --- a/src/propsheet/OptionsPage.cpp +++ b/src/propsheet/OptionsPage.cpp @@ -3,6 +3,30 @@ #include "precomp.h" +void InitializeCursorSize(const HWND hOptionsDlg) +{ + unsigned int newRadioValue = IDD_CURSOR_ADVANCED; + if (gpStateInfo->CursorType != 0) + { + // IDD_CURSOR_ADVANCED is used as a placeholder for when a + // non-legacy shape is selected. + newRadioValue = IDD_CURSOR_ADVANCED; + } + else if (gpStateInfo->CursorSize <= 25) + { + newRadioValue = IDD_CURSOR_SMALL; + } + else if (gpStateInfo->CursorSize <= 50) + { + newRadioValue = IDD_CURSOR_MEDIUM; + } + else + { + newRadioValue = IDD_CURSOR_LARGE; + } + CheckRadioButton(hOptionsDlg, IDD_CURSOR_SMALL, IDD_CURSOR_ADVANCED, newRadioValue); +} + bool OptionsCommandCallback(HWND hDlg, const unsigned int Item, const unsigned int Notification, HWND hControlWindow) { UINT Value; @@ -147,6 +171,9 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara switch (wMsg) { case WM_INITDIALOG: + // Initialize the global handle to this dialog + g_hOptionsDlg = hDlg; + CheckDlgButton(hDlg, IDD_HISTORY_NODUP, gpStateInfo->HistoryNoDup); CheckDlgButton(hDlg, IDD_QUICKEDIT, gpStateInfo->QuickEdit); CheckDlgButton(hDlg, IDD_INSERT, gpStateInfo->InsertMode); @@ -167,19 +194,7 @@ INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lPara CreateAndAssociateToolTipToControl(IDD_INTERCEPT_COPY_PASTE, hDlg, IDS_TOOLTIP_INTERCEPT_COPY_PASTE); // initialize cursor radio buttons - if (gpStateInfo->CursorSize <= 25) - { - Item = IDD_CURSOR_SMALL; - } - else if (gpStateInfo->CursorSize <= 50) - { - Item = IDD_CURSOR_MEDIUM; - } - else - { - Item = IDD_CURSOR_LARGE; - } - CheckRadioButton(hDlg, IDD_CURSOR_SMALL, IDD_CURSOR_LARGE, Item); + InitializeCursorSize(hDlg); SetDlgItemInt(hDlg, IDD_HISTORY_SIZE, gpStateInfo->HistoryBufferSize, FALSE); SendDlgItemMessage(hDlg, IDD_HISTORY_SIZE, EM_LIMITTEXT, 3, 0); diff --git a/src/propsheet/OptionsPage.h b/src/propsheet/OptionsPage.h index 21ab26cbe63..d09f1003711 100644 --- a/src/propsheet/OptionsPage.h +++ b/src/propsheet/OptionsPage.h @@ -6,7 +6,7 @@ Module Name: - OptionsPage.h Abstract: -- This module contains the definitions for console options dialog. +- This module contains the definitions for console options dialog. Author(s): Mike Griese (migrie) Oct-2016 @@ -16,3 +16,4 @@ Author(s): void ToggleV2OptionsControls(__in const HWND hDlg); INT_PTR WINAPI SettingsDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam); +void InitializeCursorSize(const HWND hOptionsDlg); diff --git a/src/propsheet/TerminalPage.cpp b/src/propsheet/TerminalPage.cpp index f7e5b0a9dbc..5f645399494 100644 --- a/src/propsheet/TerminalPage.cpp +++ b/src/propsheet/TerminalPage.cpp @@ -3,6 +3,7 @@ #include "precomp.h" #include "TerminalPage.h" +#include "OptionsPage.h" // For InitializeCursorSize #include "ColorControl.h" #include @@ -323,10 +324,22 @@ bool TerminalDlgCommand(const HWND hDlg, const WORD item, const WORD command) no case IDD_TERMINAL_UNDERSCORE: case IDD_TERMINAL_EMPTYBOX: case IDD_TERMINAL_SOLIDBOX: + { gpStateInfo->CursorType = item - IDD_TERMINAL_LEGACY_CURSOR; UpdateApplyButton(hDlg); + + // See GH#1219 - When the cursor state is something other than legacy, + // we need to manually check the "IDD_CURSOR_ADVANCED" radio button on + // the Options page. This will prevent the Options page from manually + // resetting the cursor to legacy. + if (g_hOptionsDlg != INVALID_HANDLE_VALUE) + { + InitializeCursorSize(g_hOptionsDlg); + } + handled = true; break; + } case IDD_DISABLE_SCROLLFORWARD: gpStateInfo->TerminalScrolling = IsDlgButtonChecked(hDlg, IDD_DISABLE_SCROLLFORWARD); UpdateApplyButton(hDlg); diff --git a/src/propsheet/console.rc b/src/propsheet/console.rc index 8ab505a6ede..dd7bc9df01e 100644 --- a/src/propsheet/console.rc +++ b/src/propsheet/console.rc @@ -42,6 +42,8 @@ BEGIN WS_TABSTOP | WS_GROUP AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10, AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10, + // IDD_CURSOR_ADVANCED is a hidden control, see GH#1219 + AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0, GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP LTEXT "&Buffer Size:", -1, 119, 25, 78, 9 @@ -106,6 +108,8 @@ BEGIN WS_TABSTOP | WS_GROUP AUTORADIOBUTTON "&Medium", IDD_CURSOR_MEDIUM, 14, 33, 84, 10, AUTORADIOBUTTON "&Large", IDD_CURSOR_LARGE, 14, 43, 84, 10, + // IDD_CURSOR_ADVANCED is a hidden control, see GH#1219 + AUTORADIOBUTTON "", IDD_CURSOR_ADVANCED, 14, 53, 0, 0, GROUPBOX "Command History", -1, 115, 11, 120, 56, WS_GROUP LTEXT "&Buffer Size:", -1, 119, 25, 78, 9 diff --git a/src/propsheet/dialogs.h b/src/propsheet/dialogs.h index 763aff876ce..952e2ff9a08 100644 --- a/src/propsheet/dialogs.h +++ b/src/propsheet/dialogs.h @@ -43,6 +43,7 @@ Revision History: #define IDD_LANGUAGE_GROUPBOX 116 #define DID_SETTINGS_COMCTL5 117 #define DID_SETTINGS2_COMCTL5 118 +#define IDD_CURSOR_ADVANCED 119 #define DID_FONTDLG 200 #define IDD_STATIC 201 diff --git a/src/propsheet/globals.cpp b/src/propsheet/globals.cpp index a47b0d4bf24..b7c59171cc9 100644 --- a/src/propsheet/globals.cpp +++ b/src/propsheet/globals.cpp @@ -55,3 +55,4 @@ COLORREF g_fakeBackgroundColor = RGB(12, 12, 12); // Default black COLORREF g_fakeCursorColor = RGB(242, 242, 242); // Default bright white HWND g_hTerminalDlg = static_cast(INVALID_HANDLE_VALUE); +HWND g_hOptionsDlg = static_cast(INVALID_HANDLE_VALUE); diff --git a/src/propsheet/globals.h b/src/propsheet/globals.h index 8a75fb1acc1..0d1b2adfc84 100644 --- a/src/propsheet/globals.h +++ b/src/propsheet/globals.h @@ -53,3 +53,4 @@ extern COLORREF g_fakeBackgroundColor; extern COLORREF g_fakeCursorColor; extern HWND g_hTerminalDlg; +extern HWND g_hOptionsDlg; From bac69f7cabec6068868034345e2aea22a9533696 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 9 Sep 2019 10:06:50 -0500 Subject: [PATCH 144/154] When inserting/deleting lines, preserve RGB/256 attributes (#2668) * This fixes #832 by not mucking with roundtripping attributes. Still needs a test * Add a test * Lets just make this test test everything @miniksa https://media0.giphy.com/media/d7mMzaGDYkz4ZBziP6/giphy.gif * Remove dead code --- src/host/getset.cpp | 25 +++++-- src/host/ut_host/ScreenBufferTests.cpp | 99 ++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 6 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index f576c1d9414..0c6cad8ff57 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -2055,12 +2055,25 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert) SMALL_RECT srClip = screenEdges; srClip.Top = cursorPosition.Y; - LOG_IF_FAILED(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo, - srScroll, - coordDestination, - srClip, - UNICODE_SPACE, - screenInfo.GetAttributes().GetLegacyAttributes())); + // Here we previously called to ScrollConsoleScreenBufferWImpl to + // perform the scrolling operation. However, that function only accepts + // a WORD for the fill attributes. That means we'd lose 256/RGB fidelity + // for fill attributes. So instead, we'll just call ScrollRegion + // ourselves, with the same params that ScrollConsoleScreenBufferWImpl + // would have. + // See microsoft/terminal#832 for more context. + try + { + LockConsole(); + auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); + ScrollRegion(screenInfo, + srScroll, + srClip, + coordDestination, + UNICODE_SPACE, + screenInfo.GetAttributes()); + } + CATCH_LOG(); } } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index d8caf73577f..38d4faf2684 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -167,6 +167,8 @@ class ScreenBufferTests TEST_METHOD(DeleteLinesInMargins); TEST_METHOD(ReverseLineFeedInMargins); + TEST_METHOD(InsertDeleteLines256Colors); + TEST_METHOD(SetOriginMode); TEST_METHOD(HardResetBuffer); @@ -3453,6 +3455,103 @@ void ScreenBufferTests::ReverseLineFeedInMargins() } } +void ScreenBufferTests::InsertDeleteLines256Colors() +{ + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:insert", L"{false, true}") + TEST_METHOD_PROPERTY(L"Data:colorStyle", L"{0, 1, 2}") + END_TEST_METHOD_PROPERTIES(); + + // colorStyle will be used to control whether we use a color from the 16 + // color table, a color from the 256 color table, or a pure RGB color. + const int Use16Color = 0; + const int Use256Color = 1; + const int UseRGBColor = 2; + + bool insert; + int colorStyle; + VERIFY_SUCCEEDED(TestData::TryGetValue(L"insert", insert), L"whether to insert(true) or delete(false) lines"); + VERIFY_SUCCEEDED(TestData::TryGetValue(L"colorStyle", colorStyle), L"controls whether to use the 16 color table, 256 table, or RGB colors"); + + // This test is largely taken from repro code from + // https://github.com/microsoft/terminal/issues/832#issuecomment-507447272 + Log::Comment( + L"Sets the attributes to a 256/RGB color, then scrolls some lines with" + L" DL. Verifies the rows are cleared with the attributes we'd expect."); + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& tbi = si.GetTextBuffer(); + auto& stateMachine = si.GetStateMachine(); + auto& cursor = si.GetTextBuffer().GetCursor(); + + TextAttribute expectedAttr{ si.GetAttributes() }; + std::wstring sgrSeq = L"\x1b[48;5;2m"; + if (colorStyle == Use16Color) + { + expectedAttr.SetBackground(gci.GetColorTableEntry(2)); + } + else if (colorStyle == Use256Color) + { + expectedAttr.SetBackground(gci.GetColorTableEntry(20)); + sgrSeq = L"\x1b[48;5;20m"; + } + else if (colorStyle == UseRGBColor) + { + expectedAttr.SetBackground(RGB(1, 2, 3)); + sgrSeq = L"\x1b[48;2;1;2;3m"; + } + + // Set some scrolling margins + stateMachine.ProcessString(L"\x1b[1;3r"); + + // Set the BG color to the table index 2, as a 256-color sequence + stateMachine.ProcessString(sgrSeq); + + VERIFY_ARE_EQUAL(expectedAttr, si.GetAttributes()); + + // Move to home + stateMachine.ProcessString(L"\x1b[H"); + + // Insert/Delete 10 lines + stateMachine.ProcessString(insert ? L"\x1b[10L" : L"\x1b[10M"); + + Log::Comment(NoThrowString().Format( + L"cursor=%s", VerifyOutputTraits::ToString(cursor.GetPosition()).GetBuffer())); + Log::Comment(NoThrowString().Format( + L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); + + VERIFY_ARE_EQUAL(0, cursor.GetPosition().X); + VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y); + + stateMachine.ProcessString(L"foo"); + Log::Comment(NoThrowString().Format( + L"cursor=%s", VerifyOutputTraits::ToString(cursor.GetPosition()).GetBuffer())); + VERIFY_ARE_EQUAL(3, cursor.GetPosition().X); + VERIFY_ARE_EQUAL(0, cursor.GetPosition().Y); + { + auto iter00 = tbi.GetCellDataAt({ 0, 0 }); + auto iter10 = tbi.GetCellDataAt({ 1, 0 }); + auto iter20 = tbi.GetCellDataAt({ 2, 0 }); + auto iter30 = tbi.GetCellDataAt({ 3, 0 }); + auto iter01 = tbi.GetCellDataAt({ 0, 1 }); + auto iter02 = tbi.GetCellDataAt({ 0, 2 }); + VERIFY_ARE_EQUAL(L"f", iter00->Chars()); + VERIFY_ARE_EQUAL(L"o", iter10->Chars()); + VERIFY_ARE_EQUAL(L"o", iter20->Chars()); + VERIFY_ARE_EQUAL(L"\x20", iter30->Chars()); + VERIFY_ARE_EQUAL(L"\x20", iter01->Chars()); + VERIFY_ARE_EQUAL(L"\x20", iter02->Chars()); + + VERIFY_ARE_EQUAL(expectedAttr, iter00->TextAttr()); + VERIFY_ARE_EQUAL(expectedAttr, iter10->TextAttr()); + VERIFY_ARE_EQUAL(expectedAttr, iter20->TextAttr()); + VERIFY_ARE_EQUAL(expectedAttr, iter30->TextAttr()); + VERIFY_ARE_EQUAL(expectedAttr, iter01->TextAttr()); + VERIFY_ARE_EQUAL(expectedAttr, iter02->TextAttr()); + } +} + void ScreenBufferTests::SetOriginMode() { auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); From 18bacfe973e5cefafe29e20c42bb8ea20b11c36b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Mon, 9 Sep 2019 16:01:28 -0700 Subject: [PATCH 145/154] A few PR comments. A constexpr here, a misleading comment there, and an extraneous local. --- src/buffer/out/OutputCell.cpp | 4 +--- src/renderer/dx/DxRenderer.cpp | 2 +- src/types/convert.cpp | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/buffer/out/OutputCell.cpp b/src/buffer/out/OutputCell.cpp index 456202fb23a..ad30b996bdb 100644 --- a/src/buffer/out/OutputCell.cpp +++ b/src/buffer/out/OutputCell.cpp @@ -111,7 +111,5 @@ void OutputCell::_setFromOutputCellView(const OutputCellView& cell) _dbcsAttribute = cell.DbcsAttr(); _textAttribute = cell.TextAttr(); _behavior = cell.TextAttrBehavior(); - - const auto view = cell.Chars(); - _text = view; + _text = cell.Chars(); } diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index a3d5eab2701..e8cb5589de6 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -15,7 +15,7 @@ #pragma hdrstop static constexpr float POINTS_PER_INCH = 72.0f; -static std::wstring_view FALLBACK_FONT_FACE = L"Consolas"; +static constexpr std::wstring_view FALLBACK_FONT_FACE = L"Consolas"; static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; using namespace Microsoft::Console::Render; diff --git a/src/types/convert.cpp b/src/types/convert.cpp index b42431dcd61..c2049ded918 100644 --- a/src/types/convert.cpp +++ b/src/types/convert.cpp @@ -44,7 +44,7 @@ static const WORD leftShiftScanCode = 0x2A; size_t cchNeeded; THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded)); - // Allocate ourselves space in a smart pointer. + // Allocate ourselves some space std::wstring out; out.resize(cchNeeded); @@ -85,7 +85,7 @@ static const WORD leftShiftScanCode = 0x2A; size_t cchNeeded; THROW_IF_FAILED(IntToSizeT(iTarget, &cchNeeded)); - // Allocate ourselves space in a smart pointer + // Allocate ourselves some space std::string out; out.resize(cchNeeded); From 2ac24979da6eada2d5af758e6521e649600d5510 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 10 Sep 2019 10:29:31 -0600 Subject: [PATCH 146/154] Stylus Selection Support (#2586) --- src/cascadia/TerminalControl/TermControl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 910365c0dbe..3aade82053b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -732,7 +732,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); const auto point = args.GetCurrentPoint(_root); - if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) { // Ignore mouse events while the terminal does not have focus. // This prevents the user from selecting and copying text if they @@ -818,7 +818,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); const auto point = args.GetCurrentPoint(_root); - if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) { if (point.Properties().IsLeftButtonPressed()) { @@ -897,7 +897,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation const auto ptr = args.Pointer(); - if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse) + if (ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Mouse || ptr.PointerDeviceType() == Windows::Devices::Input::PointerDeviceType::Pen) { const auto modifiers = static_cast(args.KeyModifiers()); // static_cast to a uint32_t because we can't use the WI_IsFlagSet From 2063197605bd5523cbdfb7948f558e00cd86ebf3 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Tue, 10 Sep 2019 15:56:50 -0700 Subject: [PATCH 147/154] Add SECURITY.md to repo (#2720) Open Source program office guidelines encourage us to add this information to our repository. So I'm doing it. Sourced from https://github.com/microsoft/microsoft.github.io/blob/master/SECURITY.MD --- SECURITY.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..6cda10b000d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). + + From 2da3b49c9eeab17078558686cd132eedbb49a5bd Mon Sep 17 00:00:00 2001 From: Fredi Machado Date: Wed, 11 Sep 2019 09:08:15 +1000 Subject: [PATCH 148/154] Fix json settings documentation (#2699) * Fix json settings documentation The ctrl+c issue was fixed in [#2446](https://github.com/microsoft/terminal/pull/2446) * Update UsingJsonSettings.md --- doc/user-docs/UsingJsonSettings.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user-docs/UsingJsonSettings.md b/doc/user-docs/UsingJsonSettings.md index 7a281cddb5a..537bdd82bd7 100644 --- a/doc/user-docs/UsingJsonSettings.md +++ b/doc/user-docs/UsingJsonSettings.md @@ -171,5 +171,5 @@ You can even set multiple keybindings for a single action if you'd like. For exa will bind both ctrl+shift+v and shift+Insert to `paste`. -Note: If you set your copy keybinding to `"ctrl+c"`, you won't be able to send an interrupt to the commandline application using Ctrl+C. This is a bug, and being tracked by [#2258](https://github.com/microsoft/terminal/issues/2285). +Note: If you set your copy keybinding to `"ctrl+c"`, you'll only be able to send an interrupt to the commandline application using Ctrl+C when there's no text selection. Additionally, if you set `paste` to `"ctrl+v"`, commandline applications won't be able to read a ctrl+v from the input. For these reasons, we suggest `"ctrl+shift+c"` and `"ctrl+shift+v"` From 12d2e170dd0ff0e23c956a754a308c4ce3f0c1f8 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Wed, 11 Sep 2019 02:20:46 +0100 Subject: [PATCH 149/154] Correct the boundaries of the scrolling commands (#2505) There are a number of VT escape sequences that rely on the `ScrollRegion` function to scroll the viewport (RI, DL, IL, SU, SD, ICH, and DCH) , and all of them have got the clipping rect or scroll boundaries wrong in some way, resulting in content being scrolled off the screen that should have been clipped, revealed areas not being correctly filled, or parts of the screen not being moved that should have been. This PR attempts to fix all of those issues. The `ScrollRegion` function is what ultimately handles the scrolling, but it's typically called via the `ApiRoutines::ScrollConsoleScreenBufferWImpl` method, and it's the callers of that method that have needed correcting. One "mistake" that many of these operations made, was in setting a clipping rect that was different from the scrolling rect. This should never have been necessary, since the area being scrolled is also the boundary into which the content needs to be clipped, so the easiest thing to do is just use the same rect for both parameters. Another common mistake was in clipping the horizontal boundaries to the width of the viewport. But it's really the buffer width that represents the active width of the screen - the viewport width and offset are merely a window on that active area. As such, the viewport should only be used to clip vertically - the horizontal extent should typically be the full buffer width. On that note, there is really no need to actually calculate the buffer width when we want to set any of the scrolling parameters to that width. The `ScrollRegion` function already takes care of clipping everything within the buffer boundary, so we can simply set the `Left` of the rect to `0` and the `Right` to `SHORT_MAX`. More details on individual commands: * RI (the `DoSrvPrivateReverseLineFeed` function) This now uses a single rect for both the scroll region and clipping boundary, and the width is set to `SHORT_MAX` to cover the full buffer width. Also the bottom of the scrolling region is now the bottom of the viewport (rather than bottom-1), otherwise it would be off by one. * DL and IL (the `DoSrvPrivateModifyLinesImpl` function) Again this uses a single rect for both the scroll region and clipping boundary, and the width is set to `SHORT_MAX` to cover the full width. The most significant change, though, is that the bottom boundary is now the viewport bottom rather than the buffer bottom. Using the buffer bottom prevented it clipping the content that scrolled off screen when inserting, and failed to fill the revealed area when deleting. * SU and SD (the `AdaptDispatch::_ScrollMovement` method) This was already using a single rect for both the scroll region and clipping boundary, but it was previously constrained to the width of the viewport rather than the buffer width, so some areas of the screen weren't correctly scrolled. Also, the bottom boundary was off by 1, because it was using an exclusive rect while the `ScrollRegion` function expects inclusive rects. * ICH and DCH (the `AdaptDispatch::_InsertDeleteHelper` method) This method has been considerably simplified, because it was reimplementing a lot of functionality that was already provided by the `ScrollRegion` function. And like many of the other cases, it has been updated to use a single rect for both the scroll region and clipping boundary, and clip to the full buffer width rather than the viewport width. I should add that if we were following the specs exactly, then the SU and SD commands should technically be panning the viewport over the buffer instead of moving the buffer contents within the viewport boundary. So SU would be the equivalent of a newline at the bottom of the viewport (assuming no margins). And SD would assumedly do the opposite, scrolling the back buffer back into view (an RI at the top of the viewport should do the same). This doesn't seem to be something that is consistently implemented, though. Some terminals do implement SU as a viewport pan, but I haven't seen anyone implement SD or RI as a pan. If we do want to do something about this, I think it's best addressed as a separate issue. ## Validation Steps Performed There were already existing tests for the SU, SD, ICH, and DCH commands, but they were implemented as adapter tests, which weren't effectively testing anything - the `ScrollConsoleScreenBufferW` method used in those tests was just a mock (an incomplete reimplementation of the `ScrollRegion` function), so confirming that the mock produced the correct result told you nothing about the validity of the real code. To address that, I've now reimplemented those adapter tests as screen buffer tests. For the most part I've tried to duplicate the functionality of the original tests, but there are significant differences to account for the fact that scrolling region now covers the full width of the buffer rather than just the viewport width. I've also extended those tests with additional coverage for the RI, DL, and IL commands, which are really just a variation of the SU and SD functionality. Closes #2174 --- src/host/getset.cpp | 25 +- src/host/ut_host/ScreenBufferTests.cpp | 497 ++++++++++++ src/terminal/adapter/adaptDispatch.cpp | 97 +-- .../adapter/ut_adapter/adapterTest.cpp | 733 +----------------- 4 files changed, 525 insertions(+), 827 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 0c6cad8ff57..dbd584c218e 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -1356,24 +1356,22 @@ void DoSrvPrivateAllowCursorBlinking(SCREEN_INFORMATION& screenInfo, const bool if (screenInfo.IsCursorInMargins(oldCursorPosition)) { // Cursor is at the top of the viewport - const COORD bufferSize = screenInfo.GetBufferSize().Dimensions(); - // Rectangle to cut out of the existing buffer + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. SMALL_RECT srScroll; srScroll.Left = 0; - srScroll.Right = bufferSize.X; + srScroll.Right = SHORT_MAX; srScroll.Top = viewport.Top; - srScroll.Bottom = viewport.Bottom - 1; + srScroll.Bottom = viewport.Bottom; // Paste coordinate for cut text above COORD coordDestination; coordDestination.X = 0; coordDestination.Y = viewport.Top + 1; - SMALL_RECT srClip = viewport; - Status = NTSTATUS_FROM_HRESULT(ServiceLocator::LocateGlobals().api.ScrollConsoleScreenBufferWImpl(screenInfo, srScroll, coordDestination, - srClip, + srScroll, UNICODE_SPACE, screenInfo.GetAttributes().GetLegacyAttributes())); } @@ -2033,13 +2031,13 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert) const auto cursorPosition = textBuffer.GetCursor().GetPosition(); if (screenInfo.IsCursorInMargins(cursorPosition)) { - const auto screenEdges = screenInfo.GetBufferSize().ToInclusive(); - // Rectangle to cut out of the existing buffer + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. SMALL_RECT srScroll; srScroll.Left = 0; - srScroll.Right = screenEdges.Right - screenEdges.Left; + srScroll.Right = SHORT_MAX; srScroll.Top = cursorPosition.Y; - srScroll.Bottom = screenEdges.Bottom; + srScroll.Bottom = screenInfo.GetViewport().BottomInclusive(); // Paste coordinate for cut text above COORD coordDestination; coordDestination.X = 0; @@ -2052,9 +2050,6 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert) coordDestination.Y = (cursorPosition.Y) - gsl::narrow(count); } - SMALL_RECT srClip = screenEdges; - srClip.Top = cursorPosition.Y; - // Here we previously called to ScrollConsoleScreenBufferWImpl to // perform the scrolling operation. However, that function only accepts // a WORD for the fill attributes. That means we'd lose 256/RGB fidelity @@ -2068,7 +2063,7 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert) auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); ScrollRegion(screenInfo, srScroll, - srClip, + srScroll, coordDestination, UNICODE_SPACE, screenInfo.GetAttributes()); diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 38d4faf2684..ff3d34988f9 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -161,6 +161,10 @@ class ScreenBufferTests TEST_METHOD(DontResetColorsAboveVirtualBottom); + TEST_METHOD(ScrollOperations); + TEST_METHOD(InsertChars); + TEST_METHOD(DeleteChars); + TEST_METHOD(ScrollUpInMargins); TEST_METHOD(ScrollDownInMargins); TEST_METHOD(InsertLinesInMargins); @@ -3057,6 +3061,499 @@ void ScreenBufferTests::DontResetColorsAboveVirtualBottom() } } +template +void _FillLine(COORD position, T fillContent, TextAttribute fillAttr) +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto& row = si.GetTextBuffer().GetRowByOffset(position.Y); + row.WriteCells({ fillContent, fillAttr }, position.X, false); +} + +template +void _FillLine(int line, T fillContent, TextAttribute fillAttr) +{ + _FillLine({ 0, gsl::narrow(line) }, fillContent, fillAttr); +} + +template +void _FillLines(int startLine, int endLine, T fillContent, TextAttribute fillAttr) +{ + for (auto line = startLine; line < endLine; ++line) + { + _FillLine(line, fillContent, fillAttr); + } +} + +template +bool _ValidateLineContains(COORD position, T expectedContent, TextAttribute expectedAttr) +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto actual = si.GetCellLineDataAt(position); + auto expected = OutputCellIterator{ expectedContent, expectedAttr }; + while (actual && expected) + { + if (actual->Chars() != expected->Chars() || actual->TextAttr() != expected->TextAttr()) + { + return false; + } + ++actual; + ++expected; + } + return true; +}; + +template +bool _ValidateLineContains(int line, T expectedContent, TextAttribute expectedAttr) +{ + return _ValidateLineContains({ 0, gsl::narrow(line) }, expectedContent, expectedAttr); +} + +template +auto _ValidateLinesContain(int startLine, int endLine, T expectedContent, TextAttribute expectedAttr) +{ + for (auto line = startLine; line < endLine; ++line) + { + if (!_ValidateLineContains(line, expectedContent, expectedAttr)) + { + return false; + } + } + return true; +}; + +void ScreenBufferTests::ScrollOperations() +{ + enum ScrollType : int + { + ScrollUp, + ScrollDown, + InsertLine, + DeleteLine, + ReverseIndex + }; + enum ScrollDirection : int + { + Up, + Down + }; + + ScrollType scrollType; + ScrollDirection scrollDirection; + int scrollMagnitude; + + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:scrollType", L"{0, 1, 2, 3, 4}") + TEST_METHOD_PROPERTY(L"Data:scrollMagnitude", L"{1, 2, 5}") + END_TEST_METHOD_PROPERTIES() + + VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollType", (int&)scrollType)); + VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollMagnitude", scrollMagnitude)); + + std::wstringstream escapeSequence; + switch (scrollType) + { + case ScrollUp: + Log::Comment(L"Testing scroll up (SU)."); + escapeSequence << "\x1b[" << scrollMagnitude << "S"; + scrollDirection = Up; + break; + case ScrollDown: + Log::Comment(L"Testing scroll down (SD)."); + escapeSequence << "\x1b[" << scrollMagnitude << "T"; + scrollDirection = Down; + break; + case InsertLine: + Log::Comment(L"Testing insert line (IL)."); + escapeSequence << "\x1b[" << scrollMagnitude << "L"; + scrollDirection = Down; + break; + case DeleteLine: + Log::Comment(L"Testing delete line (DL)."); + escapeSequence << "\x1b[" << scrollMagnitude << "M"; + scrollDirection = Up; + break; + case ReverseIndex: + Log::Comment(L"Testing reverse index (RI)."); + for (auto i = 0; i < scrollMagnitude; ++i) + { + escapeSequence << "\x1bM"; + } + scrollDirection = Down; + break; + default: + VERIFY_FAIL(); + return; + } + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto& stateMachine = si.GetStateMachine(); + const auto& cursor = si.GetTextBuffer().GetCursor(); + WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + const auto bufferWidth = si.GetBufferSize().Width(); + const auto bufferHeight = si.GetBufferSize().Height(); + + // Move the viewport down a few lines, and only cover part of the buffer width. + si.SetViewport(Viewport::FromDimensions({ 5, 10 }, { bufferWidth - 10, 10 }), true); + const auto viewportStart = si.GetViewport().Top(); + const auto viewportEnd = si.GetViewport().BottomExclusive(); + + // Fill the entire buffer with Zs. Blue on Green. + const auto bufferChar = L'Z'; + const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN }; + _FillLines(0, bufferHeight, bufferChar, bufferAttr); + + // Fill the viewport with a range of letters to see if they move. Red on Blue. + const auto viewportAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE }; + auto viewportChar = L'A'; + auto viewportLine = viewportStart; + while (viewportLine < viewportEnd) + { + _FillLine(viewportLine++, viewportChar++, viewportAttr); + } + + // Set the background color so that it will be used to fill the revealed area. + si.SetAttributes({ BACKGROUND_RED }); + + // Place the cursor in the center. + auto cursorPos = COORD{ bufferWidth / 2, (viewportStart + viewportEnd) / 2 }; + // Unless this is reverse index, which has to be be at the top of the viewport. + if (scrollType == ReverseIndex) + { + cursorPos.Y = viewportStart; + } + + Log::Comment(L"Set the cursor position and perform the operation."); + VERIFY_SUCCEEDED(si.SetCursorPosition(cursorPos, true)); + stateMachine.ProcessString(escapeSequence.str()); + + Log::Comment(L"Verify cursor didn't move."); + VERIFY_ARE_EQUAL(cursorPos, cursor.GetPosition()); + + Log::Comment(L"Field of Zs outside viewport should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLinesContain(0, viewportStart, bufferChar, bufferAttr)); + VERIFY_IS_TRUE(_ValidateLinesContain(viewportEnd, bufferHeight, bufferChar, bufferAttr)); + + // Depending on the direction of scrolling, lines are either deleted or inserted. + const auto deletedLines = scrollDirection == Up ? scrollMagnitude : 0; + const auto insertedLines = scrollDirection == Down ? scrollMagnitude : 0; + + // Insert and delete operations only scroll the viewport below the cursor position. + const auto scrollStart = (scrollType == InsertLine || scrollType == DeleteLine) ? cursorPos.Y : viewportStart; + + // Reset the viewport character and line number for the verification loop. + viewportChar = L'A'; + viewportLine = viewportStart; + + Log::Comment(L"Lines above the scrolled area should remain unchanged."); + while (viewportLine < scrollStart) + { + VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr)); + } + + Log::Comment(L"Scrolled area should have moved up/down by given magnitude."); + viewportChar += gsl::narrow(deletedLines); // Characters dropped when deleting + viewportLine += gsl::narrow(insertedLines); // Lines skipped when inserting + while (viewportLine < viewportEnd - deletedLines) + { + VERIFY_IS_TRUE(_ValidateLineContains(viewportLine++, viewportChar++, viewportAttr)); + } + + Log::Comment(L"The revealed area should now be blank, with default buffer attributes."); + const auto revealedStart = scrollDirection == Up ? viewportEnd - deletedLines : scrollStart; + const auto revealedEnd = revealedStart + scrollMagnitude; + VERIFY_IS_TRUE(_ValidateLinesContain(revealedStart, revealedEnd, L' ', si.GetAttributes())); +} + +void ScreenBufferTests::InsertChars() +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto& stateMachine = si.GetStateMachine(); + WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + // Set the buffer width to 40, with a centered viewport of 20. + const auto bufferWidth = 40; + const auto bufferHeight = si.GetBufferSize().Height(); + const auto viewportStart = 10; + const auto viewportEnd = viewportStart + 20; + VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false)); + si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true); + + Log::Comment( + L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. " + L"Then insert 5 spaces at the cursor. Watch spaces get inserted, text slides right " + L"out of the viewport, pushing some of the Qs out of the buffer."); + + const auto insertLine = SHORT{ 10 }; + auto insertPos = SHORT{ 20 }; + + // Place the cursor in the center of the line. + VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true)); + + // Save the cursor position. It shouldn't move for the rest of the test. + const auto& cursor = si.GetTextBuffer().GetCursor(); + auto expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + const auto bufferChar = L'Q'; + const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN }; + _FillLine(insertLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + const auto textChars = L"ABCDEFGHIJKLMNOPQRST"; + const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE }; + _FillLine({ viewportStart, insertLine }, textChars, textAttr); + + // Set the background color so that it will be used to fill the revealed area. + si.SetAttributes({ BACKGROUND_RED }); + + // Insert 5 spaces at the cursor position. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: QQQQQQQQQQABCDEFGHIJ KLMNOPQRSTQQQQQ + Log::Comment(L"Inserting 5 spaces in the middle of the line."); + auto before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + stateMachine.ProcessString(L"\x1b[5@"); + auto after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr), + L"Field of Qs left of the viewport should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJ", textAttr), + L"First half of the alphabet should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()), + L"Spaces should be inserted with the current attributes at the cursor position."); + VERIFY_IS_TRUE(_ValidateLineContains({ insertPos + 5, insertLine }, L"KLMNOPQRST", textAttr), + L"Second half of the alphabet should have moved to the right by the number of spaces inserted."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd + 5, insertLine }, L"QQQQQ", bufferAttr), + L"Field of Qs right of the viewport should be moved right, half pushed outside the buffer."); + + Log::Comment( + L"Test 2: Inserting at the exact end of the line. Same line structure. " + L"Move cursor to right edge of window and insert > 1 space. " + L"Only 1 should be inserted, everything else unchanged."); + + // Move cursor to right edge. + insertPos = bufferWidth - 1; + VERIFY_SUCCEEDED(si.SetCursorPosition({ insertPos, insertLine }, true)); + expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + _FillLine(insertLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + _FillLine({ viewportStart, insertLine }, textChars, textAttr); + + // Set the background color so that it will be used to fill the revealed area. + si.SetAttributes({ BACKGROUND_RED }); + + // Insert 5 spaces at the right edge. Only 1 should be inserted. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ + Log::Comment(L"Inserting 5 spaces at the right edge of the buffer."); + before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + stateMachine.ProcessString(L"\x1b[5@"); + after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains({ 0, insertLine }, L"QQQQQQQQQQ", bufferAttr), + L"Field of Qs left of the viewport should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, insertLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr), + L"Entire viewport range should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, insertLine }, L"QQQQQQQQQ", bufferAttr), + L"Field of Qs right of the viewport should remain unchanged except for the last spot."); + VERIFY_IS_TRUE(_ValidateLineContains({ insertPos, insertLine }, L" ", si.GetAttributes()), + L"One space should be inserted with the current attributes at the cursor postion."); + + Log::Comment( + L"Test 3: Inserting at the exact beginning of the line. Same line structure. " + L"Move cursor to left edge of buffer and insert > buffer width of space. " + L"The whole row should be replaced with spaces."); + + // Move cursor to left edge. + VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, insertLine }, true)); + expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + _FillLine(insertLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + _FillLine({ viewportStart, insertLine }, textChars, textAttr); + + // Insert greater than the buffer width at the left edge. The entire line should be erased. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: + Log::Comment(L"Inserting 100 spaces at the left edge of the buffer."); + before = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + stateMachine.ProcessString(L"\x1b[100@"); + after = si.GetTextBuffer().GetRowByOffset(insertLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from insert operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains(insertLine, L' ', si.GetAttributes()), + L"A whole line of spaces was inserted at the start, erasing the line."); +} + +void ScreenBufferTests::DeleteChars() +{ + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer().GetActiveBuffer(); + auto& stateMachine = si.GetStateMachine(); + WI_SetFlag(si.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + // Set the buffer width to 40, with a centered viewport of 20. + const auto bufferWidth = 40; + const auto bufferHeight = si.GetBufferSize().Height(); + const auto viewportStart = 10; + const auto viewportEnd = viewportStart + 20; + VERIFY_SUCCEEDED(si.ResizeScreenBuffer({ bufferWidth, bufferHeight }, false)); + si.SetViewport(Viewport::FromExclusive({ viewportStart, 0, viewportEnd, 25 }), true); + + Log::Comment( + L"Test 1: Fill the line with Qs. Write some text within the viewport boundaries. " + L"Then delete 5 characters at the cursor. Watch the rest of the line slide left, " + L"replacing the deleted characters, with spaces inserted at the end of the line."); + + const auto deleteLine = SHORT{ 10 }; + auto deletePos = SHORT{ 20 }; + + // Place the cursor in the center of the line. + VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true)); + + // Save the cursor position. It shouldn't move for the rest of the test. + const auto& cursor = si.GetTextBuffer().GetCursor(); + auto expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + const auto bufferChar = L'Q'; + const auto bufferAttr = TextAttribute{ FOREGROUND_BLUE | BACKGROUND_GREEN }; + _FillLine(deleteLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + const auto textChars = L"ABCDEFGHIJKLMNOPQRST"; + const auto textAttr = TextAttribute{ FOREGROUND_RED | BACKGROUND_BLUE }; + _FillLine({ viewportStart, deleteLine }, textChars, textAttr); + + // Set the background color so that it will be used to fill the revealed area. + si.SetAttributes({ BACKGROUND_RED }); + + // Delete 5 characters at the cursor position. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: QQQQQQQQQQABCDEFGHIJPQRSTQQQQQQQQQQ + Log::Comment(L"Deleting 5 characters in the middle of the line."); + auto before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + stateMachine.ProcessString(L"\x1b[5P"); + auto after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr), + L"Field of Qs left of the viewport should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJ", textAttr), + L"First half of the alphabet should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L"PQRST", textAttr), + L"Only half of the second part of the alphabet remains."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd - 5, deleteLine }, L"QQQQQQQQQQ", bufferAttr), + L"Field of Qs right of the viewport should be moved left."); + VERIFY_IS_TRUE(_ValidateLineContains({ bufferWidth - 5, deleteLine }, L" ", si.GetAttributes()), + L"The rest of the line should be replaced with spaces with the current attributes."); + + Log::Comment( + L"Test 2: Deleting at the exact end of the line. Same line structure. " + L"Move cursor to right edge of window and delete > 1 character. " + L"Only 1 should be deleted, everything else unchanged."); + + // Move cursor to right edge. + deletePos = bufferWidth - 1; + VERIFY_SUCCEEDED(si.SetCursorPosition({ deletePos, deleteLine }, true)); + expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + _FillLine(deleteLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + _FillLine({ viewportStart, deleteLine }, textChars, textAttr); + + // Set the background color so that it will be used to fill the revealed area. + si.SetAttributes({ BACKGROUND_RED }); + + // Delete 5 characters at the right edge. Only 1 should be deleted. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQ + Log::Comment(L"Deleting 5 characters at the right edge of the buffer."); + before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + stateMachine.ProcessString(L"\x1b[5P"); + after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains({ 0, deleteLine }, L"QQQQQQQQQQ", bufferAttr), + L"Field of Qs left of the viewport should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportStart, deleteLine }, L"ABCDEFGHIJKLMNOPQRST", textAttr), + L"Entire viewport range should remain unchanged."); + VERIFY_IS_TRUE(_ValidateLineContains({ viewportEnd, deleteLine }, L"QQQQQQQQQ", bufferAttr), + L"Field of Qs right of the viewport should remain unchanged except for the last spot."); + VERIFY_IS_TRUE(_ValidateLineContains({ deletePos, deleteLine }, L" ", si.GetAttributes()), + L"One character should be erased with the current attributes at the cursor postion."); + + Log::Comment( + L"Test 3: Deleting at the exact beginning of the line. Same line structure. " + L"Move cursor to left edge of buffer and delete > buffer width of characters. " + L"The whole row should be replaced with spaces."); + + // Move cursor to left edge. + VERIFY_SUCCEEDED(si.SetCursorPosition({ 0, deleteLine }, true)); + expectedCursor = cursor.GetPosition(); + + // Fill the entire line with Qs. Blue on Green. + _FillLine(deleteLine, bufferChar, bufferAttr); + + // Fill the viewport range with text. Red on Blue. + _FillLine({ viewportStart, deleteLine }, textChars, textAttr); + + // Delete greater than the buffer width at the left edge. The entire line should be erased. + // Before: QQQQQQQQQQABCDEFGHIJKLMNOPQRSTQQQQQQQQQQ + // After: + Log::Comment(L"Deleting 100 characters at the left edge of the buffer."); + before = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + stateMachine.ProcessString(L"\x1b[100P"); + after = si.GetTextBuffer().GetRowByOffset(deleteLine).GetText(); + Log::Comment(before.c_str(), L"Before"); + Log::Comment(after.c_str(), L" After"); + + // Verify cursor didn't move. + VERIFY_ARE_EQUAL(expectedCursor, cursor.GetPosition(), L"Verify cursor didn't move from delete operation."); + + // Verify the updated structure of the line. + VERIFY_IS_TRUE(_ValidateLineContains(deleteLine, L' ', si.GetAttributes()), + L"A whole line of spaces was inserted from the right, erasing the line."); +} + void _CommonScrollingSetup() { // Used for testing MSFT:20204600 diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 4b853910dd0..1e6089ed22d 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -506,7 +506,7 @@ bool AdaptDispatch::_InsertDeleteHelper(_In_ unsigned int const uiCount, const b SHORT sDistance; RETURN_IF_FALSE(SUCCEEDED(UIntToShort(uiCount, &sDistance))); - // get current cursor, viewport + // get current cursor, attributes CONSOLE_SCREEN_BUFFER_INFOEX csbiex = { 0 }; csbiex.cbSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX); // Make sure to reset the viewport (with MoveToBottom )to where it was @@ -515,11 +515,11 @@ bool AdaptDispatch::_InsertDeleteHelper(_In_ unsigned int const uiCount, const b RETURN_IF_FALSE(_conApi->GetConsoleScreenBufferInfoEx(&csbiex)); const auto cursor = csbiex.dwCursorPosition; - const auto viewport = Viewport::FromExclusive(csbiex.srWindow); - // Rectangle to cut out of the existing buffer + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. SMALL_RECT srScroll; srScroll.Left = cursor.X; - srScroll.Right = viewport.RightExclusive(); + srScroll.Right = SHORT_MAX; srScroll.Top = cursor.Y; srScroll.Bottom = srScroll.Top; @@ -541,85 +541,16 @@ bool AdaptDispatch::_InsertDeleteHelper(_In_ unsigned int const uiCount, const b } else { - // for delete, we need to add to the scroll region to move it off toward the right. - fSuccess = SUCCEEDED(ShortAdd(srScroll.Left, sDistance, &srScroll.Left)); + // Delete scrolls the affected region to the left, relying on the clipping rect to actually delete the characters. + fSuccess = SUCCEEDED(ShortSub(coordDestination.X, sDistance, &coordDestination.X)); } if (fSuccess) { - if (srScroll.Left >= viewport.RightExclusive() || - coordDestination.X >= viewport.RightExclusive()) - { - DWORD const nLength = viewport.RightExclusive() - cursor.X; - size_t written = 0; - - // if the select/scroll region is off screen to the right or the destination is off screen to the right, fill instead of scrolling. - fSuccess = !!_conApi->FillConsoleOutputCharacterW(ciFill.Char.UnicodeChar, - nLength, - cursor, - written); - - if (fSuccess) - { - written = 0; - fSuccess = !!_conApi->FillConsoleOutputAttribute(ciFill.Attributes, - nLength, - cursor, - written); - } - } - else - { - // clip inside the viewport. - fSuccess = !!_conApi->ScrollConsoleScreenBufferW(&srScroll, - &csbiex.srWindow, - coordDestination, - &ciFill); - - if (fSuccess && !fIsInsert) - { - // See MSFT:19888564 - // We've now shifted a number of the characters to the left. - // If the number of chars we've shifted doesn't fill the - // entire region we deleted, then artifacts of the - // previous contents of the row can get left behind. - // - // Example: (this is tested by DeleteCharsNearEndOfLineSimpleFirstCase) - // start with the following buffer contents, and the cursor on the "D" - // [ABCDEFG ] - // ^ - // When you DCH(3) here, we are trying to delete the D, E and F. - // We do that by shifting the contents of the line after the deleted - // characters to the left. HOWEVER, there are only 2 chars left to move. - // So (before the fix) the buffer end up like this: - // [ABCG F ] - // ^ - // The G and " " have moved, but the F did not get overwritten. - // - // Fill the remaining space after the characters we - // shifted with spaces (empty cells). - const short scrolledChars = viewport.RightExclusive() - srScroll.Left; - const short shiftedRightPos = cursor.X + scrolledChars; - if (shiftedRightPos < srScroll.Left) - { - size_t written = 0; - const short spacesToFill = viewport.RightInclusive() - (shiftedRightPos); - const COORD fillPos{ shiftedRightPos, cursor.Y }; - fSuccess = !!_conApi->FillConsoleOutputCharacterW(ciFill.Char.UnicodeChar, - spacesToFill, - fillPos, - written); - if (fSuccess) - { - written = 0; - fSuccess = !!_conApi->FillConsoleOutputAttribute(ciFill.Attributes, - spacesToFill, - fillPos, - written); - } - } - } - } + fSuccess = !!_conApi->ScrollConsoleScreenBufferW(&srScroll, + &srScroll, + coordDestination, + &ciFill); } return fSuccess; @@ -1025,7 +956,13 @@ bool AdaptDispatch::_ScrollMovement(const ScrollDirection sdDirection, _In_ unsi if (fSuccess) { - SMALL_RECT srScreen = csbiex.srWindow; + // Rectangle to cut out of the existing buffer. This is inclusive. + // It will be clipped to the buffer boundaries so SHORT_MAX gives us the full buffer width. + SMALL_RECT srScreen; + srScreen.Left = 0; + srScreen.Right = SHORT_MAX; + srScreen.Top = csbiex.srWindow.Top; + srScreen.Bottom = csbiex.srWindow.Bottom - 1; // srWindow is exclusive, hence the - 1 // Paste coordinate for cut text above COORD coordDestination; diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index bc65cae83ee..b2a6f4bb88c 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -47,12 +47,6 @@ enum class CursorDirection : unsigned int PREVLINE = 5 }; -enum class ScrollDirection : unsigned int -{ - UP = 0, - DOWN = 1 -}; - enum class AbsolutePosition : unsigned int { CursorHorizontal = 0, @@ -395,114 +389,10 @@ class TestGetSet final : public ConGetSet return _fPrivateWriteConsoleControlInputResult; } - bool _IsInsideClip(const SMALL_RECT* const pClipRectangle, const SHORT iRow, const SHORT iCol) - { - if (pClipRectangle == nullptr) - { - return true; - } - else - { - return iRow >= pClipRectangle->Top && iRow < pClipRectangle->Bottom && iCol >= pClipRectangle->Left && iCol < pClipRectangle->Right; - } - } - - BOOL ScrollConsoleScreenBufferW(const SMALL_RECT* pScrollRectangle, _In_opt_ const SMALL_RECT* pClipRectangle, _In_ COORD dwDestinationOrigin, const CHAR_INFO* pFill) override + BOOL ScrollConsoleScreenBufferW(const SMALL_RECT* /*pScrollRectangle*/, _In_opt_ const SMALL_RECT* /*pClipRectangle*/, _In_ COORD /*dwDestinationOrigin*/, const CHAR_INFO* /*pFill*/) override { Log::Comment(L"ScrollConsoleScreenBufferW MOCK called..."); - if (_fScrollConsoleScreenBufferWResult) - { - if (pClipRectangle != nullptr) - { - Log::Comment(NoThrowString().Format( - L"\tScrolling Rectangle (T: %d, B: %d, L: %d, R: %d) " - L"into new top-left coordinate (X: %d, Y:%d) with Fill ('%c', 0x%x) " - L"clipping to (T: %d, B: %d, L: %d, R: %d)...", - pScrollRectangle->Top, - pScrollRectangle->Bottom, - pScrollRectangle->Left, - pScrollRectangle->Right, - dwDestinationOrigin.X, - dwDestinationOrigin.Y, - pFill->Char.UnicodeChar, - pFill->Attributes, - pClipRectangle->Top, - pClipRectangle->Bottom, - pClipRectangle->Left, - pClipRectangle->Right)); - } - else - { - Log::Comment(NoThrowString().Format( - L"\tScrolling Rectangle (T: %d, B: %d, L: %d, R: %d) " - L"into new top-left coordinate (X: %d, Y:%d) with Fill ('%c', 0x%x) ", - pScrollRectangle->Top, - pScrollRectangle->Bottom, - pScrollRectangle->Left, - pScrollRectangle->Right, - dwDestinationOrigin.X, - dwDestinationOrigin.Y, - pFill->Char.UnicodeChar, - pFill->Attributes)); - } - - // allocate buffer space to hold scrolling rectangle - SHORT width = pScrollRectangle->Right - pScrollRectangle->Left; - SHORT height = pScrollRectangle->Bottom - pScrollRectangle->Top + 1; - size_t const cch = width * height; - CHAR_INFO* const ciBuffer = new CHAR_INFO[cch]; - size_t cciFilled = 0; - - Log::Comment(NoThrowString().Format(L"\tCopy buffer size is %zu chars", cch)); - - for (SHORT iCharY = pScrollRectangle->Top; iCharY <= pScrollRectangle->Bottom; iCharY++) - { - // back up space and fill it with the fill. - for (SHORT iCharX = pScrollRectangle->Left; iCharX < pScrollRectangle->Right; iCharX++) - { - COORD coordTarget; - coordTarget.X = (SHORT)iCharX; - coordTarget.Y = iCharY; - - CHAR_INFO* const pciStored = _GetCharAt(coordTarget.Y, coordTarget.X); - - // back up to buffer - ciBuffer[cciFilled] = *pciStored; - cciFilled++; - - // fill with fill - if (_IsInsideClip(pClipRectangle, coordTarget.Y, coordTarget.X)) - { - *pciStored = *pFill; - } - } - } - Log::Comment(NoThrowString().Format(L"\tCopied a total %zu chars", cciFilled)); - Log::Comment(L"\tCopying chars back"); - for (SHORT iCharY = pScrollRectangle->Top; iCharY <= pScrollRectangle->Bottom; iCharY++) - { - // back up space and fill it with the fill. - for (SHORT iCharX = pScrollRectangle->Left; iCharX < pScrollRectangle->Right; iCharX++) - { - COORD coordTarget; - coordTarget.X = dwDestinationOrigin.X + (iCharX - pScrollRectangle->Left); - coordTarget.Y = dwDestinationOrigin.Y + (iCharY - pScrollRectangle->Top); - - CHAR_INFO* const pciStored = _GetCharAt(coordTarget.Y, coordTarget.X); - - if (_IsInsideClip(pClipRectangle, coordTarget.Y, coordTarget.X) && _IsInsideClip(pClipRectangle, iCharY, iCharX)) - { - size_t index = (width) * (iCharY - pScrollRectangle->Top) + (iCharX - pScrollRectangle->Left); - CHAR_INFO charFromBuffer = ciBuffer[index]; - *pciStored = charFromBuffer; - } - } - } - - delete[] ciBuffer; - } - return _fScrollConsoleScreenBufferWResult; } @@ -978,55 +868,6 @@ class TestGetSet final : public ConGetSet } } - void InsertString(COORD coordTarget, PWSTR pwszText, WORD wAttr) - { - Log::Comment(NoThrowString().Format(L"Writing string '%s' to target (X: %d, Y:%d) with color/attr 0x%x", pwszText, coordTarget.X, coordTarget.Y, wAttr)); - - size_t cchModified = 0; - - if (pwszText != nullptr) - { - size_t cch; - if (SUCCEEDED(StringCchLengthW(pwszText, STRSAFE_MAX_LENGTH, &cch))) - { - COORD coordInsertPoint = coordTarget; - - for (size_t i = 0; i < cch; i++) - { - CHAR_INFO* const pci = _GetCharAt(coordInsertPoint.Y, coordInsertPoint.X); - pci->Char.UnicodeChar = pwszText[i]; - pci->Attributes = wAttr; - - _IncrementCoordPos(&coordInsertPoint); - cchModified++; - } - } - } - - Log::Comment(NoThrowString().Format(L"Wrote %zu characters into buffer.", cchModified)); - } - - void FillRectangle(SMALL_RECT srRect, wchar_t wch, WORD wAttr) - { - Log::Comment(NoThrowString().Format(L"Filling area (L: %d, R: %d, T: %d, B: %d) with '%c' in attr 0x%x", srRect.Left, srRect.Right, srRect.Top, srRect.Bottom, wch, wAttr)); - - size_t cchModified = 0; - - for (SHORT iRow = srRect.Top; iRow < srRect.Bottom; iRow++) - { - for (SHORT iCol = srRect.Left; iCol < srRect.Right; iCol++) - { - CHAR_INFO* const pci = _GetCharAt(iRow, iCol); - pci->Char.UnicodeChar = wch; - pci->Attributes = wAttr; - - cchModified++; - } - } - - Log::Comment(NoThrowString().Format(L"Filled %zu characters.", cchModified)); - } - void ValidateInputEvent(_In_ PCWSTR pwszExpectedResponse) { size_t const cchResponse = wcslen(pwszExpectedResponse); @@ -1053,132 +894,6 @@ class TestGetSet final : public ConGetSet } } - bool ValidateString(COORD const coordTarget, PCWSTR pwszText, WORD const wAttr) - { - Log::Comment(NoThrowString().Format(L"Validating that the string %s is written starting at (X: %d, Y: %d) with the color/attr 0x%x", pwszText, coordTarget.X, coordTarget.Y, wAttr)); - - bool fSuccess = true; - - if (pwszText != nullptr) - { - size_t cch; - fSuccess = SUCCEEDED(StringCchLengthW(pwszText, STRSAFE_MAX_LENGTH, &cch)); - - if (fSuccess) - { - COORD coordGetPos = coordTarget; - - for (size_t i = 0; i < cch; i++) - { - const CHAR_INFO* const pci = _GetCharAt(coordGetPos.Y, coordGetPos.X); - - const wchar_t wchActual = pci->Char.UnicodeChar; - const wchar_t wchExpected = pwszText[i]; - - fSuccess = wchExpected == wchActual; - - if (!fSuccess) - { - Log::Comment(NoThrowString().Format(L"ValidateString failed char comparison at (X: %d, Y: %d). Expected: '%c' Actual: '%c'", coordGetPos.X, coordGetPos.Y, wchExpected, wchActual)); - break; - } - - const WORD wAttrActual = pci->Attributes; - const WORD wAttrExpected = wAttr; - - if (!fSuccess) - { - Log::Comment(NoThrowString().Format(L"ValidateString failed attr comparison at (X: %d, Y: %d). Expected: '0x%x' Actual: '0x%x'", coordGetPos.X, coordGetPos.Y, wAttrExpected, wAttrActual)); - break; - } - - _IncrementCoordPos(&coordGetPos); - } - } - } - - return fSuccess; - } - - bool ValidateRectangleContains(SMALL_RECT srRect, wchar_t wchExpected, WORD wAttrExpected) - { - Log::Comment(NoThrowString().Format(L"Validating that the area inside (L: %d, R: %d, T: %d, B: %d) char '%c' and attr 0x%x", srRect.Left, srRect.Right, srRect.Top, srRect.Bottom, wchExpected, wAttrExpected)); - - bool fStateValid = true; - - for (SHORT iRow = srRect.Top; iRow < srRect.Bottom; iRow++) - { - Log::Comment(NoThrowString().Format(L"Validating row(y=) %d", iRow)); - for (SHORT iCol = srRect.Left; iCol < srRect.Right; iCol++) - { - CHAR_INFO* const pci = _GetCharAt(iRow, iCol); - - fStateValid = pci->Char.UnicodeChar == wchExpected; - if (!fStateValid) - { - Log::Comment(NoThrowString().Format(L"Region match failed at (X: %d, Y: %d). Expected: '%c'. Actual: '%c'", iCol, iRow, wchExpected, pci->Char.UnicodeChar)); - break; - } - - fStateValid = pci->Attributes == wAttrExpected; - if (!fStateValid) - { - Log::Comment(NoThrowString().Format(L"Region match failed at (X: %d, Y: %d). Expected Attr: 0x%x. Actual Attr: 0x%x", iCol, iRow, wAttrExpected, pci->Attributes)); - } - } - - if (!fStateValid) - { - break; - } - } - - return fStateValid; - } - - bool ValidateRectangleContains(SMALL_RECT srRect, wchar_t wchExpected, WORD wAttrExpected, SMALL_RECT srExcept) - { - bool fStateValid = true; - - Log::Comment(NoThrowString().Format(L"Validating that the area inside (L: %d, R: %d, T: %d, B: %d) but outside (L: %d, R: %d, T: %d, B: %d) contains char '%c' and attr 0x%x", srRect.Left, srRect.Right, srRect.Top, srRect.Bottom, srExcept.Left, srExcept.Right, srExcept.Top, srExcept.Bottom, wchExpected, wAttrExpected)); - - for (SHORT iRow = srRect.Top; iRow < srRect.Bottom; iRow++) - { - for (SHORT iCol = srRect.Left; iCol < srRect.Right; iCol++) - { - if (iRow >= srExcept.Top && iRow < srExcept.Bottom && iCol >= srExcept.Left && iCol < srExcept.Right) - { - // if in exception range, skip comparison. - continue; - } - else - { - CHAR_INFO* const pci = _GetCharAt(iRow, iCol); - - fStateValid = pci->Char.UnicodeChar == wchExpected; - if (!fStateValid) - { - Log::Comment(NoThrowString().Format(L"Region match failed at (X: %d, Y: %d). Expected: '%c'. Actual: '%c'", iCol, iRow, wchExpected, pci->Char.UnicodeChar)); - break; - } - - fStateValid = pci->Attributes == wAttrExpected; - if (!fStateValid) - { - Log::Comment(NoThrowString().Format(L"Region match failed at (X: %d, Y: %d). Expected Attr: 0x%x. Actual Attr: 0x%x", iCol, iRow, wAttrExpected, pci->Attributes)); - } - } - } - - if (!fStateValid) - { - break; - } - } - - return fStateValid; - } - bool ValidateEraseBufferState(SMALL_RECT* rgsrRegions, size_t cRegions, wchar_t wchExpectedInRegions, WORD wAttrExpectedInRegions) { bool fStateValid = true; @@ -1274,20 +989,6 @@ class TestGetSet final : public ConGetSet return pchar; } - void _PrepForScroll(ScrollDirection const dir, int const distance) - { - _fExpectedWindowAbsolute = FALSE; - _srExpectedConsoleWindow.Top = (SHORT)distance; - _srExpectedConsoleWindow.Bottom = (SHORT)distance; - _srExpectedConsoleWindow.Left = 0; - _srExpectedConsoleWindow.Right = 0; - if (dir == ScrollDirection::UP) - { - _srExpectedConsoleWindow.Top *= -1; - _srExpectedConsoleWindow.Bottom *= -1; - } - } - void _SetMarginsHelper(SMALL_RECT* rect, SHORT top, SHORT bottom) { rect->Top = top; @@ -1939,315 +1640,6 @@ class AdapterTest VERIFY_IS_FALSE(_pDispatch->CursorVisibility(fEnd)); } - TEST_METHOD(InsertCharacterTests) - { - Log::Comment(L"Starting test..."); - - Log::Comment(L"Test 1: The big one. Fill the buffer with Qs. Fill the window with Rs. Write a line of ABCDE at the cursor. Then insert 5 spaces at the cursor. Watch spaces get inserted, ABCDE slide right eating up the Rs in the viewport but not modifying the Qs outside."); - - // place the cursor in the center. - _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); - - // Save the cursor position. It shouldn't move for the rest of the test. - COORD coordCursorExpected = _testGetSet->_coordCursorPos; - - // Fill the entire buffer with Qs. Blue on Green. - WCHAR const wchOuterBuffer = 'Q'; - WORD const wAttrOuterBuffer = FOREGROUND_BLUE | BACKGROUND_GREEN; - SMALL_RECT srOuterBuffer; - srOuterBuffer.Top = 0; - srOuterBuffer.Left = 0; - srOuterBuffer.Bottom = _testGetSet->_coordBufferSize.Y; - srOuterBuffer.Right = _testGetSet->_coordBufferSize.X; - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - - // Fill the viewport with Rs. Red on Blue. - WCHAR const wchViewport = 'R'; - WORD const wAttrViewport = FOREGROUND_RED | BACKGROUND_BLUE; - SMALL_RECT srViewport = _testGetSet->_srViewport; - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // fill some of the text right of the cursor so we can verify it moved it and didn't overwrite it. - // change the color too so we can make sure that it's fine - - WORD const wAttrTestText = FOREGROUND_GREEN; - PWSTR const pwszTestText = L"ABCDE"; - size_t cchTestText = wcslen(pwszTestText); - SMALL_RECT srTestText; - srTestText.Top = _testGetSet->_coordCursorPos.Y; - srTestText.Bottom = srTestText.Top + 1; - srTestText.Left = _testGetSet->_coordCursorPos.X; - srTestText.Right = srTestText.Left + (SHORT)cchTestText; - _testGetSet->InsertString(_testGetSet->_coordCursorPos, pwszTestText, wAttrTestText); - - WCHAR const wchInsertExpected = L' '; - WORD const wAttrInsertExpected = _testGetSet->_wAttribute; - size_t const cchInsertSize = 5; - SMALL_RECT srInsertExpected; - srInsertExpected.Top = _testGetSet->_coordCursorPos.Y; - srInsertExpected.Bottom = srInsertExpected.Top + 1; - srInsertExpected.Left = _testGetSet->_coordCursorPos.X; - srInsertExpected.Right = srInsertExpected.Left + (SHORT)cchInsertSize; - - // the text we inserted is going to move right by the insert size, so adjust that rectangle right. - srTestText.Left += cchInsertSize; - srTestText.Right += cchInsertSize; - - // insert out 5 spots. this should clear them out with spaces and the default fill from the original cursor position - VERIFY_IS_TRUE(_pDispatch->InsertCharacter(cchInsertSize), L"Verify insert call was sucessful."); - - // the combined area of the letters + the spaces will be 10 characters wide: - SMALL_RECT srModifiedSpace; - srModifiedSpace.Top = _testGetSet->_coordCursorPos.Y; - srModifiedSpace.Bottom = srModifiedSpace.Top + 1; - srModifiedSpace.Left = _testGetSet->_coordCursorPos.X; - srModifiedSpace.Right = srModifiedSpace.Left + (SHORT)cchInsertSize + (SHORT)cchTestText; - - // verify cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // e.g. we had this in the buffer: QQQRRRRRRABCDERRRRRRRQQQ with the cursor on the A. - // now we should have this buffer: QQQRRRRRR ABCDERRQQQ with the cursor on the first space. - - // Verify the field of Qs didn't change outside the viewport. - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Verify the field of Rs within the viewport not including the inserted range and the ABCDE shifted right. (10 characters) - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srModifiedSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // Verify the 5 spaces inserted from the cursor. - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srInsertExpected, wchInsertExpected, wAttrInsertExpected), L"Spaces should be inserted with the proper attributes at the cursor."); - - // Verify the ABCDE sequence was shifted right. - COORD coordTestText; - coordTestText.X = srTestText.Left; - coordTestText.Y = srTestText.Top; - VERIFY_IS_TRUE(_testGetSet->ValidateString(coordTestText, pwszTestText, wAttrTestText), L"Inserted string should have moved to the right by the number of spaces inserted, attributes and text preserved."); - - // Test case needed for exact end of line (and full line) insert/delete lengths - Log::Comment(L"Test 2: Inserting at the exact end of the line. Same field of Qs and Rs. Move cursor to right edge of window and insert > 1 space. Only 1 should be inserted, everything else unchanged."); - - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // move cursor to right edge - _testGetSet->_coordCursorPos.X = _testGetSet->_srViewport.Right - 1; - coordCursorExpected = _testGetSet->_coordCursorPos; - - // the rectangle where the space should be is exactly the size of the cursor. - srModifiedSpace.Top = _testGetSet->_coordCursorPos.Y; - srModifiedSpace.Bottom = srModifiedSpace.Top + 1; - srModifiedSpace.Left = _testGetSet->_coordCursorPos.X; - srModifiedSpace.Right = srModifiedSpace.Left + 1; - - // insert out 5 spots. this should clear them out with spaces and the default fill from the original cursor position - VERIFY_IS_TRUE(_pDispatch->InsertCharacter(cchInsertSize), L"Verify insert call was sucessful."); - - // cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // Qs are the same outside the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Entire viewport is Rs except the one space spot - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srModifiedSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // The 5 inserted spaces at the right edge resulted in 1 space at the right edge - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srModifiedSpace, wchInsertExpected, wAttrInsertExpected), L"A space was inserted at the cursor position. All extra spaces were discarded as they hit the right boundary."); - - Log::Comment(L"Test 3: Inserting at the exact beginning of the line. Same field of Qs and Rs. Move cursor to left edge of window and insert > screen width of space. The whole row should be spaces but nothing outside the viewport should be changed."); - - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // move cursor to left edge - _testGetSet->_coordCursorPos.X = _testGetSet->_srViewport.Left; - coordCursorExpected = _testGetSet->_coordCursorPos; - - // the rectangle of spaces should be the entire line at the cursor. - srModifiedSpace.Top = _testGetSet->_coordCursorPos.Y; - srModifiedSpace.Bottom = srModifiedSpace.Top + 1; - srModifiedSpace.Left = _testGetSet->_srViewport.Left; - srModifiedSpace.Right = _testGetSet->_srViewport.Right; - - // insert greater than the entire viewport (the entire buffer width) at the cursor position - VERIFY_IS_TRUE(_pDispatch->InsertCharacter(_testGetSet->_coordBufferSize.X), L"Verify insert call was successful."); - - // cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // Qs are the same outside the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Entire viewport is Rs except the one space spot - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srModifiedSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // The inserted spaces at the left edge resulted in an entire line of spaces bounded by the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srModifiedSpace, wchInsertExpected, wAttrInsertExpected), L"A whole line of spaces was inserted at the cursor position. All extra spaces were discarded as they hit the right boundary."); - } - - TEST_METHOD(DeleteCharacterTests) - { - Log::Comment(L"Starting test..."); - - Log::Comment(L"Test 1: The big one. Fill the buffer with Qs. Fill the window with Rs. Write a line of ABCDE at the cursor. Then insert 5 spaces at the cursor. Watch spaces get inserted, ABCDE slide right eating up the Rs in the viewport but not modifying the Qs outside."); - - // place the cursor in the center. - _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); - - // Save the cursor position. It shouldn't move for the rest of the test. - COORD coordCursorExpected = _testGetSet->_coordCursorPos; - - // Fill the entire buffer with Qs. Blue on Green. - WCHAR const wchOuterBuffer = 'Q'; - WORD const wAttrOuterBuffer = FOREGROUND_BLUE | BACKGROUND_GREEN; - SMALL_RECT srOuterBuffer; - srOuterBuffer.Top = 0; - srOuterBuffer.Left = 0; - srOuterBuffer.Bottom = _testGetSet->_coordBufferSize.Y; - srOuterBuffer.Right = _testGetSet->_coordBufferSize.X; - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - - // Fill the viewport with Rs. Red on Blue. - WCHAR const wchViewport = 'R'; - WORD const wAttrViewport = FOREGROUND_RED | BACKGROUND_BLUE; - SMALL_RECT srViewport = _testGetSet->_srViewport; - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // fill some of the text right of the cursor so we can verify it moved it and wasn't deleted - // change the color too so we can make sure that it's fine - WORD const wAttrTestText = FOREGROUND_GREEN; - PWSTR const pwszTestText = L"ABCDE"; - size_t cchTestText = wcslen(pwszTestText); - SMALL_RECT srTestText; - srTestText.Top = _testGetSet->_coordCursorPos.Y; - srTestText.Bottom = srTestText.Top + 1; - srTestText.Left = _testGetSet->_coordCursorPos.X; - srTestText.Right = srTestText.Left + (SHORT)cchTestText; - _testGetSet->InsertString(_testGetSet->_coordCursorPos, pwszTestText, wAttrTestText); - - // We're going to delete "in" from the right edge, so set up that rectangle. - WCHAR const wchDeleteExpected = L' '; - WORD const wAttrDeleteExpected = _testGetSet->_wAttribute; - size_t const cchDeleteSize = 5; - SMALL_RECT srDeleteExpected; - srDeleteExpected.Top = _testGetSet->_coordCursorPos.Y; - srDeleteExpected.Bottom = srDeleteExpected.Top + 1; - srDeleteExpected.Right = _testGetSet->_srViewport.Right; - srDeleteExpected.Left = srDeleteExpected.Right - cchDeleteSize; - - // We want the ABCDE to shift left when we delete and onto the cursor. So move the cursor left 5 and adjust the srTestText rectangle left 5 to the new - // final destination of where they will be after the delete operation occurs. - _testGetSet->_coordCursorPos.X -= cchDeleteSize; - coordCursorExpected = _testGetSet->_coordCursorPos; - srTestText.Left -= cchDeleteSize; - srTestText.Right -= cchDeleteSize; - - // delete out 5 spots. this should shift the ABCDE text left by 5 and insert 5 spaces at the end of the line - VERIFY_IS_TRUE(_pDispatch->DeleteCharacter(cchDeleteSize), L"Verify delete call was sucessful."); - - // we're going to have ABCDERRRRRRRRRRRRR QQQQQQQ - // since this is a bit more complicated than the insert case, make this the "special" region and exempt it from the bulk "R" check - // we'll check the inside of this rect in 3 pieces, for the ABCDE, then for the inner Rs, then for the 5 spaces after. - SMALL_RECT srSpecialSpace; - srSpecialSpace.Top = _testGetSet->_coordCursorPos.Y; - srSpecialSpace.Bottom = srSpecialSpace.Top + 1; - srSpecialSpace.Left = _testGetSet->_coordCursorPos.X; - srSpecialSpace.Right = _testGetSet->_srViewport.Right; - - SMALL_RECT srGap; // gap space is the Rs between ABCDE and the spaces shifted in from the right - srGap.Left = srTestText.Right; - srGap.Right = srDeleteExpected.Left; - srGap.Top = _testGetSet->_coordCursorPos.Y; - srGap.Bottom = srGap.Top + 1; - - // verify cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // e.g. we had this in the buffer: QQQRRRRRR-RRRRABCDERRQQQ with the cursor on the -. - // now we should have this buffer: QQQRRRRRRABCDERR QQQ with the cursor on the A. - - // Verify the field of Qs didn't change outside the viewport. - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Verify the field of Rs within the viewport not including the special range of the ABCDE, the spaces shifted in from the right, and the Rs between them that went along for the ride. - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srSpecialSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // Verify the 5 spaces shifted in from the right edge due to the delete - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srDeleteExpected, wchDeleteExpected, wAttrDeleteExpected), L"Spaces should be inserted with the proper attributes from the right end of this line (viewport edge.)"); - - // Verify the ABCDE sequence was shifted left by 5 toward the cursor. - COORD coordTestText; - coordTestText.X = srTestText.Left; - coordTestText.Y = srTestText.Top; - VERIFY_IS_TRUE(_testGetSet->ValidateString(coordTestText, pwszTestText, wAttrTestText), L"Inserted string should have moved to the left by the number of deletes, attributes and text preserved."); - - // Verify the field of Rs between the ABCDE and the spaces shifted in from the right - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srGap, wchViewport, wAttrViewport), L"Viewport Rs should be preserved/shifted left in between the ABCDE and the spaces that came in from the right edge."); - - // Test case needed for exact end of line (and full line) insert/delete lengths - Log::Comment(L"Test 2: Deleting at the exact end of the line. Same field of Qs and Rs. Move cursor to right edge of window and delete > 1 space. Only 1 should be inserted from the right edge (delete inserts from the right), everything else unchanged."); - - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // move cursor to right edge - _testGetSet->_coordCursorPos.X = _testGetSet->_srViewport.Right - 1; - coordCursorExpected = _testGetSet->_coordCursorPos; - - // the rectangle where the space should be is exactly the size of the cursor. - SMALL_RECT srModifiedSpace; - srModifiedSpace.Top = _testGetSet->_coordCursorPos.Y; - srModifiedSpace.Bottom = srModifiedSpace.Top + 1; - srModifiedSpace.Left = _testGetSet->_coordCursorPos.X; - srModifiedSpace.Right = srModifiedSpace.Left + 1; - - // delete out 5 spots. this should clear them out with spaces and the default fill from the original cursor position - VERIFY_IS_TRUE(_pDispatch->DeleteCharacter(cchDeleteSize), L"Verify delete call was sucessful."); - - // cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from delete operation."); - - // Qs are the same outside the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Entire viewport is Rs except the one space spot - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srModifiedSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // The 5 deleted spaces at the right edge resulted in 1 space at the right edge - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srModifiedSpace, wchDeleteExpected, wAttrDeleteExpected), L"A space was inserted at the cursor position. All extra spaces deleted in from the right continued to cover that one space."); - - Log::Comment(L"Test 3: Deleting at the exact beginning of the line. Same field of Qs and Rs. Move cursor to left edge of window and delete > screen width of space. The whole row should be spaces but nothing outside the viewport should be changed."); - - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // move cursor to left edge - _testGetSet->_coordCursorPos.X = _testGetSet->_srViewport.Left; - coordCursorExpected = _testGetSet->_coordCursorPos; - - // the rectangle of spaces should be the entire line at the cursor. - srModifiedSpace.Top = _testGetSet->_coordCursorPos.Y; - srModifiedSpace.Bottom = srModifiedSpace.Top + 1; - srModifiedSpace.Left = _testGetSet->_srViewport.Left; - srModifiedSpace.Right = _testGetSet->_srViewport.Right; - - // delete greater than the entire viewport (the entire buffer width) at the cursor position - VERIFY_IS_TRUE(_pDispatch->DeleteCharacter(_testGetSet->_coordBufferSize.X), L"Verify delete call was successful."); - - // cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // Qs are the same outside the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), L"Field of Qs outside viewport should remain unchanged."); - - // Entire viewport is Rs except the one space spot - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srViewport, wchViewport, wAttrViewport, srModifiedSpace), L"Field of Rs in the viewport outside modified space should remain unchanged."); - - // The inserted spaces at the left edge resulted in an entire line of spaces bounded by the viewport - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srModifiedSpace, wchDeleteExpected, wAttrDeleteExpected), L"A whole line of spaces was inserted from the right (the cursor position was deleted enough times.) Extra deletes just covered up some of the spaces that were shifted in."); - } - // Ensures that EraseScrollback (^[[3J) deletes any content from the buffer // above the viewport, and moves the contents of the buffer in the // viewport to 0,0. This emulates the xterm behavior of clearing any @@ -2982,129 +2374,6 @@ class AdapterTest VERIFY_IS_FALSE(_pDispatch->DeviceAttributes()); } - TEST_METHOD(ScrollTest) - { - BEGIN_TEST_METHOD_PROPERTIES() - TEST_METHOD_PROPERTY(L"Data:uiDirection", L"{0, 1}") // These values align with the ScrollDirection enum class to try all the directions. - TEST_METHOD_PROPERTY(L"Data:uiMagnitude", L"{1, 2, 5}") // These values align with the ScrollDirection enum class to try all the directions. - END_TEST_METHOD_PROPERTIES() - - Log::Comment(L"Starting test..."); - - // Used to switch between the various function options. - typedef bool (AdaptDispatch::*ScrollFunc)(const unsigned int); - ScrollFunc scrollFunc = nullptr; - - // Modify variables based on directionality of this test - ScrollDirection direction; - unsigned int dir; - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"uiDirection", dir)); - direction = (ScrollDirection)dir; - unsigned int uiMagnitude; - VERIFY_SUCCEEDED_RETURN(TestData::TryGetValue(L"uiMagnitude", uiMagnitude)); - SHORT sMagnitude = (SHORT)uiMagnitude; - - switch (direction) - { - case ScrollDirection::UP: - Log::Comment(L"Testing up direction."); - scrollFunc = &AdaptDispatch::ScrollUp; - break; - case ScrollDirection::DOWN: - Log::Comment(L"Testing down direction."); - scrollFunc = &AdaptDispatch::ScrollDown; - break; - } - Log::Comment(NoThrowString().Format(L"Scrolling by %d lines", uiMagnitude)); - if (scrollFunc == nullptr) - { - VERIFY_FAIL(); - return; - } - - // place the cursor in the center. - _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); - - // Save the cursor position. It shouldn't move for the rest of the test. - COORD coordCursorExpected = _testGetSet->_coordCursorPos; - - // Fill the entire buffer with Qs. Blue on Green. - WCHAR const wchOuterBuffer = 'Q'; - WORD const wAttrOuterBuffer = FOREGROUND_BLUE | BACKGROUND_GREEN; - SMALL_RECT srOuterBuffer; - srOuterBuffer.Top = 0; - srOuterBuffer.Left = 0; - srOuterBuffer.Bottom = _testGetSet->_coordBufferSize.Y; - srOuterBuffer.Right = _testGetSet->_coordBufferSize.X; - _testGetSet->FillRectangle(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer); - - // Fill the viewport with Rs. Red on Blue. - WCHAR const wchViewport = 'R'; - WORD const wAttrViewport = FOREGROUND_RED | BACKGROUND_BLUE; - SMALL_RECT srViewport = _testGetSet->_srViewport; - _testGetSet->FillRectangle(srViewport, wchViewport, wAttrViewport); - - // Add some characters to see if they moved. - // change the color too so we can make sure that it's fine - - WORD const wAttrTestText = FOREGROUND_GREEN; - PWSTR const pwszTestText = L"ABCDE"; // Text is written at y=34, moves to y=33 - size_t cchTestText = wcslen(pwszTestText); - SMALL_RECT srTestText; - srTestText.Top = _testGetSet->_coordCursorPos.Y; - srTestText.Bottom = srTestText.Top + 1; - srTestText.Left = _testGetSet->_coordCursorPos.X; - srTestText.Right = srTestText.Left + (SHORT)cchTestText; - _testGetSet->InsertString(_testGetSet->_coordCursorPos, pwszTestText, wAttrTestText); - - //Scroll Up one line - VERIFY_IS_TRUE((_pDispatch->*(scrollFunc))(sMagnitude), L"Verify Scroll call was sucessful."); - - // verify cursor didn't move - VERIFY_ARE_EQUAL(coordCursorExpected, _testGetSet->_coordCursorPos, L"Verify cursor didn't move from insert operation."); - - // Verify the field of Qs didn't change outside the viewport. - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOuterBuffer, wchOuterBuffer, wAttrOuterBuffer, srViewport), - L"Field of Qs outside viewport should remain unchanged."); - - // Okay, this part get confusing. These change depending on the direction of the test. - // direction InViewport Outside - // UP Bottom Line Top minus One - // DOWN Top Line Bottom plus One - const bool fScrollUp = (direction == ScrollDirection::UP); - SMALL_RECT srInViewport = srViewport; - srInViewport.Top = (fScrollUp) ? (srViewport.Bottom - sMagnitude) : (srViewport.Top); - srInViewport.Bottom = srInViewport.Top + sMagnitude; - WCHAR const wchInViewport = ' '; - WORD const wAttrInViewport = _testGetSet->_wAttribute; - - // Verify the bottom line is now empty - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srInViewport, wchInViewport, wAttrInViewport), - L"InViewport line(s) should now be blank, with default buffer attributes"); - - SMALL_RECT srOutside = srViewport; - srOutside.Top = (fScrollUp) ? (srViewport.Top - sMagnitude) : (srViewport.Bottom); - srOutside.Bottom = srOutside.Top + sMagnitude; - WCHAR const wchOutside = wchOuterBuffer; - WORD const wAttrOutside = wAttrOuterBuffer; - - // Verify the line above the viewport is unchanged - VERIFY_IS_TRUE(_testGetSet->ValidateRectangleContains(srOutside, wchOutside, wAttrOutside), - L"Line(s) above the viewport is unchanged"); - - // Verify that the line where the ABCDE is now wchViewport - COORD coordTestText; - PWSTR const pwszNewTestText = L"RRRRR"; - coordTestText.X = srTestText.Left; - coordTestText.Y = srTestText.Top; - VERIFY_IS_TRUE(_testGetSet->ValidateString(coordTestText, pwszNewTestText, wAttrViewport), L"Contents of viewport should have shifted to where the string used to be."); - - // Verify that the line above/below the ABCDE now has the ABCDE - coordTestText.X = srTestText.Left; - coordTestText.Y = (fScrollUp) ? (srTestText.Top - sMagnitude) : (srTestText.Top + sMagnitude); - VERIFY_IS_TRUE(_testGetSet->ValidateString(coordTestText, pwszTestText, wAttrTestText), L"String should have moved up/down by given magnitude."); - } - TEST_METHOD(CursorKeysModeTest) { Log::Comment(L"Starting test..."); From b5fe4ffd54315ddd58e753cee7196304faf4be5c Mon Sep 17 00:00:00 2001 From: kynapse Date: Wed, 11 Sep 2019 12:21:15 -0400 Subject: [PATCH 150/154] Update link to Background Images and Icons section (#2725) --- doc/cascadia/SettingsSchema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cascadia/SettingsSchema.md b/doc/cascadia/SettingsSchema.md index c32c5cb3026..116be007d60 100644 --- a/doc/cascadia/SettingsSchema.md +++ b/doc/cascadia/SettingsSchema.md @@ -43,7 +43,7 @@ Properties listed below are specific to each unique profile. | `colorTable` | Optional | Array[String] | | Array of colors used in the profile if `colorscheme` is not set. Colors use hex color format: `"#rrggbb"`. Ordering is as follows: `[black, red, green, yellow, blue, magenta, cyan, white, bright black, bright red, bright green, bright yellow, bright blue, bright magenta, bright cyan, bright white]` | | `cursorHeight` | Optional | Integer | | Sets the percentage height of the cursor starting from the bottom. Only works when `cursorShape` is set to `"vintage"`. Accepts values from 25-100. | | `foreground` | Optional | String | | Sets the foreground color of the profile. Overrides `foreground` set in color scheme if `colorscheme` is set. Uses hex color format: `"#rrggbb"`. | -| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. See [Images and Icons](./#images_and_icons) below for help on specifying your own icons | +| `icon` | Optional | String | | Image file location of the icon used in the profile. Displays within the tab and the dropdown menu. See [Background Images and Icons](./SettingsSchema.md#background-images-and-icons) below for help on specifying your own icons | | `scrollbarState` | Optional | String | | Defines the visibility of the scrollbar. Possible values: `"visible"`, `"hidden"` | | `tabTitle` | Optional | String | | If set, will replace the `name` as the title to pass to the shell on startup. Some shells (like `bash`) may choose to ignore this initial value, while others (`cmd`, `powershell`) may use this value over the lifetime of the application. | From 537258a60fd9b3e62474b0d432330cc6b78742ed Mon Sep 17 00:00:00 2001 From: Martin Lopes <54248166+martin389@users.noreply.github.com> Date: Wed, 11 Sep 2019 17:24:20 +0100 Subject: [PATCH 151/154] Edits doc section `Configuring Windows Terminal` (#2719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Edits doc section `Configuring Windows Terminal` * Converts into a procedure. * Uses `⌵` character to replace the `down` UI element. * Additional minor edit Updates formatting, edits for brevity. * Fixed json path Added `8wekyb3d8bbwe` to file path. --- doc/user-docs/index.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/user-docs/index.md b/doc/user-docs/index.md index 5379c017079..ae9a4d09f19 100644 --- a/doc/user-docs/index.md +++ b/doc/user-docs/index.md @@ -67,16 +67,15 @@ Not currently supported "out of the box". See issue [#1060](https://github.com/m ## Configuring Windows Terminal -At the time of writing all Windows Terminal settings are managed via a json file. +All Windows Terminal settings are currently managed using the `profiles.json` file, located within `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe/RoamingState`. -From the `down` button in the top bar select Settings (default shortcut `Ctrl+,`). +To open the settings file from Windows Terminal: -Your default json editor will open up the Terminal settings file. The file can be found -at `$env:LocalAppData\Packages\Microsoft.WindowsTerminal_/RoamingState` +1. Click the `⌵` button in the top bar. +2. From the dropdown list, click `Settings`. You can also use a shortcut: `Ctrl+,`. +3. Your default `json` editor will open the settings file. -An introduction to the various settings can be found [here](UsingJsonSettings.md). - -The list of valid settings can be found in the [Profiles.json Documentation](../cascadia/SettingsSchema.md) doc. +For an introduction to the various settings, see [Using Json Settings](UsingJsonSettings.md). The list of valid settings can be found in the [profiles.json documentation](../cascadia/SettingsSchema.md) section. ## Tips and Tricks: From 1fccbc53049ff74b14e365ebb96e5826033a6b91 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 12 Sep 2019 18:46:38 +0100 Subject: [PATCH 152/154] Move cursor to left margin for IL and DL controls (#2731) * Move cursor position to the left margin after execution of the IL and DL escape sequences. * Update IL and DL screen buffer tests to account for the cursor moving to the left margin. --- src/host/getset.cpp | 5 +++++ src/host/ut_host/ScreenBufferTests.cpp | 24 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index dbd584c218e..7ac745091f0 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -2021,6 +2021,7 @@ void DoSrvPrivateSetDefaultTabStops() // Routine Description: // - internal logic for adding or removing lines in the active screen buffer +// this also moves the cursor to the left margin, which is expected behaviour for IL and DL // Parameters: // - count - the number of lines to modify // - insert - true if inserting lines, false if deleting lines @@ -2069,6 +2070,10 @@ void DoSrvPrivateModifyLinesImpl(const unsigned int count, const bool insert) screenInfo.GetAttributes()); } CATCH_LOG(); + + // The IL and DL controls are also expected to move the cursor to the left margin. + // For now this is just column 0, since we don't yet support DECSLRM. + LOG_IF_NTSTATUS_FAILED(screenInfo.SetCursorPosition({ 0, cursorPosition.Y }, false)); } } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index ff3d34988f9..70f39f01403 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -3230,8 +3230,16 @@ void ScreenBufferTests::ScrollOperations() VERIFY_SUCCEEDED(si.SetCursorPosition(cursorPos, true)); stateMachine.ProcessString(escapeSequence.str()); - Log::Comment(L"Verify cursor didn't move."); - VERIFY_ARE_EQUAL(cursorPos, cursor.GetPosition()); + // The cursor shouldn't move. + auto expectedCursorPos = cursorPos; + // Unless this is an IL or DL control, which moves the cursor to the left margin. + if (scrollType == InsertLine || scrollType == DeleteLine) + { + expectedCursorPos.X = 0; + } + + Log::Comment(L"Verify expected cursor position."); + VERIFY_ARE_EQUAL(expectedCursorPos, cursor.GetPosition()); Log::Comment(L"Field of Zs outside viewport should remain unchanged."); VERIFY_IS_TRUE(_ValidateLinesContain(0, viewportStart, bufferChar, bufferAttr)); @@ -3749,7 +3757,8 @@ void ScreenBufferTests::InsertLinesInMargins() Log::Comment(NoThrowString().Format( L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); - VERIFY_ARE_EQUAL(4, cursor.GetPosition().X); + // Verify cursor moved to left margin. + VERIFY_ARE_EQUAL(0, cursor.GetPosition().X); VERIFY_ARE_EQUAL(2, cursor.GetPosition().Y); { auto iter0 = tbi.GetCellDataAt({ 0, 0 }); @@ -3783,7 +3792,8 @@ void ScreenBufferTests::InsertLinesInMargins() Log::Comment(NoThrowString().Format( L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); - VERIFY_ARE_EQUAL(4, cursor.GetPosition().X); + // Verify cursor moved to left margin. + VERIFY_ARE_EQUAL(0, cursor.GetPosition().X); VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y); { auto iter0 = tbi.GetCellDataAt({ 0, 0 }); @@ -3824,7 +3834,8 @@ void ScreenBufferTests::DeleteLinesInMargins() Log::Comment(NoThrowString().Format( L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); - VERIFY_ARE_EQUAL(4, cursor.GetPosition().X); + // Verify cursor moved to left margin. + VERIFY_ARE_EQUAL(0, cursor.GetPosition().X); VERIFY_ARE_EQUAL(2, cursor.GetPosition().Y); { auto iter0 = tbi.GetCellDataAt({ 0, 0 }); @@ -3858,7 +3869,8 @@ void ScreenBufferTests::DeleteLinesInMargins() Log::Comment(NoThrowString().Format( L"viewport=%s", VerifyOutputTraits::ToString(si.GetViewport().ToInclusive()).GetBuffer())); - VERIFY_ARE_EQUAL(4, cursor.GetPosition().X); + // Verify cursor moved to left margin. + VERIFY_ARE_EQUAL(0, cursor.GetPosition().X); VERIFY_ARE_EQUAL(1, cursor.GetPosition().Y); { auto iter0 = tbi.GetCellDataAt({ 0, 0 }); From b693fd484aa48dc7483064381117315b99d6f0cf Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett (MSFT)" Date: Fri, 13 Sep 2019 14:34:41 -0700 Subject: [PATCH 153/154] wap: add some workaround to ensure that our package builds on 16.3 (#2730) Fixes #2625. --- build/scripts/Test-WindowsTerminalPackage.ps1 | 8 +++++++ .../CascadiaPackage/CascadiaPackage.wapproj | 24 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/build/scripts/Test-WindowsTerminalPackage.ps1 b/build/scripts/Test-WindowsTerminalPackage.ps1 index ca4d3df3d51..67b21958ff4 100644 --- a/build/scripts/Test-WindowsTerminalPackage.ps1 +++ b/build/scripts/Test-WindowsTerminalPackage.ps1 @@ -74,6 +74,14 @@ Try { If ($null -eq $AppXbf) { Throw "Failed to find App.xbf (TerminalApp project) in resources.pri" } + + If ($Manifest.Package.Identity.ProcessorArchitecture -Ne "arm64") { + ### ARM64 doesn't package cpprest_2_10. + If (($null -eq (Get-Item "$AppxPackageRootPath\cpprest_2_10.dll" -EA:Ignore)) -And + ($null -eq (Get-Item "$AppxPackageRootPath\cpprest_2_10d.dll" -EA:Ignore))) { + Throw "Failed to find cpprest_2_10.dll -- check the WAP packaging project" + } + } } Finally { Remove-Item -Recurse -Force $AppxPackageRootPath } diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 6c3839f370f..f4ad9305f38 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -295,7 +295,8 @@ important reasons), that doesn't work for us. --> - <_GenerateProjectPriFileDependsOn>OpenConsoleLiftDesktopBridgePriFiles;$(_GenerateProjectPriFileDependsOn) + + <_GenerateProjectPriFileDependsOn Condition="$(MSBuildVersion) < '16.3.0'">OpenConsoleLiftDesktopBridgePriFiles;$(_GenerateProjectPriFileDependsOn) @@ -305,4 +306,25 @@ + + + + + + $([MSBuild]::Unescape('$(WapProjBeforeGenerateAppxManifestDependsOn.Replace('_RemoveAllNonWapUWPItems', '_OpenConsoleRemoveAllNonWapUWPItems'))')) + + + + + + + + + + + + From 3d35e396b257d9281c6ccaa488d237b35c506d7e Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 13 Sep 2019 14:36:01 -0700 Subject: [PATCH 154/154] Bugfix: CLS should clear current active buffer (#2729) CLS calls two functions: - `SetConsoleCursorPositionImpl()` - `ScrollConsoleScreenBufferWImpl()` Both of these were not checking which buffer to apply to (main vs active buffer). Now we get the active buffer and apply the changes to that one. Also, we forgot to switch out of the alt buffer in the previous test. Added that in. Closes #1189. --- src/host/getset.cpp | 19 ++++-- src/host/ut_host/ScreenBufferTests.cpp | 92 ++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/host/getset.cpp b/src/host/getset.cpp index 7ac745091f0..7bf5c74d97e 100644 --- a/src/host/getset.cpp +++ b/src/host/getset.cpp @@ -632,7 +632,9 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const COORD coordScreenBufferSize = context.GetBufferSize().Dimensions(); + auto& buffer = context.GetActiveBuffer(); + + const COORD coordScreenBufferSize = buffer.GetBufferSize().Dimensions(); // clang-format off RETURN_HR_IF(E_INVALIDARG, (position.X >= coordScreenBufferSize.X || position.Y >= coordScreenBufferSize.Y || @@ -643,7 +645,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont // MSFT: 15813316 - Try to use this SetCursorPosition call to inherit the cursor position. RETURN_IF_FAILED(gci.GetVtIo()->SetCursorPosition(position)); - RETURN_IF_NTSTATUS_FAILED(context.SetCursorPosition(position, true)); + RETURN_IF_NTSTATUS_FAILED(buffer.SetCursorPosition(position, true)); LOG_IF_FAILED(ConsoleImeResizeCompStrView()); @@ -651,7 +653,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont WindowOrigin.X = 0; WindowOrigin.Y = 0; { - const SMALL_RECT currentViewport = context.GetViewport().ToInclusive(); + const SMALL_RECT currentViewport = buffer.GetViewport().ToInclusive(); if (currentViewport.Left > position.X) { WindowOrigin.X = position.X - currentViewport.Left; @@ -671,7 +673,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont } } - RETURN_IF_NTSTATUS_FAILED(context.SetViewportOrigin(false, WindowOrigin, true)); + RETURN_IF_NTSTATUS_FAILED(buffer.SetViewportOrigin(false, WindowOrigin, true)); return S_OK; } @@ -820,6 +822,8 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont LockConsole(); auto Unlock = wil::scope_exit([&] { UnlockConsole(); }); + auto& buffer = context.GetActiveBuffer(); + TextAttribute useThisAttr(fillAttribute); // Here we're being a little clever - similar to FillConsoleOutputAttributeImpl @@ -835,10 +839,11 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont // this scenario is highly unlikely, and we can reasonably do this // on their behalf. // see MSFT:19853701 - if (context.InVTMode()) + + if (buffer.InVTMode()) { const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto currentAttributes = context.GetAttributes(); + const auto currentAttributes = buffer.GetAttributes(); const auto bufferLegacy = gci.GenerateLegacyAttributes(currentAttributes); if (bufferLegacy == fillAttribute) { @@ -846,7 +851,7 @@ void ApiRoutines::GetLargestConsoleWindowSizeImpl(const SCREEN_INFORMATION& cont } } - ScrollRegion(context, source, clip, target, fillCharacter, useThisAttr); + ScrollRegion(buffer, source, clip, target, fillCharacter, useThisAttr); return S_OK; } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 70f39f01403..5cb6cd0363e 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -178,6 +178,8 @@ class ScreenBufferTests TEST_METHOD(HardResetBuffer); TEST_METHOD(RestoreDownAltBufferWithTerminalScrolling); + + TEST_METHOD(ClearAlternateBuffer); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -4219,6 +4221,8 @@ void ScreenBufferTests::RestoreDownAltBufferWithTerminalScrolling() VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top()); VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); + auto useMain = wil::scope_exit([&] { altBuffer.UseMainScreenBuffer(); }); + const COORD originalSize = originalView.Dimensions(); const COORD doubledSize = { originalSize.X * 2, originalSize.Y * 2 }; @@ -4255,3 +4259,91 @@ void ScreenBufferTests::RestoreDownAltBufferWithTerminalScrolling() VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); } } + +void ScreenBufferTests::ClearAlternateBuffer() +{ + // This is a test for microsoft/terminal#1189. Refer to that issue for more + // context + + CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& g = ServiceLocator::LocateGlobals(); + gci.LockConsole(); // Lock must be taken to manipulate buffer. + auto unlock = wil::scope_exit([&] { gci.UnlockConsole(); }); + + auto& siMain = gci.GetActiveOutputBuffer(); + auto WriteText = [&](TextBuffer& tbi) { + // Write text to buffer + auto& stateMachine = siMain.GetStateMachine(); + auto& cursor = tbi.GetCursor(); + stateMachine.ProcessString(L"foo\nfoo"); + VERIFY_ARE_EQUAL(cursor.GetPosition().X, 3); + VERIFY_ARE_EQUAL(cursor.GetPosition().Y, 1); + }; + + auto VerifyText = [&](TextBuffer& tbi) { + // Verify written text in buffer + { + auto iter00 = tbi.GetCellDataAt({ 0, 0 }); + auto iter10 = tbi.GetCellDataAt({ 1, 0 }); + auto iter20 = tbi.GetCellDataAt({ 2, 0 }); + auto iter30 = tbi.GetCellDataAt({ 3, 0 }); + auto iter01 = tbi.GetCellDataAt({ 0, 1 }); + auto iter02 = tbi.GetCellDataAt({ 1, 1 }); + auto iter03 = tbi.GetCellDataAt({ 2, 1 }); + VERIFY_ARE_EQUAL(L"f", iter00->Chars()); + VERIFY_ARE_EQUAL(L"o", iter10->Chars()); + VERIFY_ARE_EQUAL(L"o", iter20->Chars()); + VERIFY_ARE_EQUAL(L"\x20", iter30->Chars()); + VERIFY_ARE_EQUAL(L"f", iter01->Chars()); + VERIFY_ARE_EQUAL(L"o", iter02->Chars()); + VERIFY_ARE_EQUAL(L"o", iter03->Chars()); + } + }; + + WriteText(siMain.GetTextBuffer()); + VerifyText(siMain.GetTextBuffer()); + + Log::Comment(L"Create an alternate buffer"); + if (VERIFY_IS_TRUE(NT_SUCCESS(siMain.UseAlternateScreenBuffer()))) + { + VERIFY_IS_NOT_NULL(siMain._psiAlternateBuffer); + auto& altBuffer = *siMain._psiAlternateBuffer; + VERIFY_ARE_EQUAL(0, altBuffer._viewport.Top()); + VERIFY_ARE_EQUAL(altBuffer._viewport.BottomInclusive(), altBuffer._virtualBottom); + + auto useMain = wil::scope_exit([&] { altBuffer.UseMainScreenBuffer(); }); + + WriteText(altBuffer.GetTextBuffer()); + VerifyText(altBuffer.GetTextBuffer()); + +#pragma region Test ScrollConsoleScreenBufferWImpl() + // Clear text of alt buffer (same params as in CMD) + VERIFY_SUCCEEDED(g.api.ScrollConsoleScreenBufferWImpl(siMain, + { 0, 0, 120, 9001 }, + { 0, -9001 }, + std::nullopt, + L' ', + 7)); + + // Verify text is now gone + VERIFY_ARE_EQUAL(L" ", altBuffer.GetTextBuffer().GetCellDataAt({ 0, 0 })->Chars()); +#pragma endregion + +#pragma region Test SetConsoleCursorPositionImpl() + // Reset cursor position as we do with CLS command (same params as in CMD) + VERIFY_SUCCEEDED(g.api.SetConsoleCursorPositionImpl(siMain, { 0 })); + + // Verify state of alt buffer + auto& altBufferCursor = altBuffer.GetTextBuffer().GetCursor(); + VERIFY_ARE_EQUAL(altBufferCursor.GetPosition().X, 0); + VERIFY_ARE_EQUAL(altBufferCursor.GetPosition().Y, 0); +#pragma endregion + } + + // Verify state of main buffer is untouched + auto& cursor = siMain.GetTextBuffer().GetCursor(); + VERIFY_ARE_EQUAL(cursor.GetPosition().X, 3); + VERIFY_ARE_EQUAL(cursor.GetPosition().Y, 1); + + VerifyText(siMain.GetTextBuffer()); +}