From 9fcf92b8ef21b1be7db7ef5c1953a67dcc418aa8 Mon Sep 17 00:00:00 2001 From: Ian O'Neill Date: Tue, 15 Feb 2022 18:21:37 +0000 Subject: [PATCH 01/75] Potentially fix Submit OneFuzz CI Job (#12489) ## Summary of the Pull Request Modifies the OneFuzz CI Job so that it attempts to read the notification config from a file rather than the command line. ## References Potential oversight in #10431. ## PR Checklist * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA ## Detailed Description of the Pull Request / Additional comments Noticed that the CI job was failing on main, so took a look. According to the [docs](https://github.com/microsoft/onefuzz/blob/7f7d76fa7fd0e351f8ffd8c7aa5c5729e30f9e8f/docs/notifications.md#implementation), files should be referenced using `@./` notation. --- build/pipelines/fuzz.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/fuzz.yml b/build/pipelines/fuzz.yml index a5580118aa3..b5604b5f227 100644 --- a/build/pipelines/fuzz.yml +++ b/build/pipelines/fuzz.yml @@ -52,7 +52,7 @@ stages: sed -i s/INSERT_ASSIGNED_HERE/$(ado_assigned_to)/ build/Fuzz/notifications-ado.json displayName: Configure OneFuzz - bash: | - onefuzz template libfuzzer basic --colocate_all_tasks --vm_count 1 --target_exe $target_exe_path --notification_config build/Fuzz/notifications-ado.json OpenConsole $test_name $(Build.SourceVersion) default + onefuzz template libfuzzer basic --colocate_all_tasks --vm_count 1 --target_exe $target_exe_path --notification_config @./build/Fuzz/notifications-ado.json OpenConsole $test_name $(Build.SourceVersion) default displayName: Submit OneFuzz Job env: target_exe_path: $(Build.ArtifactStagingDirectory)/$(artifactName)/Fuzzing/x64/test/OpenConsoleFuzzer.exe From 3b4679431df558ac35adec991f35d75545a1f896 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Tue, 15 Feb 2022 16:07:44 -0800 Subject: [PATCH 02/75] Fix font axes/features not working on DPI change (#12492) When the dpi is changed, call `updateFont()` instead of `TriggerFontChange`, this means that we continue to use the existing font features/axes Closes #11287 --- src/cascadia/TerminalControl/ControlCore.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 27202a114e0..6bca14aa18c 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -917,16 +917,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - const auto dpi = (float)(scale * USER_DEFAULT_SCREEN_DPI); - const auto actualFontOldSize = _actualFont.GetSize(); auto lock = _terminal->LockForWriting(); _compositionScale = scale; - _renderer->TriggerFontChange(::base::saturated_cast(dpi), - _desiredFont, - _actualFont); + // _updateFont relies on the new _compositionScale set above + _updateFont(); const auto actualFontNewSize = _actualFont.GetSize(); if (actualFontNewSize != actualFontOldSize) From d36cb754db0099b23fa8ee9709e63829ee42e69e Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 16 Feb 2022 15:37:54 -0800 Subject: [PATCH 03/75] Remove "Window Persistence" feature flag (#12499) Removes the `Feature_PersistedWindowLayout` feature flag so that it's always enabled. Closes #12422 --- src/cascadia/TerminalApp/TerminalPage.cpp | 3 +-- src/cascadia/TerminalSettingsEditor/Launch.cpp | 5 ----- src/cascadia/TerminalSettingsEditor/Launch.h | 2 -- src/cascadia/TerminalSettingsEditor/Launch.idl | 3 --- src/cascadia/TerminalSettingsEditor/Launch.xaml | 3 +-- src/features.xml | 9 --------- 6 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 630378a5d07..62444e2bf5c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -297,8 +297,7 @@ namespace winrt::TerminalApp::implementation // - true if the ApplicationState should be used. bool TerminalPage::ShouldUsePersistedLayout(CascadiaSettings& settings) const { - return Feature_PersistedWindowLayout::IsEnabled() && - settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; + return settings.GlobalSettings().FirstWindowPreference() == FirstWindowPreference::PersistedWindowLayout; } // Method Description: diff --git a/src/cascadia/TerminalSettingsEditor/Launch.cpp b/src/cascadia/TerminalSettingsEditor/Launch.cpp index d5c232faa35..7280e7d746b 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.cpp +++ b/src/cascadia/TerminalSettingsEditor/Launch.cpp @@ -73,9 +73,4 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation return winrt::single_threaded_observable_vector(std::move(profiles)); } - - bool Launch::ShowFirstWindowPreference() const noexcept - { - return Feature_PersistedWindowLayout::IsEnabled(); - } } diff --git a/src/cascadia/TerminalSettingsEditor/Launch.h b/src/cascadia/TerminalSettingsEditor/Launch.h index c33a4351f79..1302808cd0a 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.h +++ b/src/cascadia/TerminalSettingsEditor/Launch.h @@ -29,8 +29,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void CurrentDefaultProfile(const IInspectable& value); winrt::Windows::Foundation::Collections::IObservableVector DefaultProfiles() const; - bool ShowFirstWindowPreference() const noexcept; - WINRT_PROPERTY(Editor::LaunchPageNavigationState, State, nullptr); GETSET_BINDABLE_ENUM_SETTING(FirstWindowPreference, Model::FirstWindowPreference, State().Settings().GlobalSettings().FirstWindowPreference); diff --git a/src/cascadia/TerminalSettingsEditor/Launch.idl b/src/cascadia/TerminalSettingsEditor/Launch.idl index 1dfc9e86bb5..e7ba647d1dc 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.idl +++ b/src/cascadia/TerminalSettingsEditor/Launch.idl @@ -20,9 +20,6 @@ namespace Microsoft.Terminal.Settings.Editor // https://github.com/microsoft/microsoft-ui-xaml/issues/5395 IObservableVector DefaultProfiles { get; }; - - Boolean ShowFirstWindowPreference { get; }; - IInspectable CurrentFirstWindowPreference; IObservableVector FirstWindowPreferenceList { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/Launch.xaml b/src/cascadia/TerminalSettingsEditor/Launch.xaml index f52668f8b49..308ce6a10ab 100644 --- a/src/cascadia/TerminalSettingsEditor/Launch.xaml +++ b/src/cascadia/TerminalSettingsEditor/Launch.xaml @@ -138,8 +138,7 @@ + Style="{StaticResource ExpanderSettingContainerStyle}"> - - Feature_PersistedWindowLayout - Whether to allow the user to enable persisted window layout saving and loading - 766 - AlwaysEnabled - - - - Feature_AtlasEngine If enabled, AtlasEngine and the experimental.useAtlasEngine setting are compiled into the project From 71c75561e5df3db53cfe6f9da1173a46441b99ed Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 17 Feb 2022 18:58:31 +0100 Subject: [PATCH 04/75] Fix typos found by codespell (#12475) ## Summary of the Pull Request Fix typos found by codespell. Some of it in documentation and user-visible text, mostly in code comments. While I understand you might not be interested in fixing code comments, one of the reasons being extra noise in git history, kindly note that most spell checking tools do not discriminate between documentation and code comments. So it's easier to fix everything for long maintenance. ## References ## PR Checklist * [ ] Closes #xxx * [X] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [X] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: [#501](https://github.com/MicrosoftDocs/terminal/pull/501) * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed I have checked and re-checked all changes. --- .github/actions/spelling/expect/expect.txt | 4 ---- doc/Niksa.md | 2 +- doc/cascadia/Unittesting-CppWinRT-Xaml.md | 2 +- doc/specs/#1790 - Font features and axes-spec.md | 2 +- .../#2871 - Pane Navigation/#2871 - Pane Navigation.md | 2 +- .../#1032 - Elevation Quality of Life Improvements.md | 2 +- .../#5000 - Process Model 2.0/#5000 - Process Model 2.0.md | 2 +- doc/specs/#6899 - Action IDs/#6899 - Action IDs.md | 2 +- .../#885 - Terminal Settings Model.md | 2 +- doc/specs/TerminalSettings-spec.md | 2 +- doc/specs/drafts/#1256 - Tab tearoff.md | 2 +- doc/specs/drafts/#997 Non-Terminal-Panes.md | 2 +- samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs | 2 +- samples/ReadConsoleInputStream/ReadConsoleInputStream.cs | 4 ++-- src/buffer/out/textBuffer.cpp | 2 +- src/cascadia/TerminalApp/TerminalPage.cpp | 4 ++-- src/cascadia/TerminalApp/TerminalTab.cpp | 2 +- src/cascadia/TerminalControl/TermControl.cpp | 2 +- .../TerminalSettingsSerializationHelpers.h | 2 +- .../UnitTests_TerminalCore/ConptyRoundtripTests.cpp | 6 +++--- src/cascadia/WindowsTerminal/AppHost.cpp | 2 +- src/cascadia/WindowsTerminal/IslandWindow.cpp | 6 +++--- src/host/popup.cpp | 2 +- src/host/screenInfo.cpp | 2 +- src/host/selectionInput.cpp | 2 +- src/host/ut_host/ScreenBufferTests.cpp | 2 +- src/inc/consoletaeftemplates.hpp | 6 +++--- src/interactivity/win32/Clipboard.cpp | 2 +- .../win32/ut_interactivity_win32/UiaTextRangeTests.cpp | 2 +- src/renderer/dx/CustomTextLayout.cpp | 2 +- src/renderer/dx/DxRenderer.cpp | 2 +- src/renderer/dx/DxRenderer.hpp | 2 +- src/renderer/inc/IRenderData.hpp | 2 +- src/terminal/adapter/adaptDispatch.cpp | 4 ++-- src/terminal/adapter/ut_adapter/adapterTest.cpp | 2 +- src/terminal/adapter/ut_adapter/inputTest.cpp | 2 +- src/terminal/parser/ut_parser/StateMachineTest.cpp | 4 ++-- src/tools/MonarchPeasantSample/AppState.cpp | 2 +- src/tools/ansi-color/ansi-color.cmd | 6 +++--- src/tools/ansi-color/crisman.def | 4 ++-- src/types/viewport.cpp | 2 +- tools/bcz.cmd | 4 ++-- 42 files changed, 55 insertions(+), 59 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 3afa4c47656..6706e4ef049 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -678,7 +678,6 @@ dxp dxsm dxttbmp Dyreen -eachother eaf EASTEUROPE ECH @@ -829,7 +828,6 @@ FORCEOFFFEEDBACK FORCEONFEEDBACK FORCEV foreach -forground fprintf framebuffer FRAMECHANGED @@ -1698,7 +1696,6 @@ outfile Outof OUTOFCONTEXT OUTOFMEMORY -outout Outptr outstr OVERLAPPEDWINDOW @@ -1850,7 +1847,6 @@ pragma prc prealigned prebuilt -precendence precomp prect prefast diff --git a/doc/Niksa.md b/doc/Niksa.md index c4c7e479885..c3f8a1fa1af 100644 --- a/doc/Niksa.md +++ b/doc/Niksa.md @@ -163,7 +163,7 @@ Given that we're using Xaml islands to host a modern UI and stitching a DirectX 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. +[2] When you have two windows on the same desktop in the same window station, they can communicate with each other. 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. diff --git a/doc/cascadia/Unittesting-CppWinRT-Xaml.md b/doc/cascadia/Unittesting-CppWinRT-Xaml.md index 4160e035e2e..9ddcae046a0 100644 --- a/doc/cascadia/Unittesting-CppWinRT-Xaml.md +++ b/doc/cascadia/Unittesting-CppWinRT-Xaml.md @@ -268,7 +268,7 @@ this: ``` -Again, verify the correct paths to your dependant C++/WinRT dlls, as they may be +Again, verify the correct paths to your dependent C++/WinRT dlls, as they may be different than the above #### Activating the manifest from TAEF diff --git a/doc/specs/#1790 - Font features and axes-spec.md b/doc/specs/#1790 - Font features and axes-spec.md index 63b56230361..fd0f59bb7e9 100644 --- a/doc/specs/#1790 - Font features and axes-spec.md +++ b/doc/specs/#1790 - Font features and axes-spec.md @@ -74,7 +74,7 @@ Should not affect security. ### Reliability -Aside from additional parsing required for the settings file (which inherently offers more locations for parsing to fail), we need to be careful about badly formed/non-existant feature tags or axes specified in the user-defined dictionaries. We must make sure to ignore such declarations (perhaps alongside emitting a warning to the user) and only apply those that are correctly formed and exist. +Aside from additional parsing required for the settings file (which inherently offers more locations for parsing to fail), we need to be careful about badly formed/non-existent feature tags or axes specified in the user-defined dictionaries. We must make sure to ignore such declarations (perhaps alongside emitting a warning to the user) and only apply those that are correctly formed and exist. ### Compatibility diff --git a/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md b/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md index 64fcf5d782b..e4b5ec6a4f1 100644 --- a/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md +++ b/doc/specs/#2871 - Pane Navigation/#2871 - Pane Navigation.md @@ -241,7 +241,7 @@ So `focusPane(target=1, direction=up)` will attempt to focus the pane above pane > 👉 **NOTE**: At this point, the author considered "Do we even want a separate > action to engage the tab switcher with panes expanded?" Perhaps panes being -> visible in the tab switcher is just part fo the tab switcher's behavior. Maybe +> visible in the tab switcher is just part of the tab switcher's behavior. Maybe > there shouldn't be a separate "open the tab switcher with the panes expanded > to the pane I'm currently on, and the panes listed in MRU order" action. diff --git a/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md b/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md index 1757352e9ca..a1137a77ac9 100644 --- a/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md +++ b/doc/specs/#5000 - Process Model 2.0/#1032 - Elevation Quality of Life Improvements.md @@ -552,7 +552,7 @@ following behaviors: * `false`: If the current window is elevated, try to create a new unelevated window to host this connection. -We could always re-introduce this setting, to supercede `elevate`. +We could always re-introduce this setting, to supersede `elevate`. ### Change profile appearance for elevated windows diff --git a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md index c1a3dee38d9..e32af6b72ea 100644 --- a/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md +++ b/doc/specs/#5000 - Process Model 2.0/#5000 - Process Model 2.0.md @@ -1123,7 +1123,7 @@ elevated windows, when they trust the extension. We could have an additional set of settings the user could use to enable certain extensions in elevated windows. However, this setting cannot live in the normal `settings.json` or even `state.json` (see [#7972], since those files are writable by any medium-IL -process. Instead, this setting would ned to live in a separate file that's +process. Instead, this setting would need to live in a separate file that's protected to only be writable by elevated processes. This would ensure that an attacker could not just add their extension to the list of white-listed extensions. When the settings UI wants to modify that setting, it'll need to diff --git a/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md b/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md index c3d2871cb36..50faf9d1efb 100644 --- a/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md +++ b/doc/specs/#6899 - Action IDs/#6899 - Action IDs.md @@ -26,7 +26,7 @@ This spec was largely inspired by the following diagram from @DHowett: ![figure 1](data-mockup.png) The goal is to introduce an `id` parameter by which actions could be uniquely -refered to. If we'd ever like to use an action outside the list of `actions`, we +referred to. If we'd ever like to use an action outside the list of `actions`, we can simply refer to the action's ID, allowing the user to only define the action _once_. diff --git a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md index c7dbefeab0b..ad361218403 100644 --- a/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md +++ b/doc/specs/#885 - Terminal Settings Model/#885 - Terminal Settings Model.md @@ -230,7 +230,7 @@ def cloneGraph(oldSource, newSource, visited): for old in oldSource.adj: # Below check is for backtracking, so new - # nodes don't get initialized everytime + # nodes don't get initialized every time if clone is None or(clone is not None and clone.key != old.key): clone = Node(old.key, []) newSource.adj.append(clone) diff --git a/doc/specs/TerminalSettings-spec.md b/doc/specs/TerminalSettings-spec.md index 1bf098274a8..16d3cfbd938 100644 --- a/doc/specs/TerminalSettings-spec.md +++ b/doc/specs/TerminalSettings-spec.md @@ -58,7 +58,7 @@ VS needs to be able to persist settings just as a simple set of global settings. When the application needs to retrieve these settings, they need to use them as a tripartite structure: frontend-component-terminal settings. Each frontend will have its own set of settings. -Each component implementation will also ned to have some settings that control it. +Each component implementation will also need to have some settings that control it. The terminal also will have some settings specific to the terminal. ### Globals and Profiles diff --git a/doc/specs/drafts/#1256 - Tab tearoff.md b/doc/specs/drafts/#1256 - Tab tearoff.md index d57a695067c..fa1b2e9a0d5 100644 --- a/doc/specs/drafts/#1256 - Tab tearoff.md +++ b/doc/specs/drafts/#1256 - Tab tearoff.md @@ -124,7 +124,7 @@ There's a few areas to study here. #### Communicating the launch For the parameters passing, I see a few options: -1. `conhost.exe` can look up the package registration for `wt.exe` and call an entrypoint with arguments. This could be adapted to instead look up which package is registered as the default one instead of `wt.exe` for third party hosts. We would have to build provisions into the OS to select this, or use some sort of publically documented registry key mechanism. Somewhat gross. +1. `conhost.exe` can look up the package registration for `wt.exe` and call an entrypoint with arguments. This could be adapted to instead look up which package is registered as the default one instead of `wt.exe` for third party hosts. We would have to build provisions into the OS to select this, or use some sort of publicly documented registry key mechanism. Somewhat gross. 1. `conhost.exe` can call the execution alias with parameters. WSL distro launchers use this. 1. We can define a protocol handler for these sorts of connections and let `wt.exe` register for it. Protocol handlers are already well supported and understood both by classic applications and by packaged/modern applications on Windows. They must have provisions to communicate at least some semblance of argument data as well. This is the route I'd probably prefer. `ms-term://incoming/` or something like that. The receiving `wt.exe` can contact the manager process (or set one up if it is the first) and negotiate receiving the session that was specified into a new tab. diff --git a/doc/specs/drafts/#997 Non-Terminal-Panes.md b/doc/specs/drafts/#997 Non-Terminal-Panes.md index 512165580bd..6fb539cf33c 100644 --- a/doc/specs/drafts/#997 Non-Terminal-Panes.md +++ b/doc/specs/drafts/#997 Non-Terminal-Panes.md @@ -77,7 +77,7 @@ is a bigger discussion than the feature at hand, however. ### Performance, Power, and Efficiency decide to host a WebView in a pane, then it surely could impact these measures. -I don't believe this will have a noticable impact _on its own_. Should the user +I don't believe this will have a noticeable impact _on its own_. Should the user However, I leave that discussion to the implementation of the actual alternative pane content itself. diff --git a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs index 84d3d3d44bf..50cb6064b64 100644 --- a/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs +++ b/samples/ConPTY/MiniTerm/MiniTerm/PseudoConsole.cs @@ -26,7 +26,7 @@ internal static PseudoConsole Create(SafeFileHandle inputReadSide, SafeFileHandl 0, out IntPtr hPC); if(createResult != 0) { - throw new InvalidOperationException("Could not create psuedo console. Error Code " + createResult); + throw new InvalidOperationException("Could not create pseudo console. Error Code " + createResult); } return new PseudoConsole(hPC); } diff --git a/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs b/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs index 2b0c8088a21..cc3c9158a1e 100644 --- a/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs +++ b/samples/ReadConsoleInputStream/ReadConsoleInputStream.cs @@ -136,7 +136,7 @@ public override int Read(byte[] buffer, int offset, int count) if (record.EventType != Kernel32.EVENT_TYPE.FOCUS_EVENT) { // I assume success adding records - this is not so critical - // if it is critical to you, loop on this with a miniscule delay + // if it is critical to you, loop on this with a minuscule delay _nonKeyEvents.TryAdd(record); } } @@ -195,4 +195,4 @@ private void ValidateRead(byte[] buffer, int offset, int count) if (!CanRead) throw new NotSupportedException("Get read not supported."); } } -} \ No newline at end of file +} diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 36266710a86..777dc83c1da 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -1751,7 +1751,7 @@ const TextBuffer::TextAndColor TextBuffer::GetText(const bool includeCRLF, if (copyTextColor) { - // cant see CR/LF so just use black FG & BK + // can't see CR/LF so just use black FG & BK COLORREF const Blackness = RGB(0x00, 0x00, 0x00); selectionFgAttr.push_back(Blackness); selectionFgAttr.push_back(Blackness); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 62444e2bf5c..19ccaa195e0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -316,7 +316,7 @@ namespace winrt::TerminalApp::implementation // elevated window. if (!_startupActions || IsElevated() || _shouldStartInboundListener) { - // there arent startup actions, or we're elevated. In that case, go for it. + // there aren't startup actions, or we're elevated. In that case, go for it. return false; } @@ -1219,7 +1219,7 @@ namespace winrt::TerminalApp::implementation // message without vkey or scanCode if a user drags a tab. // The KeyChord constructor has a debug assertion ensuring that all KeyChord // either have a valid vkey/scanCode. This is important, because this prevents - // accidential insertion of invalid KeyChords into classes like ActionMap. + // accidental insertion of invalid KeyChords into classes like ActionMap. if (!vkey && !scanCode) { return; diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 0c0617066af..bacba947c5f 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -1415,7 +1415,7 @@ namespace winrt::TerminalApp::implementation selectedTabBrush.Color(color); // currently if a tab has a custom color, a deselected state is - // signified by using the same color with a bit ot transparency + // signified by using the same color with a bit of transparency auto deselectedTabColor = color; deselectedTabColor.A = 64; deselectedTabBrush.Color(deselectedTabColor); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index cf415b000c9..b6bb9e6b98b 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -936,7 +936,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation // message without vkey or scanCode if a user drags a tab. // The KeyChord constructor has a debug assertion ensuring that all KeyChord // either have a valid vkey/scanCode. This is important, because this prevents - // accidential insertion of invalid KeyChords into classes like ActionMap. + // accidental insertion of invalid KeyChords into classes like ActionMap. if (!vkey && !scanCode) { e.Handled(true); diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h index 26a83e0e42f..940843fcbce 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettingsSerializationHelpers.h @@ -133,7 +133,7 @@ JSON_ENUM_MAPPER(::winrt::Microsoft::Terminal::Settings::Model::CloseOnExitMode) }; // This specialization isn't using JSON_ENUM_MAPPER because we need to have a different -// value type (unsinged int) and return type (FontWeight struct). JSON_ENUM_MAPPER +// value type (unsigned int) and return type (FontWeight struct). JSON_ENUM_MAPPER // expects that the value type _is_ the return type. template<> struct ::Microsoft::Terminal::Settings::Model::JsonUtils::ConversionTrait<::winrt::Windows::UI::Text::FontWeight> : diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index 8a812d75a2e..0d7f72f1c6f 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -4,7 +4,7 @@ // This test class creates an in-proc conpty host as well as a Terminal, to // validate that strings written to the conpty create the same response on the // terminal end. Tests can be written that validate both the contents of the -// host buffer as well as the terminal buffer. Everytime that +// host buffer as well as the terminal buffer. Every time that // `renderer.PaintFrame()` is called, the tests will validate the expected // output, and then flush the output of the VtEngine straight to the Terminal. @@ -392,7 +392,7 @@ void ConptyRoundtripTests::WriteTwoLinesUsesNewline() void ConptyRoundtripTests::WriteAFewSimpleLines() { Log::Comment(NoThrowString().Format( - L"Write more lines of outout. We should use \r\n to move the cursor")); + L"Write more lines of output. We should use \r\n to move the cursor")); auto& g = ServiceLocator::LocateGlobals(); auto& renderer = *g.pRender; @@ -3047,7 +3047,7 @@ void ConptyRoundtripTests::NewLinesAtBottomWithBackground() // Line 2 chars: ____#_________#___ (break) // Line 2 attrs: BBBBBBBBBBBBBBDDDD (First spacesToPrint+5 are blue BG, then default attrs) // [<----->] - // This number of spaces controled by spacesToPrint + // This number of spaces controlled by spacesToPrint if (i > 0) { sm.ProcessString(L"\r\n"); diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index f1380679c38..ec539fcfa5a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -392,7 +392,7 @@ void AppHost::Initialize() } // Method Description: -// - Called everytime when the active tab's title changes. We'll also fire off +// - Called every time when the active tab's title changes. We'll also fire off // a window message so we can update the window's title on the main thread, // though we'll only do so if the settings are configured for that. // Arguments: diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index cbd2509385c..bacf5c35b5d 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -389,7 +389,7 @@ void IslandWindow::_OnGetMinMaxInfo(const WPARAM /*wParam*/, const LPARAM lParam } // Method Description: -// - Helper function that calculates a singe dimension value, given initialWindow and nonClientSizes +// - Helper function that calculates a single dimension value, given initialWindow and nonClientSizes // Arguments: // - isWidth: parameter to pass to SnapDimensionCallback. // True if the method is invoked for width computation, false if for height. @@ -1332,7 +1332,7 @@ void IslandWindow::_dropdownWindow(const uint32_t dropdownDuration, { // First, get the window that's currently in the foreground. We'll need // _this_ window to be able to appear on top of. If we just use - // GetForegroundWindow afer the SetWindowPlacement call, _we_ will be the + // GetForegroundWindow after the SetWindowPlacement call, _we_ will be the // foreground window. const auto oldForegroundWindow = GetForegroundWindow(); @@ -1389,7 +1389,7 @@ void IslandWindow::_globalActivateWindow(const uint32_t dropdownDuration, { // First, get the window that's currently in the foreground. We'll need // _this_ window to be able to appear on top of. If we just use - // GetForegroundWindow afer the SetWindowPlacement/ShowWindow call, _we_ + // GetForegroundWindow after the SetWindowPlacement/ShowWindow call, _we_ // will be the foreground window. const auto oldForegroundWindow = GetForegroundWindow(); diff --git a/src/host/popup.cpp b/src/host/popup.cpp index 5fd28534c94..34683011145 100644 --- a/src/host/popup.cpp +++ b/src/host/popup.cpp @@ -26,7 +26,7 @@ using Microsoft::Console::Interactivity::ServiceLocator; // - Creates an object representing an interactive popup overlay during cooked mode command line editing. // - NOTE: Modifies global popup count (and adjusts cursor visibility as appropriate.) // Arguments: -// - screenInfo - Reference to screen on which the popup should be drawn/overlayed. +// - screenInfo - Reference to screen on which the popup should be drawn/overlaid. // - proposedSize - Suggested size of the popup. May be adjusted based on screen size. Popup::Popup(SCREEN_INFORMATION& screenInfo, const COORD proposedSize) : _screenInfo(screenInfo), diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index f0f3e03710d..6c786b4aa42 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -2711,7 +2711,7 @@ const FontInfo& SCREEN_INFORMATION::GetCurrentFont() const noexcept // - Gets the desired font of the screen buffer. If we try loading this font and // have to fallback to another, then GetCurrentFont()!=GetDesiredFont(). // We store this separately, so that if we need to reload the font, we can -// try again with our prefered font info (in the desired font info) instead +// try again with our preferred font info (in the desired font info) instead // of re-using the looked up value from before. // Arguments: // - diff --git a/src/host/selectionInput.cpp b/src/host/selectionInput.cpp index cab5aa88b6f..64d27d9a470 100644 --- a/src/host/selectionInput.cpp +++ b/src/host/selectionInput.cpp @@ -628,7 +628,7 @@ bool Selection::_HandleColorSelection(const INPUT_KEY_INFO* const pInputKeyInfo) const WORD wVirtualKeyCode = pInputKeyInfo->GetVirtualKey(); // It's a numeric key, a text mode buffer and the color selection regkey is set, - // then check to see if the user want's to color the selection or search and + // then check to see if the user wants to color the selection or search and // highlight the selection. bool fAltPressed = pInputKeyInfo->IsAltPressed(); bool fShiftPressed = pInputKeyInfo->IsShiftPressed(); diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index a722c8bef84..2a963911fce 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -4536,7 +4536,7 @@ void ScreenBufferTests::ScrollLines256Colors() int scrollType; int colorStyle; - VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollType", scrollType), L"controls whether to use InsertLines, DeleteLines ot ReverseLineFeed"); + VERIFY_SUCCEEDED(TestData::TryGetValue(L"scrollType", scrollType), L"controls whether to use InsertLines, DeleteLines or ReverseLineFeed"); 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 diff --git a/src/inc/consoletaeftemplates.hpp b/src/inc/consoletaeftemplates.hpp index 04986dc63fa..c5242de71fb 100644 --- a/src/inc/consoletaeftemplates.hpp +++ b/src/inc/consoletaeftemplates.hpp @@ -20,9 +20,9 @@ Revision History: #include // Helper for declaring a variable to store a TEST_METHOD_PROPERTY and get it's value from the test metadata -#define INIT_TEST_PROPERTY(type, identifer, description) \ - type identifer; \ - VERIFY_SUCCEEDED(TestData::TryGetValue(L## #identifer, identifer), description); +#define INIT_TEST_PROPERTY(type, identifier, description) \ + type identifier; \ + VERIFY_SUCCEEDED(TestData::TryGetValue(L## #identifier, identifier), description); // Thinking of adding a new VerifyOutputTraits for a new type? MAKE SURE that // you include this header (or at least the relevant definition) before _every_ diff --git a/src/interactivity/win32/Clipboard.cpp b/src/interactivity/win32/Clipboard.cpp index e2d285795d3..2e60c4afb93 100644 --- a/src/interactivity/win32/Clipboard.cpp +++ b/src/interactivity/win32/Clipboard.cpp @@ -340,7 +340,7 @@ bool Clipboard::FilterCharacterOnPaste(_Inout_ WCHAR* const pwch) { switch (*pwch) { - // swallow tabs to prevent inadvertant tab expansion + // swallow tabs to prevent inadvertent tab expansion case UNICODE_TAB: { fAllowChar = false; diff --git a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp index 9f9a79a07bf..ac93141e1dd 100644 --- a/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp +++ b/src/interactivity/win32/ut_interactivity_win32/UiaTextRangeTests.cpp @@ -1667,7 +1667,7 @@ class UiaTextRangeTests VERIFY_SUCCEEDED(utr->GetAttributeValue(UIA_UnderlineStyleAttributeId, &result)); VERIFY_ARE_EQUAL(TextDecorationLineStyle_Single, result.lVal); - // Double underline (double supercedes single) + // Double underline (double supersedes single) attr.SetDoublyUnderlined(true); updateBuffer(attr); VERIFY_SUCCEEDED(utr->GetAttributeValue(UIA_UnderlineStyleAttributeId, &result)); diff --git a/src/renderer/dx/CustomTextLayout.cpp b/src/renderer/dx/CustomTextLayout.cpp index 0f75c46b1fd..b958b914e89 100644 --- a/src/renderer/dx/CustomTextLayout.cpp +++ b/src/renderer/dx/CustomTextLayout.cpp @@ -1578,7 +1578,7 @@ CATCH_RETURN(); // Arguments: // - // Return Value: -// - Mutable reference ot the current run. +// - Mutable reference of the current run. [[nodiscard]] CustomTextLayout::LinkedRun& CustomTextLayout::_GetCurrentRun() { return _runs.at(_runIndex); diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 42055233ce5..68ba9ff684e 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -591,7 +591,7 @@ try RETURN_IF_FAILED(_d3dDevice.As(&_dxgiDevice)); RETURN_IF_FAILED(_d2dFactory->CreateDevice(_dxgiDevice.Get(), _d2dDevice.ReleaseAndGetAddressOf())); - // Create a device context out of it (supercedes render targets) + // Create a device context out of it (supersedes render targets) RETURN_IF_FAILED(_d2dDevice->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, &_d2dDeviceContext)); if (createSwapChain) diff --git a/src/renderer/dx/DxRenderer.hpp b/src/renderer/dx/DxRenderer.hpp index e93c3f1c7c2..7cfe9834abb 100644 --- a/src/renderer/dx/DxRenderer.hpp +++ b/src/renderer/dx/DxRenderer.hpp @@ -231,7 +231,7 @@ namespace Microsoft::Console::Render // Experimental and deprecated retro terminal effect // Preserved for backwards compatibility // Implemented in terms of the more generic pixel shader effect - // Has precendence over pixel shader effect + // Has precedence over pixel shader effect bool _retroTerminalEffect; // Experimental and pixel shader effect diff --git a/src/renderer/inc/IRenderData.hpp b/src/renderer/inc/IRenderData.hpp index 6e4f1b9183f..722c31528ff 100644 --- a/src/renderer/inc/IRenderData.hpp +++ b/src/renderer/inc/IRenderData.hpp @@ -27,7 +27,7 @@ namespace Microsoft::Console::Render // This is where the data is stored const TextBuffer& buffer; - // This is where the top left of the stored buffer should be overlayed on the screen + // This is where the top left of the stored buffer should be overlaid on the screen // (relative to the current visible viewport) const COORD origin; diff --git a/src/terminal/adapter/adaptDispatch.cpp b/src/terminal/adapter/adaptDispatch.cpp index 48d89483f01..773087fa539 100644 --- a/src/terminal/adapter/adaptDispatch.cpp +++ b/src/terminal/adapter/adaptDispatch.cpp @@ -877,7 +877,7 @@ void AdaptDispatch::_WriteResponse(const std::wstring_view reply) const // TODO GH#4954 During the input refactor we may want to add a "priority" input list // to make sure that "response" input is spooled directly into the application. // We switched this to an append (vs. a prepend) to fix GH#1637, a bug where two CPR - // could collide with eachother. + // could collide with each other. _pConApi->WriteInput(inEvents, eventsWritten); } @@ -1578,7 +1578,7 @@ void AdaptDispatch::_ResetTabStops() noexcept // current screen width, initializing tab stops every 8 columns in the // newly allocated space, iff the _initDefaultTabStops flag is set. // Arguments: -// - width - the width of the screen buffer that we need to accomodate +// - width - the width of the screen buffer that we need to accommodate // Return value: // - void AdaptDispatch::_InitTabStopsForWidth(const size_t width) diff --git a/src/terminal/adapter/ut_adapter/adapterTest.cpp b/src/terminal/adapter/ut_adapter/adapterTest.cpp index f102b168e87..c75be3a58b2 100644 --- a/src/terminal/adapter/ut_adapter/adapterTest.cpp +++ b/src/terminal/adapter/ut_adapter/adapterTest.cpp @@ -1684,7 +1684,7 @@ class AdapterTest Log::Comment(L"Test 2: Verify multiple CPRs with a cursor move between them"); _testGetSet->PrepData(CursorX::XCENTER, CursorY::YCENTER); - // enable retention so that the two DSR responses don't delete eachother + // enable retention so that the two DSR responses don't delete each other auto retentionScope{ _testGetSet->EnableInputRetentionInScope() }; // start with the cursor position in the buffer. diff --git a/src/terminal/adapter/ut_adapter/inputTest.cpp b/src/terminal/adapter/ut_adapter/inputTest.cpp index fe19266a474..368c792e4cc 100644 --- a/src/terminal/adapter/ut_adapter/inputTest.cpp +++ b/src/terminal/adapter/ut_adapter/inputTest.cpp @@ -464,7 +464,7 @@ void InputTest::TerminalInputModifierKeyTests() case VK_TAB: if (AltPressed(uiKeystate)) { - // Alt+Tab isn't possible - thats reserved by the system. + // Alt+Tab isn't possible - that's reserved by the system. continue; } else if (ShiftPressed(uiKeystate)) diff --git a/src/terminal/parser/ut_parser/StateMachineTest.cpp b/src/terminal/parser/ut_parser/StateMachineTest.cpp index 67137beee6c..125036c16f7 100644 --- a/src/terminal/parser/ut_parser/StateMachineTest.cpp +++ b/src/terminal/parser/ut_parser/StateMachineTest.cpp @@ -149,7 +149,7 @@ class Microsoft::Console::VirtualTerminal::StateMachineTest return true; } - TEST_METHOD(TwoStateMachinesDoNotInterfereWithEachother); + TEST_METHOD(TwoStateMachinesDoNotInterfereWithEachOther); TEST_METHOD(PassThroughUnhandled); TEST_METHOD(RunStorageBeforeEscape); @@ -159,7 +159,7 @@ class Microsoft::Console::VirtualTerminal::StateMachineTest TEST_METHOD(DcsDataStringsReceivedByHandler); }; -void StateMachineTest::TwoStateMachinesDoNotInterfereWithEachother() +void StateMachineTest::TwoStateMachinesDoNotInterfereWithEachOther() { auto firstEnginePtr{ std::make_unique() }; // this dance is required because StateMachine presumes to take ownership of its engine. diff --git a/src/tools/MonarchPeasantSample/AppState.cpp b/src/tools/MonarchPeasantSample/AppState.cpp index 8eb2c164b59..0a4ebfed518 100644 --- a/src/tools/MonarchPeasantSample/AppState.cpp +++ b/src/tools/MonarchPeasantSample/AppState.cpp @@ -56,7 +56,7 @@ void AppState::remindKingWhoTheyAre(const winrt::MonarchPeasantSample::IPeasant& } else { - printf("Shoot, we wanted to be able to get the monarchImpl here but couldnt\n"); + printf("Shoot, we wanted to be able to get the monarchImpl here but couldn't\n"); } } diff --git a/src/tools/ansi-color/ansi-color.cmd b/src/tools/ansi-color/ansi-color.cmd index 3f215f960bc..a0169cf3b62 100644 --- a/src/tools/ansi-color/ansi-color.cmd +++ b/src/tools/ansi-color/ansi-color.cmd @@ -94,7 +94,7 @@ REM SET "SEPARATOR.CELL=╎" & :: UTF-8 IF DEFINED SHOW.UTF8 (SET "SEPARATOR.STUBHEAD_BOXHEAD=│") ELSE (SET "SEPARATOR.STUBHEAD_BOXHEAD=:") IF DEFINED SHOW.UTF8 (SET "SEPARATOR.STUBHEAD_STUB=─") ELSE (SET "SEPARATOR.STUBHEAD_STUB=-") IF DEFINED SHOW.UTF8 (SET "SEPARATOR.INTERSECT=┘") ELSE (SET "SEPARATOR.INTERSECT=+") -:: You can also define control for formating +:: You can also define control for formatting SET "SECTION=!CSI!1;4m" __TABLE:END__ @@ -1402,7 +1402,7 @@ SET @counter=FOR %%. IN (1 2) DO IF [%%.] EQU [2] (%\n% :: @exit [ErrorLevel] :: :: Used to exit and optionally sets an error code if provided. -:: This is prefered for exiting a script over GOTO :EOF for consistency +:: This is preferred for exiting a script over GOTO :EOF for consistency :: and to pass Error Levels if necessary. This tidy's up a call :: to EXIT /B [ErrorLevel] so the use feels the same as other macros. :: @@ -1535,7 +1535,7 @@ ECHO. :: # :: # This file echoes a bunch of color codes to the :: # terminal to demonstrate what's available. Each -:: # line is the color code of one forground color, +:: # line is the color code of one foreground color, :: # out of 17 (default + 16 escapes), followed by a :: # test use of that color on all nine background :: # colors (default + 8 escapes). diff --git a/src/tools/ansi-color/crisman.def b/src/tools/ansi-color/crisman.def index bacfe408fce..1705ef70225 100644 --- a/src/tools/ansi-color/crisman.def +++ b/src/tools/ansi-color/crisman.def @@ -2,7 +2,7 @@ :: # :: # This file echoes a bunch of color codes to the :: # terminal to demonstrate what's available. Each -:: # line is the color code of one forground color, +:: # line is the color code of one foreground color, :: # out of 17 (default + 16 escapes), followed by a :: # test use of that color on all nine background :: # colors (default + 8 escapes). @@ -77,4 +77,4 @@ m 37m 1;37m __ROWS:END__ -__DATA:END__ \ No newline at end of file +__DATA:END__ diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index ac3ac498f49..9038a60cca5 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -910,7 +910,7 @@ Viewport Viewport::ToOrigin() const noexcept } // Function Description: -// - Creates a viewport from the intersection fo both the parameter viewports. +// - Creates a viewport from the intersection of both the parameter viewports. // The result will be the smallest area that fits within both rectangles. // Arguments: // - lhs: one of the viewports to intersect diff --git a/tools/bcz.cmd b/tools/bcz.cmd index d70e61ddbd0..c95fbe21fff 100644 --- a/tools/bcz.cmd +++ b/tools/bcz.cmd @@ -122,8 +122,8 @@ 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. +rem the variables at the time the if was executed. So instead, make a function +rem here with `enabledelayedexpansion` set. :get_project setlocal enabledelayedexpansion From 961c576bca9eaba8d1a028cacdf338d985b8e19c Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 18 Feb 2022 14:54:44 +0100 Subject: [PATCH 05/75] Replace "acrylic" with "acrylic material" in i18n strings (#12505) "Acrylic material" is the official name as used on Microsoft Docs and should ensure it gets properly translated in all languages. ## PR Checklist * [x] Closes #9846 * [x] Closes MSFT:36776499 * [x] I work here ## Detailed Description of the Pull Request / Additional comments We originally intended to replace the strings with "acrylic transparency", but "acrylic material" is actually the official term on Microsoft Docs. --- doc/cascadia/profiles.schema.json | 4 +-- .../Resources/en-US/Resources.resw | 26 +++++-------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 191975b9638..b7abd93b661 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1761,7 +1761,7 @@ }, "useAcrylicInTabRow": { "default": false, - "description": "When set to true, the tab row will have an acrylic background with 50% opacity.", + "description": "When set to true, the tab row will have an acrylic material background with 50% opacity.", "type": "boolean" }, "actions": { @@ -2224,7 +2224,7 @@ }, "useAcrylic": { "default": false, - "description": "When set to true, the window will have an acrylic background. When set to false, the window will have a plain, untextured background.", + "description": "When set to true, the window will have an acrylic material background. When set to false, the window will have a plain, untextured background.", "type": "boolean" } }, diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 01ffb91a0b6..a069c7de6ec 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -364,12 +364,12 @@ A description for what the "show titlebar" setting does. Presented near "Globals_ShowTitlebar.Header". - Show acrylic in tab row (requires relaunch) - Header for a control to toggle whether acrylic shows in the tab row. Changing this setting requires the user to relaunch the app. + Use acrylic material in the tab row (requires relaunch) + Header for a control to toggle whether "acrylic material" is used. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic When checked, the tab row will have the acrylic material. - A description for what the "use acrylic in tab row" setting does. Presented near "Globals_AcrylicTabRow.Header". + A description for the "Globals_AcrylicTabRow.Header" setting does. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic Use active terminal title as application title @@ -519,14 +519,6 @@ Actions Tooltip for the "actions" menu item. - - Acrylic opacity - Header for a control to determine the level of opacity for the acrylic rendering material. The user can choose to make the acrylic background of the app more or less opaque. - - - Sets the transparency of the window. - A description for what the "acrylic opacity" setting does. Presented near "Profile_AcrylicOpacity.Header". - Background opacity Name for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. @@ -976,12 +968,12 @@ Button label that deletes the unfocused appearance for this profile. - Enable acrylic - Header for a control to toggle the acrylic-like rendering of the background. The acrylic material creates a translucent texture. + Enable acrylic material + Header for a control to toggle the use of acrylic material for the background. "Acrylic material" is a Microsoft-specific term: https://docs.microsoft.com/en-us/windows/apps/design/style/acrylic Applies a translucent texture to the background of the window. - A description for what the "enable acrylic" setting does. Presented near "Profile_UseAcrylic". + A description for what the "Profile_UseAcrylic" setting does. Use desktop wallpaper @@ -1166,13 +1158,9 @@ Name Header for a control to determine the name of the profile. This is a text box. - - Acrylic - Header for a group of settings related to the acrylic texture rendering on the background of the app. - Transparency - Header for a group of settings related to transparency, including the acrylic texture rendering on the background of the app. + Header for a group of settings related to transparency, including the acrylic material background of the app. Background image From 788d33ce94d28e2903bc49f841ce279211b7f557 Mon Sep 17 00:00:00 2001 From: PankajBhojwani Date: Fri, 18 Feb 2022 14:31:39 -0800 Subject: [PATCH 06/75] No longer load content dialogs when there is already one being shown (#12517) ## Summary of the Pull Request Somehow, the controls v2 update caused an issue where if you as much as _load_ a content dialog when there's already one open, we get holes in the terminal window (#12447) This commit introduces logic to `TerminalPage` to check whether there is a content dialog open before we try to load another one. ## PR Checklist * [x] Closes #12447 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [ ] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [x] I work here ## Validation Steps Performed Can no longer repro #12447 --- src/cascadia/TerminalApp/AppLogic.cpp | 8 +++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/AppLogic.idl | 7 +-- src/cascadia/TerminalApp/TerminalPage.cpp | 62 ++++++++++++---------- src/cascadia/TerminalApp/TerminalPage.h | 3 ++ src/cascadia/TerminalApp/TerminalPage.idl | 2 + src/cascadia/TerminalApp/TerminalPage.xaml | 8 +++ 7 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index fc63fc0cc37..71698f7c4c2 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -396,6 +396,14 @@ namespace winrt::TerminalApp::implementation } } + // Method Description: + // - Returns true if there is no dialog currently being shown (meaning that we can show a dialog) + // - Returns false if there is a dialog currently being shown (meaning that we cannot show another dialog) + bool AppLogic::CanShowDialog() + { + return (_dialog == nullptr); + } + // Method Description: // - Displays a dialog for errors found while loading or validating the // settings. Uses the resources under the provided title and content keys diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 1d43f113ba5..582866704b4 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -122,6 +122,7 @@ namespace winrt::TerminalApp::implementation bool GetShowTitleInTitlebar(); winrt::Windows::Foundation::IAsyncOperation ShowDialog(winrt::Windows::UI::Xaml::Controls::ContentDialog dialog); + bool CanShowDialog(); void DismissDialog(); Windows::Foundation::Collections::IMapView GlobalHotkeys(); diff --git a/src/cascadia/TerminalApp/AppLogic.idl b/src/cascadia/TerminalApp/AppLogic.idl index 57e7a914e25..df0f7b3cd47 100644 --- a/src/cascadia/TerminalApp/AppLogic.idl +++ b/src/cascadia/TerminalApp/AppLogic.idl @@ -33,6 +33,8 @@ namespace TerminalApp SystemMenuItemHandler Handler { get; }; }; + // See IDialogPresenter and TerminalPage's DialogPresenter for more + // information. [default_interface] runtimeclass AppLogic : IDirectKeyListener, IDialogPresenter { AppLogic(); @@ -106,11 +108,6 @@ namespace TerminalApp Windows.Foundation.Collections.IMapView GlobalHotkeys(); - // See IDialogPresenter and TerminalPage's DialogPresenter for more - // information. - Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); - void DismissDialog(); - event Windows.Foundation.TypedEventHandler SetTitleBarContent; event Windows.Foundation.TypedEventHandler TitleChanged; event Windows.Foundation.TypedEventHandler LastTabClosed; diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 19ccaa195e0..8f79e9b8090 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -685,10 +685,7 @@ namespace winrt::TerminalApp::implementation // Notes link, and privacy policy link. void TerminalPage::_ShowAboutDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - presenter.ShowDialog(FindName(L"AboutDialog").try_as()); - } + _ShowDialogHelper(L"AboutDialog"); } winrt::hstring TerminalPage::ApplicationDisplayName() @@ -708,6 +705,33 @@ namespace winrt::TerminalApp::implementation ShellExecute(nullptr, nullptr, currentPath.c_str(), nullptr, nullptr, SW_SHOW); } + // Method description: + // - Called when the user closes a content dialog + // - Tells the presenter to update its knowledge of whether there is a content dialog open + void TerminalPage::_DialogCloseClick(const IInspectable&, + const ContentDialogButtonClickEventArgs&) + { + if (auto presenter{ _dialogPresenter.get() }) + { + presenter.DismissDialog(); + } + } + + // Method Description: + // - Helper to show a content dialog + // - We only open a content dialog if there isn't one open already + winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowDialogHelper(const std::wstring_view& name) + { + if (auto presenter{ _dialogPresenter.get() }) + { + if (presenter.CanShowDialog()) + { + co_return co_await presenter.ShowDialog(FindName(name).try_as()); + } + } + co_return ContentDialogResult::None; + } + // Method Description: // - Displays a dialog to warn the user that they are about to close all open windows. // Once the user clicks the OK button, shut down the application. @@ -716,11 +740,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowQuitDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"QuitDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"QuitDialog"); } // Method Description: @@ -732,22 +752,14 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCloseWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"CloseAllDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"CloseAllDialog"); } // Method Description: // - Displays a dialog for warnings found while closing the terminal tab marked as read-only winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowCloseReadOnlyDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"CloseReadOnlyDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"CloseReadOnlyDialog"); } // Method Description: @@ -760,11 +772,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowMultiLinePasteWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"MultiLinePasteDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"MultiLinePasteDialog"); } // Method Description: @@ -775,11 +783,7 @@ namespace winrt::TerminalApp::implementation // when this is called, nothing happens. See _ShowDialog for details winrt::Windows::Foundation::IAsyncOperation TerminalPage::_ShowLargePasteWarningDialog() { - if (auto presenter{ _dialogPresenter.get() }) - { - co_return co_await presenter.ShowDialog(FindName(L"LargePasteDialog").try_as()); - } - co_return ContentDialogResult::None; + return _ShowDialogHelper(L"LargePasteDialog"); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index a1867178590..2e48d9db883 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -215,12 +215,15 @@ namespace winrt::TerminalApp::implementation std::shared_ptr _windowIdToast{ nullptr }; std::shared_ptr _windowRenameFailedToast{ nullptr }; + winrt::Windows::Foundation::IAsyncOperation _ShowDialogHelper(const std::wstring_view& name); + void _ShowAboutDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowQuitDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowCloseReadOnlyDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowMultiLinePasteWarningDialog(); winrt::Windows::Foundation::IAsyncOperation _ShowLargePasteWarningDialog(); + void _DialogCloseClick(const IInspectable& sender, const Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs& eventArgs); void _CreateNewTabFlyout(); void _OpenNewTabDropdown(); diff --git a/src/cascadia/TerminalApp/TerminalPage.idl b/src/cascadia/TerminalApp/TerminalPage.idl index 979483d0ad8..b726fcf23ef 100644 --- a/src/cascadia/TerminalApp/TerminalPage.idl +++ b/src/cascadia/TerminalApp/TerminalPage.idl @@ -14,6 +14,8 @@ namespace TerminalApp interface IDialogPresenter { Windows.Foundation.IAsyncOperation ShowDialog(Windows.UI.Xaml.Controls.ContentDialog dialog); + Boolean CanShowDialog(); + void DismissDialog(); }; [default_interface] runtimeclass TerminalPage : Windows.UI.Xaml.Controls.Page, Windows.UI.Xaml.Data.INotifyPropertyChanged diff --git a/src/cascadia/TerminalApp/TerminalPage.xaml b/src/cascadia/TerminalApp/TerminalPage.xaml index f7855356634..5ca8ed25858 100644 --- a/src/cascadia/TerminalApp/TerminalPage.xaml +++ b/src/cascadia/TerminalApp/TerminalPage.xaml @@ -75,6 +75,7 @@ @@ -96,21 +97,25 @@ @@ -145,6 +152,7 @@ From 722aafadeb5601002464515252d5e8f190f7dee0 Mon Sep 17 00:00:00 2001 From: Ian O'Neill Date: Tue, 22 Feb 2022 15:20:05 +0000 Subject: [PATCH 07/75] Don't crash trying to parse a command line that's a directory (#12538) ## Summary of the Pull Request Prevents a crash that could occur when invoking `wt C:\` ## PR Checklist * [x] Closes #12535 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments Updates `CascadiaSettings::NormalizeCommandLine()` to check that there are a suitable number of command line arguments to be concatenated together, to prevent accessing an array index in `argv` that doesn't exist. Also prevents a test flake that could occur in `TerminalSettingsTests::CommandLineToArgvW()`, due to generating an empty command line argument. ## Validation Steps Performed Added a test, and checked that invoking each of the command lines below behaved as expected: ``` wtd C:\ # Window pops up with [error 2147942405 (0x80070005) when launching `C:\'] wtd C:\Program Files # Window pops up with [error 2147942402 (0x80070002) when launching `C:\Program Files'] wtd cmd # cmd profile pops up wtd C:\Program Files\Powershell\7\pwsh -WorkingDirectory C:\ # PowerShell profile pops up in C:\ wtd "C:\Program Files\Powershell\7\pwsh" -WorkingDirectory C:\ # PowerShell profile pops up in C:\ wtd . # Window pops up with [error 2147942405 (0x80070005) when launching `.'] ``` --- .../TerminalSettingsTests.cpp | 20 ++++++++++++++----- .../CascadiaSettings.cpp | 11 +++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index 4f1ce422c14..ad2463bbb70 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -85,7 +85,8 @@ namespace SettingsModelLocalTests for (int i = 0; i < expectedArgc; ++i) { const bool useQuotes = static_cast(rng(2)); - const auto count = static_cast(rng(64)); + // We need to ensure there is at least one character + const auto count = static_cast(rng(64) + 1); const auto ch = static_cast(rng('z' - 'a' + 1) + 'a'); if (i != 0) @@ -107,6 +108,7 @@ namespace SettingsModelLocalTests input.push_back(L'"'); } } + Log::Comment(NoThrowString().Format(input.c_str())); int argc; wil::unique_hlocal_ptr argv{ ::CommandLineToArgvW(input.c_str(), &argc) }; @@ -168,10 +170,18 @@ namespace SettingsModelLocalTests touch(file1); touch(file2); - const auto commandLine = file2.native() + LR"( -foo "bar1 bar2" -baz)"s; - const auto expected = file2.native() + L"\0-foo\0bar1 bar2\0-baz"s; - const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine.c_str()); - VERIFY_ARE_EQUAL(expected, actual); + { + const auto commandLine = file2.native() + LR"( -foo "bar1 bar2" -baz)"s; + const auto expected = file2.native() + L"\0-foo\0bar1 bar2\0-baz"s; + const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine.c_str()); + VERIFY_ARE_EQUAL(expected, actual); + } + { + const auto commandLine = L"C:\\"; + const auto expected = L"C:\\"; + const auto actual = implementation::CascadiaSettings::NormalizeCommandLine(commandLine); + VERIFY_ARE_EQUAL(expected, actual); + } } void TerminalSettingsTests::GetProfileForArgsWithCommandline() diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 4935cee4b9c..b3f49688a26 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -744,12 +744,17 @@ std::wstring CascadiaSettings::NormalizeCommandLine(LPCWSTR commandLine) break; } } + // All other error types aren't handled at the moment. + else if (status != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + break; + } // If the file path couldn't be found by SearchPathW this could be the result of us being given a commandLine - // like "C:\foo bar\baz.exe -arg" which is resolved to the argv array {"C:\foo", "bar\baz.exe", "-arg"}. + // like "C:\foo bar\baz.exe -arg" which is resolved to the argv array {"C:\foo", "bar\baz.exe", "-arg"}, + // or we were erroneously given a directory to execute (e.g. someone ran `wt .`). // Just like CreateProcessW() we thus try to concatenate arguments until we successfully resolve a valid path. // Of course we can only do that if we have at least 2 remaining arguments in argv. - // All other error types aren't handled at the moment. - else if ((argc - startOfArguments) < 2 || status != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + if ((argc - startOfArguments) < 2) { break; } From 4016a9fd615a3d3763e7a8ea4f99d7f313a56eec Mon Sep 17 00:00:00 2001 From: James Holderness Date: Tue, 22 Feb 2022 17:58:52 +0000 Subject: [PATCH 08/75] Commit updated TerminalApp.Unit.Tests.manifest file (#12549) ## Summary of the Pull Request In PR #12462, a new `maxversiontested` entry was added to the _WindowsTerminal.manifest_ file which now gets propagated to the _TerminalApp.Unit.Tests.manifest_ file whenever a full build is executed. This PR is just committing the updated test manifest. ## PR Checklist * [x] Closes #12543 * [x] CLA signed. * [ ] Tests added/passed * [ ] Documentation updated. * [ ] Schema updated. * [x] I've discussed this with core contributors already. Issue number where discussion took place: #12543 --- src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest b/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest index 47612326b28..8e1afe241f0 100644 --- a/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest +++ b/src/cascadia/ut_app/TerminalApp.Unit.Tests.manifest @@ -14,6 +14,11 @@ + + + From 30aa51427644ee0f1067133a3c39a60099076fb7 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 22 Feb 2022 11:59:40 -0600 Subject: [PATCH 09/75] Protect the command palette against being paged with no items (#12528) Fixes two crashes amounting to 14% of our crash burden in Simulated Selfhost. I can't reproduce this organically, but I was able to do so by forcing the command palette to be empty. --- src/cascadia/TerminalApp/CommandPalette.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp index 382e599ac35..68a8a6c003c 100644 --- a/src/cascadia/TerminalApp/CommandPalette.cpp +++ b/src/cascadia/TerminalApp/CommandPalette.cpp @@ -164,11 +164,16 @@ namespace winrt::TerminalApp::implementation // - the approximate number of items visible in the list (in other words the size of the page) uint32_t CommandPalette::_getNumVisibleItems() { - const auto container = _filteredActionsView().ContainerFromIndex(0); - const auto item = container.try_as(); - const auto itemHeight = ::base::saturated_cast(item.ActualHeight()); - const auto listHeight = ::base::saturated_cast(_filteredActionsView().ActualHeight()); - return listHeight / itemHeight; + if (const auto container = _filteredActionsView().ContainerFromIndex(0)) + { + if (const auto item = container.try_as()) + { + const auto itemHeight = ::base::saturated_cast(item.ActualHeight()); + const auto listHeight = ::base::saturated_cast(_filteredActionsView().ActualHeight()); + return listHeight / itemHeight; + } + } + return 0; } // Method Description: From 9ab4abfb5471e91235e34379bf90e24ec3a22c67 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 22 Feb 2022 19:11:23 +0100 Subject: [PATCH 10/75] AtlasEngine: Fix inverted cursor alpha (#12548) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation only inverted the RGB values of the cell, but failed to account for situations where the `color` is transparent, which is the case when `backgroundOpaqueMixin` is 0 (for instance if acrylic backgrounds are enabled). In these situations the alpha component remained 0 which caused the cursor to be invisible. For some inexplicable reason this issue is only visible on a HDR display, even though it should also effect regular ones. God knows why. With this commit the cursor texture is treated as a mask that inverts the color. We use branching here, because I couldn't come up with a more clever solution. ## PR Checklist * [x] Closes #12507 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Cursor is visible on a HDR display with acrylic background ✅ * TBD performance benchmark for `[branch]` ❌ --- src/renderer/atlas/shader_ps.hlsl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index 15a24ae6c0e..a6f74899a90 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -142,10 +142,13 @@ float4 main(float4 pos: SV_Position): SV_Target } // Layer 3 (optional): - // Uncolored cursors invert the cells color. - if ((cell.flags & CellFlags_Cursor) && cursorColor == INVALID_COLOR) + // Uncolored cursors are used as a mask that inverts the cells color. + [branch] if (cell.flags & CellFlags_Cursor) { - color.rgb = abs(glyphs[cellPos].rgb - color.rgb); + [flatten] if (cursorColor == INVALID_COLOR && glyphs[cellPos].a != 0) + { + color = float4(1 - color.rgb, 1); + } } // Layer 4: From c62705ea9f90472a58ccabc2287ac1d565f19ec1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 24 Feb 2022 12:41:00 -0600 Subject: [PATCH 11/75] Update light theme tab background for greater contrast. (#12529) Changes the tab view BG to `#e8e8e8`, as discussed in mail thread. Closes #12398 --- src/cascadia/TerminalApp/App.xaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index 07c70c6b72f..074aed6ab1c 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -77,8 +77,16 @@ + + + Color="#E8E8E8" /> From 53a454fbd3e2992be5c1f2c8ed9e2c2974f0e7b8 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 24 Feb 2022 18:09:28 -0600 Subject: [PATCH 12/75] build: ship a Win11 build of Terminal that's <=half the size (#12560) Four (4) squashed changes, with messages preserved. ## release: move symbol publication into its own phase Right now, symbol publication happens every time we produce a final bundle. In the future, we may be producing multiple bundles from the same pipeline run, and we need to make sure we only do *one* symbol publication to MSDL. When we do that, it will be advantageous for us to have just one phase that source-indexes and publishes all of the symbols. ## Remove Terminal's built-in copy of the VC Runtime This removes the trick we pulled in #5661 and saves us ~550kb per arch. Some of our dependencies still depend on the "app" versions of the runtime libraries, so we are going to continue shipping the forwarders in our package. Build rules have been updated to remove the non-Desktop VCLibs dependency to slim down our package graph. This is not a problem on Windows 11 -- it looks like it's shipped inbox. **BREAKING CHANGE**: When launched unpackaged, Terminal now requires the vcruntime redist to be installed. ## Prepare for toggling XAML between 2.7.0 and -prerelease on Win11 common.openconsole.props is a pretty good place to stash the XAML version since it is included in every project (including the WAP project (unlike the C++ build props!)). I've gone ahead and added a "double dependency" on multiple XAML versions. We'll toggle them with a build flag. ## Run the release pipeline twice, for Win10 and Win11, at the same time This required some changes in how we download artifacts to make sure that we could control which version of Windows we were processing in any individual step. We're also going to patch the package manifest on the Windows 11 version so the store targets it more specifically. On top of the prior three steps, this lets us ship a Windows 11 package that costs only ~15MB on disk. The Windows 10 version, for comparison, is about 40. --- .github/actions/spelling/allow/microsoft.txt | 8 +- build/pipelines/release.yml | 170 +++++++++++------- .../Patch-ManifestsToWindowsVersion.ps1 | 14 ++ common.openconsole.props | 14 ++ custom.props | 12 ++ .../ScratchIslandApp/Package/Package.wapproj | 4 +- .../SampleApp/SampleAppLib.vcxproj | 4 +- .../SampleApp/dll/SampleApp.vcxproj | 4 +- .../SampleApp/packages.config | 2 +- .../WindowExe/WindowExe.vcxproj | 4 +- .../WindowExe/packages.config | 2 +- .../CascadiaPackage/CascadiaPackage.wapproj | 22 ++- .../SettingsModel.LocalTests.vcxproj | 8 +- .../TerminalApp.LocalTests.vcxproj | 4 +- .../TestHostApp/TestHostApp.vcxproj | 2 +- .../TerminalApp/TerminalAppLib.vcxproj | 4 +- .../TerminalApp/dll/TerminalApp.vcxproj | 4 +- src/cascadia/TerminalApp/packages.config | 1 + ...Microsoft.Terminal.Settings.Editor.vcxproj | 4 +- .../TerminalSettingsEditor/packages.config | 2 +- ...crosoft.Terminal.Settings.ModelLib.vcxproj | 4 +- .../Microsoft.Terminal.Settings.Model.vcxproj | 4 +- .../WindowsTerminal/WindowsTerminal.vcxproj | 25 +-- src/cascadia/WindowsTerminal/packages.config | 4 +- .../ut_app/TerminalApp.UnitTests.vcxproj | 6 +- src/cppwinrt.build.pre.props | 8 +- 26 files changed, 200 insertions(+), 140 deletions(-) create mode 100644 build/scripts/Patch-ManifestsToWindowsVersion.ps1 diff --git a/.github/actions/spelling/allow/microsoft.txt b/.github/actions/spelling/allow/microsoft.txt index 13197f167b2..1f4a28664f2 100644 --- a/.github/actions/spelling/allow/microsoft.txt +++ b/.github/actions/spelling/allow/microsoft.txt @@ -19,6 +19,7 @@ CPRs cryptbase DACL DACLs +defaultlib diffs disposables dotnetfeed @@ -27,6 +28,8 @@ DWINRT enablewttlogging Intelli IVisual +libucrt +libucrtd LKG LOCKFILE Lxss @@ -36,10 +39,11 @@ microsoftonline MSAA msixbundle MSVC +MSVCP muxc netcore -osgvsowi Onefuzz +osgvsowi PFILETIME pgc pgo @@ -63,6 +67,8 @@ systemroot taskkill tasklist tdbuildteamid +ucrt +ucrtd unvirtualized VCRT vcruntime diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml index 4c40ac623c5..abe79847680 100644 --- a/build/pipelines/release.yml +++ b/build/pipelines/release.yml @@ -22,6 +22,10 @@ parameters: displayName: "Run Compliance and Security Build" type: boolean default: true + - name: publishSymbolsToPublic + displayName: "Publish Symbols to MSDL" + type: boolean + default: true - name: buildTerminalVPack displayName: "Build Windows Terminal VPack" type: boolean @@ -48,6 +52,11 @@ parameters: - x64 - x86 - arm64 + - name: buildWindowsVersions + type: object + default: + - Win10 + - Win11 variables: TerminalInternalPackageVersion: "0.0.7" @@ -64,9 +73,11 @@ jobs: matrix: ${{ each config in parameters.buildConfigurations }}: ${{ each platform in parameters.buildPlatforms }}: - ${{ config }}_${{ platform }}: - BuildConfiguration: ${{ config }} - BuildPlatform: ${{ platform }} + ${{ each windowsVersion in parameters.buildWindowsVersions }}: + ${{ config }}_${{ platform }}_${{ windowsVersion }}: + BuildConfiguration: ${{ config }} + BuildPlatform: ${{ platform }} + TerminalTargetWindowsVersion: ${{ windowsVersion }} displayName: Build timeoutInMinutes: 240 cancelTimeoutInMinutes: 1 @@ -163,6 +174,10 @@ jobs: arguments: -MarkdownNoticePath .\NOTICE.md -OutputPath .\src\cascadia\CascadiaPackage\NOTICE.html pwsh: true - ${{ if eq(parameters.buildTerminal, true) }}: + - pwsh: |- + ./build/scripts/Patch-ManifestsToWindowsVersion.ps1 -NewWindowsVersion "10.0.22000.0" + displayName: Update manifest target version to Win11 (if necessary) + condition: and(succeeded(), eq(variables['TerminalTargetWindowsVersion'], 'Win11')) - task: VSBuild@1 displayName: Build solution **\OpenConsole.sln condition: true @@ -180,7 +195,7 @@ jobs: continueOnError: True inputs: PathtoPublish: $(Build.SourcesDirectory)\msbuild.binlog - ArtifactName: binlog-$(BuildPlatform) + ArtifactName: binlog-$(BuildPlatform)-$(TerminalTargetWindowsVersion) - task: PowerShell@2 displayName: Check MSIX for common regressions inputs: @@ -243,7 +258,7 @@ jobs: displayName: Publish Artifact (appx) inputs: PathtoPublish: $(Build.ArtifactStagingDirectory)/appx - ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration) + ArtifactName: appx-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion) - ${{ if eq(parameters.buildWPF, true) }}: - task: CopyFiles@2 displayName: Copy PublicTerminalCore.dll to Artifacts @@ -261,7 +276,7 @@ jobs: condition: and(succeeded(), ne(variables['BuildPlatform'], 'arm64')) inputs: PathtoPublish: $(Build.ArtifactStagingDirectory)/wpf - ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration) + ArtifactName: wpf-dll-$(BuildPlatform)-$(BuildConfiguration)-$(TerminalTargetWindowsVersion) - task: PublishSymbols@2 displayName: Publish symbols path @@ -279,6 +294,11 @@ jobs: - ${{ if eq(parameters.buildTerminal, true) }}: - job: BundleAndSign + strategy: + matrix: + ${{ each windowsVersion in parameters.buildWindowsVersions }}: + ${{ windowsVersion }}: + TerminalTargetWindowsVersion: ${{ windowsVersion }} displayName: Create and sign AppX/MSIX bundles dependsOn: Build steps: @@ -291,23 +311,16 @@ jobs: displayName: Package ES - Setup Build inputs: disableOutputRedirect: true - - task: DownloadBuildArtifacts@0 - displayName: Download Artifacts (*.appx, *.msix, *.appxsym) - inputs: - downloadType: specific - itemPattern: >- - **/*.msix - - **/*.appx - - **/*.appxsym - extractTars: false - + - ${{ each platform in parameters.buildPlatforms }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Artifacts ${{ platform }} $(TerminalTargetWindowsVersion) + inputs: + artifactName: appx-${{ platform }}-Release-$(TerminalTargetWindowsVersion) - task: PowerShell@2 displayName: Create WindowsTerminal*.msixbundle inputs: filePath: build\scripts\Create-AppxBundle.ps1 - arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" + arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" - task: EsrpCodeSigning@1 displayName: Submit *.msixbundle to ESRP for code signing inputs: @@ -343,55 +356,11 @@ jobs: } ] - # It seems easier to do this -- download every appxsym -- then enumerate all the PDBs in the build directory for the - # public symbol push. Otherwise, we would have to list all of the PDB files one by one. - - pwsh: |- - mkdir $(Build.SourcesDirectory)/appxsym-temp - Get-ChildItem "$(System.ArtifactsDirectory)" -Filter *.appxsym -Recurse | % { - $src = $_.FullName - $dest = Join-Path "$(Build.SourcesDirectory)/appxsym-temp/" $_.Name - - mkdir $dest - Write-Host "Extracting $src to $dest..." - tar -x -v -f $src -C $dest - } - displayName: Extract symbols for public consumption - - # Pull the Windows SDK for the developer tools like the debuggers so we can index sources later - - template: .\templates\install-winsdk-steps.yml - - task: PowerShell@2 - displayName: Source Index PDBs (the public ones) - inputs: - filePath: build\scripts\Index-Pdbs.ps1 - arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion) - - # Publish the app symbols to the public MSDL symbol server - # accessible via https://msdl.microsoft.com/download/symbols - - task: PublishSymbols@2 - displayName: 'Publish app symbols to MSDL' - inputs: - symbolsFolder: '$(Build.SourcesDirectory)/appxsym-temp' - searchPattern: '**/*.pdb' - SymbolsMaximumWaitTime: 30 - SymbolServerType: 'TeamServices' - SymbolsProduct: 'Windows Terminal Application Binaries' - SymbolsVersion: '$(XES_APPXMANIFESTVERSION)' - # The ADO task does not support indexing of GitHub sources. - indexSources: false - detailedLog: true - # There is a bug which causes this task to fail if LIB includes an inaccessible path (even though it does not depend on it). - # To work around this issue, we just force LIB to be any dir that we know exists. - # Copied from https://github.com/microsoft/icu/blob/f869c214adc87415dfe751d81f42f1bca55dcf5f/build/azure-nuget.yml#L564-L583 - env: - LIB: $(Build.SourcesDirectory) - ArtifactServices_Symbol_AccountName: microsoftpublicsymbols - ArtifactServices_Symbol_PAT: $(ADO_microsoftpublicsymbols_PAT) - - task: PublishBuildArtifacts@1 displayName: 'Publish Artifact: appxbundle-signed' inputs: PathtoPublish: $(System.ArtifactsDirectory) - ArtifactName: appxbundle-signed + ArtifactName: appxbundle-signed-$(TerminalTargetWindowsVersion) - ${{ if eq(parameters.buildWPF, true) }}: - job: PackageAndSignWPF @@ -415,14 +384,14 @@ jobs: - task: DownloadBuildArtifacts@0 displayName: Download x86 PublicTerminalCore inputs: - artifactName: wpf-dll-x86-$(BuildConfiguration) + artifactName: wpf-dll-x86-$(BuildConfiguration)-Win10 itemPattern: '**/*.dll' downloadPath: bin\Win32\$(BuildConfiguration)\ extractTars: false - task: DownloadBuildArtifacts@0 displayName: Download x64 PublicTerminalCore inputs: - artifactName: wpf-dll-x64-$(BuildConfiguration) + artifactName: wpf-dll-x64-$(BuildConfiguration)-Win10 itemPattern: '**/*.dll' downloadPath: bin\x64\$(BuildConfiguration)\ extractTars: false @@ -504,6 +473,71 @@ jobs: PathtoPublish: $(Build.ArtifactStagingDirectory)\nupkg ArtifactName: wpf-nupkg-$(BuildConfiguration) +- ${{ if eq(parameters.publishSymbolsToPublic, true) }}: + - job: PublishSymbols + displayName: Publish Symbols + dependsOn: BundleAndSign + steps: + - checkout: self + clean: true + fetchDepth: 1 + submodules: true + - task: PkgESSetupBuild@12 + displayName: Package ES - Setup Build + + # Download the appx-PLATFORM-CONFIG-VERSION artifact for every platform/version combo + - ${{ each platform in parameters.buildPlatforms }}: + - ${{ each windowsVersion in parameters.buildWindowsVersions }}: + - task: DownloadBuildArtifacts@0 + displayName: Download Symbols ${{ platform }} ${{ windowsVersion }} + inputs: + artifactName: appx-${{ platform }}-Release-${{ windowsVersion }} + + # It seems easier to do this -- download every appxsym -- then enumerate all the PDBs in the build directory for the + # public symbol push. Otherwise, we would have to list all of the PDB files one by one. + - pwsh: |- + mkdir $(Build.SourcesDirectory)/appxsym-temp + Get-ChildItem "$(System.ArtifactsDirectory)" -Filter *.appxsym -Recurse | % { + $src = $_.FullName + $dest = Join-Path "$(Build.SourcesDirectory)/appxsym-temp/" $_.Name + + mkdir $dest + Write-Host "Extracting $src to $dest..." + tar -x -v -f $src -C $dest + } + displayName: Extract symbols for public consumption + + # Pull the Windows SDK for the developer tools like the debuggers so we can index sources later + - template: .\templates\install-winsdk-steps.yml + - task: PowerShell@2 + displayName: Source Index PDBs (the public ones) + inputs: + filePath: build\scripts\Index-Pdbs.ps1 + arguments: -SearchDir '$(Build.SourcesDirectory)/appxsym-temp' -SourceRoot '$(Build.SourcesDirectory)' -recursive -Verbose -CommitId $(Build.SourceVersion) + + # Publish the app symbols to the public MSDL symbol server + # accessible via https://msdl.microsoft.com/download/symbols + - task: PublishSymbols@2 + displayName: 'Publish app symbols to MSDL' + inputs: + symbolsFolder: '$(Build.SourcesDirectory)/appxsym-temp' + searchPattern: '**/*.pdb' + SymbolsMaximumWaitTime: 30 + SymbolServerType: 'TeamServices' + SymbolsProduct: 'Windows Terminal Application Binaries' + SymbolsVersion: '$(XES_APPXMANIFESTVERSION)' + # The ADO task does not support indexing of GitHub sources. + indexSources: false + detailedLog: true + # There is a bug which causes this task to fail if LIB includes an inaccessible path (even though it does not depend on it). + # To work around this issue, we just force LIB to be any dir that we know exists. + # Copied from https://github.com/microsoft/icu/blob/f869c214adc87415dfe751d81f42f1bca55dcf5f/build/azure-nuget.yml#L564-L583 + env: + LIB: $(Build.SourcesDirectory) + ArtifactServices_Symbol_AccountName: microsoftpublicsymbols + ArtifactServices_Symbol_PAT: $(ADO_microsoftpublicsymbols_PAT) + + - ${{ if eq(parameters.buildTerminalVPack, true) }}: - job: VPack displayName: Create Windows vPack @@ -518,7 +552,7 @@ jobs: - task: DownloadBuildArtifacts@0 displayName: Download Build Artifacts inputs: - artifactName: appxbundle-signed + artifactName: appxbundle-signed-Win11 extractTars: false - task: PowerShell@2 displayName: Rename and stage packages for vpack @@ -527,7 +561,7 @@ jobs: script: >- # Rename to known/fixed name for Windows build system - Get-ChildItem Microsoft.WindowsTerminal_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' } + Get-ChildItem Microsoft.WindowsTerminal_Win11_*.msixbundle | Rename-Item -NewName { 'Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle' } # Create vpack directory and place item inside diff --git a/build/scripts/Patch-ManifestsToWindowsVersion.ps1 b/build/scripts/Patch-ManifestsToWindowsVersion.ps1 new file mode 100644 index 00000000000..fe86f24fd8c --- /dev/null +++ b/build/scripts/Patch-ManifestsToWindowsVersion.ps1 @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +Param( + [string]$NewWindowsVersion = "10.0.22000.0" +) + +Get-ChildItem src/cascadia/CascadiaPackage -Recurse -Filter *.appxmanifest | ForEach-Object { + $xml = [xml](Get-Content $_.FullName) + $xml.Package.Dependencies.TargetDeviceFamily | Where-Object Name -Like "Windows*" | ForEach-Object { + $_.MinVersion = $NewWindowsVersion + } + $xml.Save($_.FullName) +} diff --git a/common.openconsole.props b/common.openconsole.props index e521ad61996..400555dbdd5 100644 --- a/common.openconsole.props +++ b/common.openconsole.props @@ -10,4 +10,18 @@ $(MSBuildThisFileDirectory) + + + 2.7.0-prerelease.210913003 + + 2.7.0 + + diff --git a/custom.props b/custom.props index e82cc844cc2..aab0b65870b 100644 --- a/custom.props +++ b/custom.props @@ -2,6 +2,18 @@ + + $([MSBuild]::Add($(VersionBuildRevision), 1)) + true 2022 1 diff --git a/scratch/ScratchIslandApp/Package/Package.wapproj b/scratch/ScratchIslandApp/Package/Package.wapproj index 8f526698b1c..4de7a05a3cf 100644 --- a/scratch/ScratchIslandApp/Package/Package.wapproj +++ b/scratch/ScratchIslandApp/Package/Package.wapproj @@ -140,12 +140,12 @@ - + 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/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj index 04b9b082496..5d84890f014 100644 --- a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj @@ -147,13 +147,13 @@ - + 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/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj index 59d500ccc6e..91592d6f771 100644 --- a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj +++ b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj @@ -80,13 +80,13 @@ - + 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/scratch/ScratchIslandApp/SampleApp/packages.config b/scratch/ScratchIslandApp/SampleApp/packages.config index ec5cd9a0fbd..f167ce9570a 100644 --- a/scratch/ScratchIslandApp/SampleApp/packages.config +++ b/scratch/ScratchIslandApp/SampleApp/packages.config @@ -1,6 +1,6 @@ - + diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj index 18a5a05fe33..f83b237149c 100644 --- a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj @@ -137,14 +137,14 @@ - + 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/scratch/ScratchIslandApp/WindowExe/packages.config b/scratch/ScratchIslandApp/WindowExe/packages.config index 319e836803c..cbcb02cb6dc 100644 --- a/scratch/ScratchIslandApp/WindowExe/packages.config +++ b/scratch/ScratchIslandApp/WindowExe/packages.config @@ -2,6 +2,6 @@ - + diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index 48d8caffdf9..ca2a76477f6 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -138,36 +138,34 @@ - - - + + + - - + + + - - + 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/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj index f383f371001..2ad3e5508e3 100644 --- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj @@ -81,8 +81,8 @@ If you don't have this, then you'll see an error like "(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)" --> - /INCLUDE:_DllMain@12 - /INCLUDE:DllMain + %(AdditionalOptions) /INCLUDE:_DllMain@12 + %(AdditionalOptions) /INCLUDE:DllMain @@ -99,10 +99,10 @@ x86 $(Platform) - <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\" + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" - + diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index 6217790a895..d44431a5f89 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -92,11 +92,11 @@ x86 $(Platform) - <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\" + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" - + diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 5197b0bd07f..a97fe8064b4 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -123,7 +123,7 @@ - + diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index 4cb06143311..40fad5cce81 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -400,13 +400,13 @@ - + 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/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index a5d86577692..ef10edfa434 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -89,13 +89,13 @@ - + 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 e039fa4ae47..62c921a4fac 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -1,6 +1,7 @@ + diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj index e508129eb35..7c5f17695a9 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj @@ -345,12 +345,12 @@ - + 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}. - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/packages.config b/src/cascadia/TerminalSettingsEditor/packages.config index 44d99225891..9b938bd8686 100644 --- a/src/cascadia/TerminalSettingsEditor/packages.config +++ b/src/cascadia/TerminalSettingsEditor/packages.config @@ -1,6 +1,6 @@ - + diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index ad7c9dcc07f..bf0e68e2662 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -268,12 +268,12 @@ - + 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}. - + - - x86 - $(Platform) - - - - - <_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" /> - - - $(ProjectName) - BuiltProjectOutputGroup - %(Filename)%(Extension) - - - - + diff --git a/src/cascadia/WindowsTerminal/packages.config b/src/cascadia/WindowsTerminal/packages.config index 39685454320..762898bbd46 100644 --- a/src/cascadia/WindowsTerminal/packages.config +++ b/src/cascadia/WindowsTerminal/packages.config @@ -2,8 +2,8 @@ - + - + diff --git a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj index 41a6a3e96a9..11759a5613f 100644 --- a/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj +++ b/src/cascadia/ut_app/TerminalApp.UnitTests.vcxproj @@ -72,8 +72,8 @@ If you don't have this, then you'll see an error like "(init.obj) : error LNK2005: DllMain already defined in MSVCRTD.lib(dll_dllmain_stub.obj)" --> - /INCLUDE:_DllMain@12 - /INCLUDE:DllMain + %(AdditionalOptions) /INCLUDE:_DllMain@12 + %(AdditionalOptions) /INCLUDE:DllMain @@ -93,7 +93,7 @@ x86 $(Platform) - <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.2.7.0-prerelease.210913003\runtimes\win10-$(Native-Platform)\native\" + <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" diff --git a/src/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 478970ef564..263da4fd9bf 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -43,17 +43,15 @@ + + <_VC_Target_Library_Platform>Desktop <_NoWinAPIFamilyApp>true From 24a030f4417c6cb5a6e63e6e375a238f21b78464 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 25 Feb 2022 15:05:45 -0600 Subject: [PATCH 13/75] Add a pipeline that validates builds that touch features.xml (#12573) This is intended to be a PR triggered build that only runs when features.xml has been touched, to validate that the disabled features do not cause compilation errors. --- build/pipelines/feature-flag-ci.yml | 29 +++++++++++++++++++ .../pipelines/templates/build-console-ci.yml | 6 ++-- .../CascadiaPackage/CascadiaPackage.wapproj | 6 ++-- src/host/proxy/Host.Proxy.vcxproj | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 build/pipelines/feature-flag-ci.yml diff --git a/build/pipelines/feature-flag-ci.yml b/build/pipelines/feature-flag-ci.yml new file mode 100644 index 00000000000..0ca4400463d --- /dev/null +++ b/build/pipelines/feature-flag-ci.yml @@ -0,0 +1,29 @@ +trigger: none + +pr: + branches: + include: + - main + paths: + include: + - src/features.xml + +variables: + - name: runCodesignValidationInjectionBG + value: false + +parameters: + - name: buildBrandings + type: object + default: + - Release + - Preview + # Dev is built automatically + # WindowsInbox does not typically build with VS. + +jobs: + - ${{ each branding in parameters.buildBrandings }}: + - template: ./templates/build-console-ci.yml + parameters: + platform: x64 + branding: ${{ branding }} diff --git a/build/pipelines/templates/build-console-ci.yml b/build/pipelines/templates/build-console-ci.yml index 7256d521988..45d6bd4267e 100644 --- a/build/pipelines/templates/build-console-ci.yml +++ b/build/pipelines/templates/build-console-ci.yml @@ -1,14 +1,16 @@ parameters: configuration: 'Release' + branding: 'Dev' platform: '' additionalBuildArguments: '' jobs: -- job: Build${{ parameters.platform }}${{ parameters.configuration }} - displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} +- job: Build${{ parameters.platform }}${{ parameters.configuration }}${{ parameters.branding }} + displayName: Build ${{ parameters.platform }} ${{ parameters.configuration }} ${{ parameters.branding }} variables: BuildConfiguration: ${{ parameters.configuration }} BuildPlatform: ${{ parameters.platform }} + WindowsTerminalBranding: ${{ parameters.branding }} pool: ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: name: WinDevPoolOSS-L diff --git a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj index ca2a76477f6..9aaffac83ee 100644 --- a/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj +++ b/src/cascadia/CascadiaPackage/CascadiaPackage.wapproj @@ -11,7 +11,7 @@ --> false false - wtd + wtd wt @@ -38,7 +38,7 @@ Designer - + Designer @@ -52,7 +52,7 @@ - + -Dev diff --git a/src/host/proxy/Host.Proxy.vcxproj b/src/host/proxy/Host.Proxy.vcxproj index c6129bf828f..dd0551e104d 100644 --- a/src/host/proxy/Host.Proxy.vcxproj +++ b/src/host/proxy/Host.Proxy.vcxproj @@ -60,7 +60,7 @@ PROXY_CLSID_IS={0x1833E661,0xCC81,0x4DD0,{0x87,0xC6,0xC2,0xF7,0x4B,0xD3,0x9E,0xFA}};%(PreprocessorDefinitions) - + PROXY_CLSID_IS={0xDEC4804D,0x56D1,0x4F73,{0x9F,0xBE,0x68,0x28,0xE7,0xC8,0x5C,0x56}};%(PreprocessorDefinitions) From d0d42c4ba1196148c973f5fbaaa5516a76c8507b Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Fri, 25 Feb 2022 15:54:45 -0800 Subject: [PATCH 14/75] Default Terminal Spec (#7414) Co-authored-by: Dustin L. Howett --- doc/specs/#492 - Default Terminal/spec.md | 270 ++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 doc/specs/#492 - Default Terminal/spec.md diff --git a/doc/specs/#492 - Default Terminal/spec.md b/doc/specs/#492 - Default Terminal/spec.md new file mode 100644 index 00000000000..e54b4d71b9d --- /dev/null +++ b/doc/specs/#492 - Default Terminal/spec.md @@ -0,0 +1,270 @@ +--- +author: Michael Niksa @miniksa +created on: 2020-08-14 +last updated: 2022-01-13 +issue id: #492 +--- + +# Default Terminal Choice in Windows OS + +## Abstract + +Since the beginning, Windows has offered a single choice in default terminal hosting behavior. Specifically, the default terminal is defined as the one that the operating system will start on your behalf when a command-line application is started without a terminal attached. This specification intends to detail how we will offer customers the ultimate in choice among first and third party replacements for their default terminal experience. + +## Inspiration + +We've had a lot of success in the past several years on our terminal team journey. We updated the old console host user interface with long-desired features. We updated the console environment to bring Windows closer to Linux and Mac by implementing the client (receiving) end of Virtual Terminal sequences to unlock WSL, Docker, and other cross-platform command-line application compatibility. We then created the ConPTY to expose the server end of the console environment to first and third party applications to enable the hosting of any of those command-line clients within their own user interfaces by implementing the server (sending) end of Virtual Terminal sequences. And then we built Windows Terminal as our flagship implementation of the development environment on this model. + +Through all of this, the entrypoint for alternatives to the console host UX continued to be "Start your alternative terminal implementation first, then start the command-line application inside." For those familiar with Linux and Mac or for those using the broad ecosystem of alternative Windows Terminals like ConEmu, Cmder, Console2, and the like... that was natural. But Windows did it differently a long time ago allowing the starting of a command-line application directly from the shell or kernel without a terminal specified. On noticing the missing terminal, the system would just-in-time start and attach the one terminal it could count on as always present, `conhost.exe`. + +And so the inspiration of this is simple: We want to allow our customers to choose whichever terminal they want as the just-in-time terminal attached to an application without one present/specified on launch. This final move completes our journey to allow the ultimate in choice AND decouple the terminal experience from the operating system release schedule. + +## Solution Design + +There are three components to the proposed design: + +1. **Inbox console**: This is the `conhost.exe` that is resident inside every Windows installation. +1. **Updated console**: This is the `openconsole.exe` that we ship with the Windows Terminal to provide a more up-to-date console server experience. +1. **Terminal UX**: This is `WindowsTerminal.exe`, the new Terminal user interface that runs on VT sequences. + +And there are a few scenarios here to consider: + +1. Replacement console API server and replacement terminal UX. + 1. This is the Windows Terminal scenario today. `OpenConsole.exe` is packed in the package to be the console API server and ConPTY environment for `WindowsTerminal.exe`. +1. Replacement console API server and legacy terminal UX. + 1. We don't explicitly distribute this today, but it's technically possible to just run `OpenConsole.exe` to accomplish this. +1. Inbox console API server and replacement terminal UX. + 1. The WSL environment does this when doing Windows interop and I believe VS Code does this too when told to use the ConPTY environment. (And since VS Code does it, anything using node-pty also does it, covering some 3rd party terminals as well). +1. Inbox console API server and inbox terminal UX. + 1. This is what we have today in `conhost.exe` running as the default application. + +The goal is to offer the ultimate in choice here where any of the components can be replaced as necessary for a 1st or 3rd party scenario. + +### Overview + +#### Inbox console + +The inbox console will be updated to support delegation of the incoming console client application connection to another console API server if one is registered and available. + +We leave the inbox console in-place and always available on the operating system for these reasons: + +1. A last chance fall-back should any of the delegation operations fail +1. An ongoing host for applications that aren't going to need a window at all +1. Continued support of our legacy `conhostv1.dll` environment, if chosen + +The general operation is as follows: + +- A command-line client application is started (from the start menu, run box, or any other `CreateProcess` or `ShellExecute` route) without an existing console server attached +- The inbox console is launched from `C:\windows\system32\conhost.exe` as always by the initialization routines inside `kernelbase.dll`. +- The inbox console accepts the incoming initial connection and looks for the `ShowWindow` information on the connection packet, as received from the kernel's process creation routines based on the parameters given to the `CreateProcess` call. (See [CREATE_NO_WINDOW](https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags) flag for details.) +- If the session is about to create a window, check for registration of a delegated/updated console and hand-off to it if it exists. +- Otherwise, start normally. + +This workflow affords us several benefits: + +- The only inbox component we have to change is `conhost.exe`, the one we already regularly update from open source on a regular basis. There is no change to the `kernelbase.dll` console initialization routines, `conclnt.lib` communication protocol, nor the `condrv.sys` driver. +- We should be able to make this change quickly, relatively easily, and the code delta should be relatively small + - This makes it easy to squeeze in early in the development of the solution and get it into the Windows OS product as soon as possible for self-hosting, validation, and potentially shipping in the OS before the remainder of the solution has shaken out + - This also makes it potentially possible to backport this portion of the code change to popular in-market versions of Windows 10. For instance, WSL2 has just backported to 1903 and 1909. The less churn and risk, the easier it is to sell a backport. + +*Potential future:* +- ~~If no updated console exists, potentially check for registration of a terminal UX that is willing to use the inbox ConPTY bits, start it, and transition to being a PTY instead.~~ +- **CUT FROM v1**: To simplify the story for end-users, we're offering this as a package deal in the first revision. Explaining the difference between consoles and terminals to end users is very difficult. + +The registration would operate as follows: +- A registry key in `HKCU\Console\%%Startup` (format `REG_SZ`, name `DelegationConsole`) would specify ~~the path to ~~the replacement console that would be used to service the remainder of the connection process. + - Alternatively or additionally, this same `REG_SZ` could list a COM server ID that could be looked up in the classes root and invoked. **V1 NOTE:** This was what was done. + - Packaged applications and classic applications can easily register a COM server + - WinRT libraries should be able to be easily registered as the COM server as well (given WinRT is COM underneath) + - WinRT cannot be exposed outside of the package context itself, so the `conhost.exe` that is in the OS and is naturally outside the package cannot find it. + - **V1 NOTE:** The subkey `%%Startup` was chosen to separate these keys (this one and the `DelegationTerminal` one below) in case we needed to ACL them or protect them in some way. We want a per-user choice of which Terminal/Console are used, but we might need to take action to prevent these keys from being slammed at some point in the future. Why `%%`? The subkeys are traditionally used to resolve paths to client binaries that have their own console preferences set. The `%%` should never be resolvable as it won't lead to a valid path or expanded path variable. + +The delegation process would operate as follows: +- A method contract is established between the existing inbox console and any updated console (an interface). + - `HRESULT ConsoleEstablishHandoff(HANDLE server, HANDLE driverInputEvent, const PortableConnectMessage* const msg, HANDLE signalPipe, HANDLE inboxProcess, HANDLE* process)` + - `HANDLE server`: This is the server side handle to the console driver, used with `DeviceIoControl` to receive/send messages with the client command-line application + - `HANDLE driverInputEvent`: The input event is created and assigned to the driver immediately on first connection, before any messages are read from the driver, to ensure that it can track a blocking state should first message be an input request that we do not yet have data to fill. As such, the inbox console will have created this and assigned it to the driver before pulling off the connection packet and determining that it wants to delegate. Therefore, we will transfer ownership of this event to the updated console. + - ~~`const PortableArguments* const args`: This contains the startup argument information that was passed in when the process was started including the original command line and the in/out handles.~~ + - ~~The `ConsoleArguments` structure could technically change between versions, so we will make a version agnostic portable structure that just carries the communication from the old one to the new one.~~ + - **CUT FROM V1**: The only arguments coming in from a default light-up are the server handle. Pretty much all the other arguments are related to the operation of the PTY. Since this feature is about "default application" launches where no arguments are specified, this was cut from the initial revision. + - `const PortableConnectMessage* const msg`: + - The `CONSOLE_API_MSG` structure contains both the actual packet data from the driver as well as some overhead/administration data related to the packet state, ordering, completion, errors, and buffers. It's a broad scope structure for every type of message we process and it can change over time as we improve the way the `conserver.lib` handles packets. + - This represents a version agnostic variant for ONLY the connect message that can pass along the initial connect information structure, the packet sequencing information, and other relevant payload only to that one message type. It will purposefully discard references to things like a specific set of API servicing routines because the point of handing off is to get updated routines, if necessary. + - **V1 NOTE:** This was named `CONSOLE_PORTABLE_ATTACH_MSG` + - `HANDLE signalPipe`: During authoring, it was identified that Ctrl+C and other similar signals need to make it back to the original `conhost.exe` application as the Operating System grants it special privilege over the originally attached client application. This privilege cannot be transferred to the delegated console. So this channel remains for the delegated one to send its signals back through the original one for commanding the underlying client. (This also implies the original `conhost.exe` inbox cannot close and must remain a part of the process tree for the life of the session to maintain this control.) + - `HANDLE inboxProcess`: Since we have to keep the inbox `conhost.exe` running for signal/ownership reasons, we also need to track its lifetime. If it disappears for whatever reason, we need to tear down the entire chain as part of our operation has been compromised. + - `HANDLE* process`: On the contrary to `inboxProcess`, we need to give our process handle back so it can also be tracked. After the inbox console delegates, it remains in a very limited capacity. If the delegation one disappears, the session will no longer function and needs to be torn down (and the client closed). + - *Return* `HRESULT`: This is one of the older style methods in the initialization. We moved them from mostly `NTSTATUS` to mostly `HRESULT` a while ago to take advantage of `wil`. This one will continue to follow the pattern and not move to exceptions. A return of `S_OK` will symbolize that the handoff worked and the inbox console can clean itself up and stop handling the session. +- When the connection packet is parsed for visibility information (see `srvinit.cpp`), we will attempt to resolve the registered handoff and call it. + - ~~In the initial revision here, I have this as a `LoadLibrary`/`GetProcAddress` to the above exported contract method from the updated console. This maintains the server session in the same process space and avoids:~~ + 1. ~~The issue of passing the server, event, and other handles into another process space. We're not entirely sure if the console driver will happily accept these things moving to a different process. It probably should, but unconfirmed.~~ + 1. ~~Some command-line client applications rely on spelunking the process tree to figure out who is their servicing application. Maintaining the delegated/updated console inside the same process space maintains some level of continuity for these sorts of applications.~~ + - **Alternative:** We may make this just be a COM server/client contract. ~~An in-proc COM server should operate in much the same fashion here (loading the DLL into the process and running particular method) while being significantly more formal and customizable (version revisions, moving to out-of-proc, not really needing to know the binary path because the catalog knows).~~ + - **V1 NOTE:** We landed on an out-of-proc COM server/client here. This maintains the isolation of the newly running code from the old code. Since we're maintaining the original `conhost.exe` for signaling purposes, we're no longer worried about the spelunking the process tree and not having the relationship for clients to find. + - **Not considering:** ~~WinRT. `conhost.exe` has no WinRT. Adding WinRT to it would significantly increase the complexity of compilation in the inbox and out of box code base. It would also significantly increase the compilation time, binary size, library link list, etc... unless we use just the ABI to access it. But I don't see an advantage to that over just using classic COM at that point. This is only one handoff method and a rather simplistic one at that. Every benefit WinRT provides is outweighed by the extra effort that would be required over just a classic COM server in this case.~~ +- After delegation is complete, the inbox console will have to clean up any threads, handles, and state related to the session. We do a fairly good job with this normally, but some portions of the `conhost.exe` codebase are reliant on the process exiting for final cleanup. There may be a bit of extra effort to do some explicit cleanup here. + - **V1 NOTE:** The inbox one cleans up everything it can and sits in a state waiting for the child/delegated process handle to exit. It also maintains a thread listening for the signals to come through in case it needs to send a command to the client application using the privilege granted to it by the driver. + +#### Updated console + +The updated replacement console will have the same console API server capabilities as the inbox console, but will be a later, updated, or customized-to-the-scenario version of the API server generally revolving around improving ConPTY support for a Terminal application. + +On receiving the handoff from the method signature listed above, the updated console will: +- Establish its own set of IO threading, device communication infrastructure, and API messaging routines while storing the handles given +- ~~Re-parse the command line arguments, if necessary, and store them for guiding the remainder of launch~~ +- Dispatch the attach message as if it were received normally +- Continue execution from there + +There will then either be a registration for a Terminal UX to take over the session by using ConPTY, ~~or the updated console will choose to launch its potentially updated version of the `conhost` UX~~. + +For registration, we repeat the dance above with another key: +- A registry key in `HKCU\Console\%%Startup` (format `REG_SZ`, name `DelegationTerminal`). + +The delegation repeats the same dance as above as well: +- A contract (interface) is established between the updated console and the terminal + - `HRESULT EstablishPtyHandoff(HANDLE in, HANDLE out, HANDLE signal, HANDLE ref, HANDLE server, HANDLE client)` + - `HANDLE in`: The handle to read client application output from the ConPTY and display on the Terminal + - `HANDLE out`: The handle to write user input from the Terminal to the ConPTY + - `HANDLE signal`: The signal handle for the ConPTY for out-of-band communication between PTY server and Terminal application + - ~~`COORD size`: The initial window size from the starting application, as it can be a preference in the connection structure. (A resize message may get sent back downward almost immediately from the Terminal as its dimensions could be different.)~~ **V1 NOTE:** This proved unnecessary as the resize operations sorted themselves out naturally. + - `HANDLE ref`: This is a "client reference handle" to the console driver and session. We hold onto a copy of this in the Terminal so the session will stay alive until we let go. (The other console hosts in the chain also hold one of these, as should the client.) + - `HANDLE server`: This is a process handle to the PTY we're attached to. We monitor this to know when the PTY is still alive from the Terminal side. + - `HANDLE client`: This is a process handle to the underlying client application. The terminal tracks this for exit handling. + - **Alternative:** This should likely just be a COM server/client contract as well. This would be consistent with the above and wouldn't require argument parsing or wink/nudge understanding of standard handle passing. It also conveys the same COM flexibility as described in the inbox console section. **V1 NOTE:** We used this alternative. We used COM, not a well-known exported function from the prototype. +- The contract is called and on success, responsibility of the UX is given over to the Terminal. The console sits in PTY mode. + - On failure, the console launches interactive. + +#### Terminal UX + +The terminal will be its own complete presentation and input solution on top of a ConPTY connection, separating the concerns between API servicing and the user experience. + +Today the Terminal knows how to start and then launches a ConPTY under it. The Terminal will need to be updated to accept a pre-existing ConPTY connection on launch (or when the multi-process model arrives, as an inbound connection), and connect that to a new tab/pane instead of using the `winconpty.lib` libraries to make its own. + +For now, I'm considering only the fresh-start scenario. +- The Terminal will have to detect the inbound connection through ~~its argument parsing (or through~~ a new entrypoint in the COM alternative ~~)~~ and store the PTY in/out/signal handles for that connection in the startup arguments information +- When the control is instantiated on a new tab, that initial creation where normally the "default profile" is launched will instead have to place the PTY in/out/signal handles already received into the `ConPtyConnection` object and use that as if it was already created. +- The Terminal can then let things run normally and the connection will come through and be hosted inside the session. + +There are several issues/concerns: +- Which profile/settings get loaded? We don't really know anything about the client that is coming in already-established. That makes it difficult to know what user preferences to apply to the inbound tab. We could: + - Use only the defaults for the incoming connection. Do not apply any profile-specific settings. + - Use the profile information from the default profile to some degree. This could cause some weird scenarios/mismatches if that profile has a particular icon or a color scheme that makes it recognizable to the user. + - Create some sort of "inbound profile" profile that is used for incoming connections + - Add a heuristic that attempts to match the name/path of the connecting client binary to a profile that exists and use those settings, falling back if one is not found. + - **Proposal:** Do the first one immediately for bootstrapping, then investigate the others as a revision going forward. +- The handles that are coming in are "raw" and "unpacked", not in the nice opaque `HPCON` structure that is usually provided to the `ConPtyConnection` object by the `winconpty.lib`. + - Add methods to `winconpty.lib` that allow for the packing of incoming raw handles into the `HPCON` structure so the rest of the lifetime can be treated the same + - Put the entrypoint for the COM server (or delegate the entrypoint for an argument) directly into this library so it can pack them up right away and hand of a ready-made `HPCON`. + +## UI/UX Design + +The user experience for this feature overall should be: + +1. The user launches a command-line client application through the Start Menu, Win+X menu, the Windows Explorer, the Run Dialog box (WinKey+R), or through another existing Windows application. +1. Using the established settings, the console system transparently starts, delegates itself to the updated console, switches itself into ConPTY mode, and a copy of Windows Terminal launches with the first tab open to host the command-line client application. + - **NOTE:** I'm not precluding 3rd party registrations of either the delegation updated console nor the delegation terminal. It is our intention to allow either or both of these pieces to be replaced to the user's desires. The example is for brevity of our golden path and motivation for this scenario. +1. The user is able to interact with the command-line client application as they would with the original console host. + - The user receives the additional benefit that short-running executions of a command-line application may not "blink in and disappear" as they do today when a user runs something like `ipconfig` from the run dialog. The Terminal's default states tend to leave the tab open and say that the client has exited. This would allow a Run Dialog `ipconfig` user an improved experience over the default console host state of disappearing quickly. +1. If any portion of the delegation fails, we will progressively degrade back to a `conhost` style Win32+GDI UX and nothing will be different from before. + +The settings experience includes: +- Configuration of the delegation operations: + - Locations: + - With the registry + - This is what's going to be available first and will remain available. We will progress to some or all of the below after. + - We will need to potentially add specifications to this to both the default profile (for new installations of Windows) or to upgrade/migration profiles (for users coming from previous editions of Windows) to enable the delegation process, especially if we put a copy of Windows Terminal directly into the box. + - **V1 NOTE:** we didn't add additional migration logic here as `HKCU\Console*` and subkeys were already in the migration logic, so adding another should just carry along. + - Inside Windows Terminal + - Inside the new Settings UI, we will likely need a page that configures the delegation keys in `HKCU\Console\%%Startup` ~~or a link out to the Windows Settings panel, should we manage to get the settings configurable there~~. + - Inside the console property sheet + - Same as for Terminal but with `comctl` controls over XAML +/- a link to the Windows Settings panel + - Inside the Settings panel for Windows (probably on the developer settings page) + - The ultimate location for this is likely a panel directly inside Windows. This is the hardest one to accomplish because of the timelines of the Windows product. We may not get this in an initial revision, but it should likely be our ultimate goal. **V1 NOTE:** We did it! + - Operation: + - Specify paths/server IDs - This is the initial revision + - Offer a list of registered servers or discovered manifests from the app catalog - This is the ideal scenario where we search the installed app catalog +/- the COM catalog and offer a list of apps that conform to the contract in a drop-down. + - The final process was to use [App Extensions](https://docs.microsoft.com/windows/uwp/launch-resume/how-to-create-an-extension) inside the Terminal APPX package to declare the COM GUIDs that were available for the `DelegationConsole` and `DelegationTerminal` fields respectively. A configuration class `DelegationConfig` was added to `propslib.lib` that enables the lookup of these from the application state catalog and presents a list of them to choose from. It also manages reading and writing the registry keys. + - **V1 NOTE:** Our configuration options currently allow pairings of replacement consoles and terminals to be adjusted in lock-step from the UI. That's not to say further combinations are not possible or even necessarily inhibited by the code. We just went for minimal confusion in our first round. + +- Configuration of the legacy console state: + - ~~Since we could end up in an experience where the default launch experience gets you directly into Windows Terminal, we believe that the Terminal will likely need an additional setting or settings in the new Settings UI that will allow the toggling of some of the `HKCU\Console` values to do things like set/remove the legacy console state.~~ **V1 NOTE:** Cut as low priority. Switch back to console and configure it that way or use the existing property sheet or tamper with registry keys. + - We have left the per-launch debugging and advanced access hole of calling something like `conhost.exe cmd.exe` which will use the inbox conhost to launch `cmd.exe` even if there is a default specified. + +Concerns: +- State separation policy for Windows. I believe `HKCU\Console` is already specified as a part of the "user state" that should be mutable and carried forward on OS Swap, especially as we have been improving the OS swap experience. +- Ability for installers/elevated scripts to stomp the Delegation keys + - This was a long time problem for default app registrations and was limited in our OS. Are we about to run down the same path? + - What is the alternative here? To use a protocol handler? To store this configuration state data with other protected state in a registry area that is mutable, but only ACL'd to the `SYSTEM` user like some other things in the Settings control panel? + - **V1 NOTE:** We set ourselves up for some future ACL thing with the subkey, but we otherwise haven't enforced anything at this time. + +## Capabilities + +### Accessibility + +Accessibility applications are the most likely to resort to a method of spelunking the process tree or window handles to attempt to find content to read out. Presuming they have hardcoded rules for console-type applications, these algorithms could be surprised by the substitution of another terminal environment. + +The major players here that I am considering are NVDA, JAWS, and Narrator. As far as I am aware, all of these applications attempt to drive their interactivity through UI Automation where possible. And we have worked with all of these applications in the past in improving their support for both `conhost.exe` and the Windows Terminal product. I have relatively high confidence that we will be able to work with them again to help update these assistive products to understand the new UI delegation, if necessary. + +### Security + +Let's hit the elephant in the room. "You plan on pulling a completely different binary inside the `conhost.exe` process and just... delegating all activity to it?" Yes. + +(**V1 NOTE:** Well, it's out of proc now. But it is at the same privilege level as the original one thanks to the mechanics of COM.) + +As far as I'm concerned, the `conhost.exe` that is started to host the command-line client application is running at the same integrity level as the client binary that is partially started and waiting for its server to be ready. This is the long-standing existing protection that we have from the Windows operating system. Anything running in the same integrity level is already expected to be able to tamper with anything else at the same integrity level. The delegated binary that we would be loading into our process space will also be at the same integrity level. Nothing really stops a malicious actor from launching that binary in any other way in the same integrity level as a part of the command-line client application's startup. + +The mitigation here, if necessary, would be to use `WinVerifyTrust` to validate the certification path of the `OpenConsole.exe` binary to ensure that only one that is signed by Microsoft can be the substitute server host for the application. This doesn't stop third parties from redistributing our `OpenConsole.exe` off of GitHub if necessary with their products, but it would stop someone from introducing any random binary that met the signature interface of the delegation methods into `conhost.exe`. The only value I see this providing is stopping someone from being "tricked" into delegating their `conhost.exe` to another binary through the configuration methods we provide. It doesn't really stop someone (or an attacker) from taking ownership of the `conhost.exe` in System32 and replacing it directly. So this point might be moot. (It is expected that replacement of the System32 one is already protected, to some degree, by being owned by the SYSTEM account and requiring some measure of authority to replace.) + +### Reliability + +The change on its own may honestly improve reliability of the hosting system. The existing just-in-time startup of the console host application only had a single chance at initializing a user experience before it would give up and return that the command-line application could not be started. + +However, there are now several phases in the startup process that will have the opportunity to make multiple attempts at multiple versions or applications to find a suitable host for the starting application before giving up. + +One layer of this is where the `conhost.exe` baked into the operating system will be on the lookout for an `OpenConsole.exe` that will replace its server activities. The delegation binary loses a bit of reliability, theoretically, by the fact that loading another process during launch could have versioning/resolution/path/dependency issues, but it simultaneously offers us the opportunity for improved reliability by being able to service that binary quickly outside the Windows OS release cycle. Fixes can arrive in days instead of months to years. + +Another layer of this is where either `conhost.exe` or the delegated `OpenConsole.exe` server will search for a terminal user experience host, like `WindowsTerminal.exe` or another registered first or third party host, and split the responsibility of hosting the session with that binary. Again, there's a theoretical reliability loss with the additional process launch/load, but there's much to be gained by reducing the scope of what each binary must accomplish. Removing the need to handle user interaction from `conhost.exe` or `OpenConsole.exe` and delegating those activities means there is less surface area running and less chance for a UX interaction to interfere with API call servicing and vice versa. And again, having the delegation to external components means that they can be fixed on a timeline of days instead of months or years as when baked into the operating system. + +### Compatibility + +One particular scenario that this could break is an application that makes use of spelunking the process tree when a command-line application starts to identify the hosting terminal application window by HWND to inject input, extract output, or otherwise hook and bind to hosting services. As the default application UI that will launch may not have the `conhost.exe` name (for spelunking via searching processes) and the HWND located may either be the ConPTY fake HWND or an HWND belonging to a completely different UI, these applications might not work. + +Two considerations here: + +1. At a minimum, we must offer an opt-out of the delegation to another terminal for the default application. +1. We may also want to offer a process-name, policy, manifest, or other per client application opt-out mechanism. + +**V1 NOTE:** There is no per-client specific way of doing this. The toggle is per-user and can be adjusted in 3 different places. + +### Performance, Power, and Efficiency + +I expect to take some degree of performance, power, and efficiency hit by implementing this replacement default app scenario just by it's nature. We will be loading multiple processes, performing tests and branches during startup, and we will likely need to load COM/WinRT and packaging data that was not loaded prior to resolve the final state of default application load. I would expect this to accrue to some failures in the performance and power gates inside the Windows product. Additionally, the efficiency of running pretty much everything through the ConPTY is lower than just rendering it directly to `conhost.exe`'s embedded GDI-powered UI itself thanks to the multiple levels of translation and parsing that occur in this scenario. + +The mitigations to these losses are as follows: + +1. We will delay load any of the interface load and packaging data lookup libraries to only be pulled into process space should we determine that the application is non-interactive. + 1. That should save us some of the commit and power costs for the sorts of non-interactive scripts and applications that typically run early in OS startup (and leverage `conhost.exe` as their host environment). + 1. We will still likely get hit with the on-disk commit cost for the additional export libraries linked as well as additional code. That would be a by-design change. + +1. We plan to begin Profile Guided Optimization across our `OpenConsole.exe` and `WindowsTerminal.exe` binaries. This should allow us to optimize the startup paths for this scenario and bias the `OpenConsole.exe` binary that we redistribute to focus its efforts and efficiency on the ConPTY role specifically, ignoring all of the interactive Win32/GDI portions that aren't typically used. + 1. We may need to add a PGO scenario inside Windows to tune the optimization of `conhost.exe` especially if we're going to go full on Windows Terminal in the box default application. The existing PGO that occurs in the optimization branches is running on several `conhost.exe` interactive scenarios, none of which will be relevant here. We would probably want to update it to focus on the default app delegation routine AND on the non-interactive scenario for hosted applications (where delegation will not occur but Win32/GDI will still not be involved). + +## Potential Issues + +### Passing Handles with COM +COM doesn't inherently expose a way for us to pass handles directly between processes with the existing contracts. We know this is possible because Windows does it all the time, but it doesn't appear to be public. We believe the mission forward is to expose this functionality to the public as if it's good enough for us internally and it is a requirement to build complex functionality like this... then it should be good enough for the public. + +**V1 NOTE:** We gained approval to open this up and documented it. [`system_handle` attribute](https://docs.microsoft.com/windows/win32/midl/system-handle). It didn't require any code changes because the public IDL compiler already recognized the existence of this attribute and did the correct thing. It just wasn't documented for use. + +## Future considerations + +* We additionally would like to leave the door open to distributing updated `OpenConsole.exe`s in their own app package as a dependency that others could rely on. + * This was one of the original management requests when we were opening the source of the console product as well as the Terminal back in spring of 2019. For the sake of ongoing servicing and maintainability, it was requested that we reach a point where our dependencies could be serviced potentially independently of the product as a whole static unit. We didn't achieve that goal initially, but this design would enable us to do something like this. + * One negative to this scenario is that dependency resolution and the installation of dependent packages through APPX is currently lacking in several ways. It's difficult/impossible to do in environments where the store or the internet is unavailable. And it's a problem often enough that the Windows Terminal package embeds the VC runtimes inside itself instead of relying on the dependency resolution of the app platform. + +## Resources + +- [Windows Terminal Process Model 2.0 spec](https://github.com/microsoft/terminal/pull/7240) +- [Windows Terminal 2.0 Process Model Improvements](https://github.com/microsoft/terminal/issues/5000) +- [Console allocation policy specifications](https://github.com/microsoft/terminal/pull/7337) +- [Fine-grained console allocation policy feature](https://github.com/microsoft/terminal/issues/7335) From 8962c75c61fbb18eeaed065e0e03e2d3697b65f1 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 1 Mar 2022 12:50:53 -0600 Subject: [PATCH 15/75] Fix a crash setting the hotkey during teardown (#12580) From MSFT:36797001. Okay so this is only .22% of our crashes, but every little bit helps, right? Turns out this is also hitting in: * MSFT:35726322 * MSFT:34662459 and together they're a fairly hot bug. There's a large class of bugs where we might get a callback to one of our event handlers when we call `app.Close()` in the `AppHost` dtor. This PR adds manual revokers to these events, and makes sure to revoke them BEFORE nulling out the `_window`. That will prevent callbacks during the rest of the dtor, when the `_window` is null. --- src/cascadia/WindowsTerminal/AppHost.cpp | 146 +++++++++++++++++------ src/cascadia/WindowsTerminal/AppHost.h | 43 ++++++- 2 files changed, 149 insertions(+), 40 deletions(-) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index ec539fcfa5a..3ebfd52593a 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -79,12 +79,29 @@ AppHost::AppHost() noexcept : _logic, std::placeholders::_1, std::placeholders::_2)); + + // Event handlers: + // MAKE SURE THEY ARE ALL: + // * winrt::auto_revoke + // * revoked manually in the dtor before the window is nulled out. + // + // If you don't, then it's possible for them to get triggered as the app is + // tearing down, after we've nulled out the window, during the dtor. That + // can cause unexpected AV's everywhere. + // + // _window callbacks don't need to be treated this way, because: + // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like this) + // * This particular bug scenario applies when we've already freed the window. + // + // (Most of these events are actually set up in AppHost::Initialize) _window->MouseScrolled({ this, &AppHost::_WindowMouseWheeled }); _window->WindowActivated({ this, &AppHost::_WindowActivated }); _window->WindowMoved({ this, &AppHost::_WindowMoved }); _window->HotkeyPressed({ this, &AppHost::_GlobalHotkeyPressed }); - _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); _window->ShouldExitFullscreen({ &_logic, &winrt::TerminalApp::AppLogic::RequestExitFullscreen }); + + _window->SetAlwaysOnTop(_logic.GetInitialAlwaysOnTop()); + _window->MakeWindow(); _GetWindowLayoutRequestedToken = _windowManager.GetWindowLayoutRequested([this](auto&&, const winrt::Microsoft::Terminal::Remoting::GetWindowLayoutArgs& args) { @@ -93,7 +110,7 @@ AppHost::AppHost() noexcept : args.WindowLayoutJsonAsync(_GetWindowLayoutAsync()); }); - _windowManager.BecameMonarch({ this, &AppHost::_BecomeMonarch }); + _revokers.BecameMonarch = _windowManager.BecameMonarch(winrt::auto_revoke, { this, &AppHost::_BecomeMonarch }); if (_windowManager.IsMonarch()) { _BecomeMonarch(nullptr, nullptr); @@ -103,6 +120,13 @@ AppHost::AppHost() noexcept : AppHost::~AppHost() { // destruction order is important for proper teardown here + + // revoke ALL our event handlers. There's a large class of bugs where we + // might get a callback to one of these when we call app.Close() below. Make + // sure to revoke these first, so we won't get any more callbacks, then null + // out the window, then close the app. + _revokers = {}; + _window = nullptr; _app.Close(); _app = nullptr; @@ -232,11 +256,12 @@ void AppHost::_HandleCommandlineArgs() // future commandline invocations. When our peasant is told to execute a // commandline (in the future), it'll trigger this callback, that we'll // use to send the actions to the app. - peasant.ExecuteCommandlineRequested({ this, &AppHost::_DispatchCommandline }); - peasant.SummonRequested({ this, &AppHost::_HandleSummon }); - - peasant.DisplayWindowIdRequested({ this, &AppHost::_DisplayWindowId }); - peasant.QuitRequested({ this, &AppHost::_QuitRequested }); + // + // MORE EVENT HANDLERS, same rules as the ones above. + _revokers.peasantExecuteCommandlineRequested = peasant.ExecuteCommandlineRequested(winrt::auto_revoke, { this, &AppHost::_DispatchCommandline }); + _revokers.peasantSummonRequested = peasant.SummonRequested(winrt::auto_revoke, { this, &AppHost::_HandleSummon }); + _revokers.peasantDisplayWindowIdRequested = peasant.DisplayWindowIdRequested(winrt::auto_revoke, { this, &AppHost::_DisplayWindowId }); + _revokers.peasantQuitRequested = peasant.QuitRequested(winrt::auto_revoke, { this, &AppHost::_QuitRequested }); // We need this property to be set before we get the InitialSize/Position // and BecameMonarch which normally sets it is only run after the window @@ -320,30 +345,39 @@ void AppHost::Initialize() _logic.SetTitleBarContent({ this, &AppHost::_UpdateTitleBarContent }); } + // MORE EVENT HANDLERS HERE! + // MAKE SURE THEY ARE ALL: + // * winrt::auto_revoke + // * revoked manually in the dtor before the window is nulled out. + // + // If you don't, then it's possible for them to get triggered as the app is + // tearing down, after we've nulled out the window, during the dtor. That + // can cause unexpected AV's everywhere. + // + // _window callbacks don't need to be treated this way, because: + // * IslandWindow isn't a WinRT type (so it doesn't have neat revokers like this) + // * This particular bug scenario applies when we've already freed the window. + // Register the 'X' button of the window for a warning experience of multiple // tabs opened, this is consistent with Alt+F4 closing _window->WindowCloseButtonClicked([this]() { - const auto pos = _GetWindowLaunchPosition(); - _logic.CloseWindow(pos); + _CloseRequested(nullptr, nullptr); }); // If the user requests a close in another way handle the same as if the 'X' // was clicked. - _logic.CloseRequested([this](auto&&, auto&&) { - const auto pos = _GetWindowLaunchPosition(); - _logic.CloseWindow(pos); - }); + _revokers.CloseRequested = _logic.CloseRequested(winrt::auto_revoke, { this, &AppHost::_CloseRequested }); // Add an event handler to plumb clicks in the titlebar area down to the // application layer. _window->DragRegionClicked([this]() { _logic.TitlebarClicked(); }); - _logic.RequestedThemeChanged({ this, &AppHost::_UpdateTheme }); - _logic.FullscreenChanged({ this, &AppHost::_FullscreenChanged }); - _logic.FocusModeChanged({ this, &AppHost::_FocusModeChanged }); - _logic.AlwaysOnTopChanged({ this, &AppHost::_AlwaysOnTopChanged }); - _logic.RaiseVisualBell({ this, &AppHost::_RaiseVisualBell }); - _logic.SystemMenuChangeRequested({ this, &AppHost::_SystemMenuChangeRequested }); - _logic.ChangeMaximizeRequested({ this, &AppHost::_ChangeMaximizeRequested }); + _revokers.RequestedThemeChanged = _logic.RequestedThemeChanged(winrt::auto_revoke, { this, &AppHost::_UpdateTheme }); + _revokers.FullscreenChanged = _logic.FullscreenChanged(winrt::auto_revoke, { this, &AppHost::_FullscreenChanged }); + _revokers.FocusModeChanged = _logic.FocusModeChanged(winrt::auto_revoke, { this, &AppHost::_FocusModeChanged }); + _revokers.AlwaysOnTopChanged = _logic.AlwaysOnTopChanged(winrt::auto_revoke, { this, &AppHost::_AlwaysOnTopChanged }); + _revokers.RaiseVisualBell = _logic.RaiseVisualBell(winrt::auto_revoke, { this, &AppHost::_RaiseVisualBell }); + _revokers.SystemMenuChangeRequested = _logic.SystemMenuChangeRequested(winrt::auto_revoke, { this, &AppHost::_SystemMenuChangeRequested }); + _revokers.ChangeMaximizeRequested = _logic.ChangeMaximizeRequested(winrt::auto_revoke, { this, &AppHost::_ChangeMaximizeRequested }); _window->MaximizeChanged([this](bool newMaximize) { if (_logic) @@ -354,16 +388,16 @@ void AppHost::Initialize() _logic.Create(); - _logic.TitleChanged({ this, &AppHost::AppTitleChanged }); - _logic.LastTabClosed({ this, &AppHost::LastTabClosed }); - _logic.SetTaskbarProgress({ this, &AppHost::SetTaskbarProgress }); - _logic.IdentifyWindowsRequested({ this, &AppHost::_IdentifyWindowsRequested }); - _logic.RenameWindowRequested({ this, &AppHost::_RenameWindowRequested }); - _logic.SettingsChanged({ this, &AppHost::_HandleSettingsChanged }); - _logic.IsQuakeWindowChanged({ this, &AppHost::_IsQuakeWindowChanged }); - _logic.SummonWindowRequested({ this, &AppHost::_SummonWindowRequested }); - _logic.OpenSystemMenu({ this, &AppHost::_OpenSystemMenu }); - _logic.QuitRequested({ this, &AppHost::_RequestQuitAll }); + _revokers.TitleChanged = _logic.TitleChanged(winrt::auto_revoke, { this, &AppHost::AppTitleChanged }); + _revokers.LastTabClosed = _logic.LastTabClosed(winrt::auto_revoke, { this, &AppHost::LastTabClosed }); + _revokers.SetTaskbarProgress = _logic.SetTaskbarProgress(winrt::auto_revoke, { this, &AppHost::SetTaskbarProgress }); + _revokers.IdentifyWindowsRequested = _logic.IdentifyWindowsRequested(winrt::auto_revoke, { this, &AppHost::_IdentifyWindowsRequested }); + _revokers.RenameWindowRequested = _logic.RenameWindowRequested(winrt::auto_revoke, { this, &AppHost::_RenameWindowRequested }); + _revokers.SettingsChanged = _logic.SettingsChanged(winrt::auto_revoke, { this, &AppHost::_HandleSettingsChanged }); + _revokers.IsQuakeWindowChanged = _logic.IsQuakeWindowChanged(winrt::auto_revoke, { this, &AppHost::_IsQuakeWindowChanged }); + _revokers.SummonWindowRequested = _logic.SummonWindowRequested(winrt::auto_revoke, { this, &AppHost::_SummonWindowRequested }); + _revokers.OpenSystemMenu = _logic.OpenSystemMenu(winrt::auto_revoke, { this, &AppHost::_OpenSystemMenu }); + _revokers.QuitRequested = _logic.QuitRequested(winrt::auto_revoke, { this, &AppHost::_RequestQuitAll }); _window->UpdateTitle(_logic.Title()); @@ -424,7 +458,7 @@ void AppHost::LastTabClosed(const winrt::Windows::Foundation::IInspectable& /*se } else if (_window->IsQuakeWindow()) { - _HideNotificationIconRequested(); + _HideNotificationIconRequested(nullptr, nullptr); } // We don't want to try to save layouts if we are about to close. @@ -690,6 +724,15 @@ void AppHost::_ChangeMaximizeRequested(const winrt::Windows::Foundation::IInspec void AppHost::_AlwaysOnTopChanged(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::Foundation::IInspectable&) { + // MSFT:34662459 + // + // Although we're manually revoking the event handler now in the dtor before + // we null out the window, let's be extra careful and check JUST IN CASE. + if (_window == nullptr) + { + return; + } + _window->SetAlwaysOnTop(_logic.AlwaysOnTop()); } @@ -852,6 +895,15 @@ winrt::fire_and_forget AppHost::_WindowActivated() void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*sender*/, const winrt::Windows::Foundation::IInspectable& /*args*/) { + // MSFT:35726322 + // + // Although we're manually revoking the event handler now in the dtor before + // we null out the window, let's be extra careful and check JUST IN CASE. + if (_window == nullptr) + { + return; + } + _setupGlobalHotkeys(); if (_windowManager.DoesQuakeWindowExist() || @@ -880,11 +932,11 @@ void AppHost::_BecomeMonarch(const winrt::Windows::Foundation::IInspectable& /*s }); // These events are coming from peasants that become or un-become quake windows. - _windowManager.ShowNotificationIconRequested([this](auto&&, auto&&) { _ShowNotificationIconRequested(); }); - _windowManager.HideNotificationIconRequested([this](auto&&, auto&&) { _HideNotificationIconRequested(); }); + _revokers.ShowNotificationIconRequested = _windowManager.ShowNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_ShowNotificationIconRequested }); + _revokers.HideNotificationIconRequested = _windowManager.HideNotificationIconRequested(winrt::auto_revoke, { this, &AppHost::_HideNotificationIconRequested }); // If the monarch receives a QuitAll event it will signal this event to be // ran before each peasant is closed. - _windowManager.QuitAllRequested({ this, &AppHost::_QuitAllRequested }); + _revokers.QuitAllRequested = _windowManager.QuitAllRequested(winrt::auto_revoke, { this, &AppHost::_QuitAllRequested }); // The monarch should be monitoring if it should save the window layout. if (!_getWindowLayoutThrottler.has_value()) @@ -967,6 +1019,15 @@ winrt::fire_and_forget AppHost::_setupGlobalHotkeys() // The hotkey MUST be registered on the main thread. It will fail otherwise! co_await winrt::resume_foreground(_logic.GetRoot().Dispatcher()); + if (!_window) + { + // MSFT:36797001 There's a surprising number of hits of this callback + // getting triggered during teardown. As a best practice, we really + // should make sure _window exists before accessing it on any coroutine. + // We might be getting called back after the app already began getting + // cleaned up. + co_return; + } // Unregister all previously registered hotkeys. // // RegisterHotKey(), will not unregister hotkeys automatically. @@ -1279,11 +1340,11 @@ void AppHost::_IsQuakeWindowChanged(const winrt::Windows::Foundation::IInspectab // specifically for the quake window. If not, it should not be destroyed. if (!_window->IsQuakeWindow() && _logic.IsQuakeWindow()) { - _ShowNotificationIconRequested(); + _ShowNotificationIconRequested(nullptr, nullptr); } else if (_window->IsQuakeWindow() && !_logic.IsQuakeWindow()) { - _HideNotificationIconRequested(); + _HideNotificationIconRequested(nullptr, nullptr); } _window->IsQuakeWindow(_logic.IsQuakeWindow()); @@ -1392,7 +1453,8 @@ void AppHost::_DestroyNotificationIcon() _notificationIcon = nullptr; } -void AppHost::_ShowNotificationIconRequested() +void AppHost::_ShowNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) { if (_windowManager.IsMonarch()) { @@ -1407,7 +1469,8 @@ void AppHost::_ShowNotificationIconRequested() } } -void AppHost::_HideNotificationIconRequested() +void AppHost::_HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) { if (_windowManager.IsMonarch()) { @@ -1466,3 +1529,10 @@ void AppHost::_WindowMoved() } } } + +void AppHost::_CloseRequested(const winrt::Windows::Foundation::IInspectable& /*sender*/, + const winrt::Windows::Foundation::IInspectable& /*args*/) +{ + const auto pos = _GetWindowLaunchPosition(); + _logic.CloseWindow(pos); +} diff --git a/src/cascadia/WindowsTerminal/AppHost.h b/src/cascadia/WindowsTerminal/AppHost.h index debc0c3a807..c4c15555b8d 100644 --- a/src/cascadia/WindowsTerminal/AppHost.h +++ b/src/cascadia/WindowsTerminal/AppHost.h @@ -106,14 +106,18 @@ class AppHost void _RequestQuitAll(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& args); + void _CloseRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); void _QuitAllRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::QuitAllRequestedArgs& args); void _CreateNotificationIcon(); void _DestroyNotificationIcon(); - void _ShowNotificationIconRequested(); - void _HideNotificationIconRequested(); + void _ShowNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); + void _HideNotificationIconRequested(const winrt::Windows::Foundation::IInspectable& sender, + const winrt::Windows::Foundation::IInspectable& args); std::unique_ptr _notificationIcon; winrt::event_token _ReAddNotificationIconToken; winrt::event_token _NotificationIconPressedToken; @@ -122,4 +126,39 @@ class AppHost winrt::event_token _GetWindowLayoutRequestedToken; winrt::event_token _WindowCreatedToken; winrt::event_token _WindowClosedToken; + + // Helper struct. By putting these all into one struct, we can revoke them + // all at once, by assigning _revokers to a fresh Revokers instance. That'll + // cause us to dtor the old one, which will immediately call revoke on all + // the members as a part of their own dtors. + struct Revokers + { + // Event handlers to revoke in ~AppHost, before calling App.Close + winrt::Microsoft::Terminal::Remoting::WindowManager::BecameMonarch_revoker BecameMonarch; + winrt::Microsoft::Terminal::Remoting::Peasant::ExecuteCommandlineRequested_revoker peasantExecuteCommandlineRequested; + winrt::Microsoft::Terminal::Remoting::Peasant::SummonRequested_revoker peasantSummonRequested; + winrt::Microsoft::Terminal::Remoting::Peasant::DisplayWindowIdRequested_revoker peasantDisplayWindowIdRequested; + winrt::Microsoft::Terminal::Remoting::Peasant::QuitRequested_revoker peasantQuitRequested; + winrt::TerminalApp::AppLogic::CloseRequested_revoker CloseRequested; + winrt::TerminalApp::AppLogic::RequestedThemeChanged_revoker RequestedThemeChanged; + winrt::TerminalApp::AppLogic::FullscreenChanged_revoker FullscreenChanged; + winrt::TerminalApp::AppLogic::FocusModeChanged_revoker FocusModeChanged; + winrt::TerminalApp::AppLogic::AlwaysOnTopChanged_revoker AlwaysOnTopChanged; + winrt::TerminalApp::AppLogic::RaiseVisualBell_revoker RaiseVisualBell; + winrt::TerminalApp::AppLogic::SystemMenuChangeRequested_revoker SystemMenuChangeRequested; + winrt::TerminalApp::AppLogic::ChangeMaximizeRequested_revoker ChangeMaximizeRequested; + winrt::TerminalApp::AppLogic::TitleChanged_revoker TitleChanged; + winrt::TerminalApp::AppLogic::LastTabClosed_revoker LastTabClosed; + winrt::TerminalApp::AppLogic::SetTaskbarProgress_revoker SetTaskbarProgress; + winrt::TerminalApp::AppLogic::IdentifyWindowsRequested_revoker IdentifyWindowsRequested; + winrt::TerminalApp::AppLogic::RenameWindowRequested_revoker RenameWindowRequested; + winrt::TerminalApp::AppLogic::SettingsChanged_revoker SettingsChanged; + winrt::TerminalApp::AppLogic::IsQuakeWindowChanged_revoker IsQuakeWindowChanged; + winrt::TerminalApp::AppLogic::SummonWindowRequested_revoker SummonWindowRequested; + winrt::TerminalApp::AppLogic::OpenSystemMenu_revoker OpenSystemMenu; + winrt::TerminalApp::AppLogic::QuitRequested_revoker QuitRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager::ShowNotificationIconRequested_revoker ShowNotificationIconRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager::HideNotificationIconRequested_revoker HideNotificationIconRequested; + winrt::Microsoft::Terminal::Remoting::WindowManager::QuitAllRequested_revoker QuitAllRequested; + } _revokers{}; }; From 79a08ecd18c4d00bc1cc48cb0d81fe9ed49c97c3 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 2 Mar 2022 00:02:08 +0100 Subject: [PATCH 16/75] Fix fail-fast due to unlocked FreeProcessData call (#12599) 2b202ce6 introduced a bug, where FreeProcessData was called without the console lock being held. The previous code can be found in 40e3dea, on line 441-454. ## PR Checklist * [x] Closes MSFT:21372705 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed None, as this fix is purely theoretic, but it matches the stack trace and 40e3dea clearly wasn't correctly ported to strict C++ either. --- src/server/IoDispatchers.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index f52cba56c7d..e19526cccf5 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -288,8 +288,6 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API LockConsole(); const auto cleanup = wil::scope_exit([&]() noexcept { - UnlockConsole(); - if (!NT_SUCCESS(Status)) { pReceiveMsg->SetReplyStatus(Status); @@ -299,6 +297,9 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API gci.ProcessHandleList.FreeProcessData(ProcessData); } } + + // FreeProcessData() above requires the console to be locked. + UnlockConsole(); }); DWORD const dwProcessId = (DWORD)pReceiveMsg->Descriptor.Process; From 00113e3e48ddb702826a91cb25cbbe741ab56dd8 Mon Sep 17 00:00:00 2001 From: Ian O'Neill Date: Tue, 1 Mar 2022 23:14:16 +0000 Subject: [PATCH 17/75] Fix RTF generation for Unicode characters (#12586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request Fixes RTF generation for text with Unicode characters. ## PR Checklist * [x] Closes #12379 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Tests added/passed * [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [ ] Schema updated. * [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Validation Steps Performed Added some unit tests. Ran the following in PowerShell and copied the emitted text into WordPad. ```pwsh echo "This is some Ascii \ {}`nLow code units: á é í ó ú `u{2b81} `u{2b82}`nHigh code units: `u{a7b5} `u{a7b7}`nSurrogates: `u{1f366} `u{1f47e} `u{1f440}" ``` --- src/buffer/out/textBuffer.cpp | 41 ++++++++++++++++++---------- src/buffer/out/textBuffer.hpp | 2 ++ src/host/ut_host/TextBufferTests.cpp | 33 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 777dc83c1da..a7df90e8c76 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2034,20 +2034,8 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi const auto writeAccumulatedChars = [&](bool includeCurrent) { if (col >= startOffset) { - const auto unescapedText = ConvertToA(CP_UTF8, std::wstring_view(rows.text.at(row)).substr(startOffset, col - startOffset + includeCurrent)); - for (const auto c : unescapedText) - { - switch (c) - { - case '\\': - case '{': - case '}': - contentBuilder << "\\" << c; - break; - default: - contentBuilder << c; - } - } + const auto text = std::wstring_view{ rows.text.at(row) }.substr(startOffset, col - startOffset + includeCurrent); + _AppendRTFText(contentBuilder, text); startOffset = col; } @@ -2146,6 +2134,31 @@ std::string TextBuffer::GenRTF(const TextAndColor& rows, const int fontHeightPoi } } +void TextBuffer::_AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text) +{ + for (const auto codeUnit : text) + { + if (codeUnit <= 127) + { + switch (codeUnit) + { + case L'\\': + case L'{': + case L'}': + contentBuilder << "\\" << gsl::narrow(codeUnit); + break; + default: + contentBuilder << gsl::narrow(codeUnit); + } + } + else + { + // Windows uses unsigned wchar_t - RTF uses signed ones. + contentBuilder << "\\u" << std::to_string(til::bit_cast(codeUnit)) << "?"; + } + } +} + // Function Description: // - Reflow the contents from the old buffer into the new buffer. The new buffer // can have different dimensions than the old buffer. If it does, then this diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index a2b34ed5b88..fe87ee8822d 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -247,6 +247,8 @@ class TextBuffer final void _PruneHyperlinks(); + static void _AppendRTFText(std::ostringstream& contentBuilder, const std::wstring_view& text); + std::unordered_map _idsAndPatterns; size_t _currentPatternId; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 3f18d754b96..489768461fe 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -146,6 +146,8 @@ class TextBufferTests TEST_METHOD(TestBurrito); + TEST_METHOD(TestAppendRTFText); + void WriteLinesToBuffer(const std::vector& text, TextBuffer& buffer); TEST_METHOD(GetWordBoundaries); TEST_METHOD(MoveByWord); @@ -2011,6 +2013,37 @@ void TextBufferTests::TestBurrito() VERIFY_IS_FALSE(afterBurritoIter); } +void TextBufferTests::TestAppendRTFText() +{ + { + std::ostringstream contentStream; + const auto ascii = L"This is some Ascii \\ {}"; + TextBuffer::_AppendRTFText(contentStream, ascii); + VERIFY_ARE_EQUAL("This is some Ascii \\\\ \\{\\}", contentStream.str()); + } + { + std::ostringstream contentStream; + // "Low code units: á é í ó ú ⮁ ⮂" in UTF-16 + const auto lowCodeUnits = L"Low code units: \x00E1 \x00E9 \x00ED \x00F3 \x00FA \x2B81 \x2B82"; + TextBuffer::_AppendRTFText(contentStream, lowCodeUnits); + VERIFY_ARE_EQUAL("Low code units: \\u225? \\u233? \\u237? \\u243? \\u250? \\u11137? \\u11138?", contentStream.str()); + } + { + std::ostringstream contentStream; + // "High code units: ꞵ ꞷ" in UTF-16 + const auto highCodeUnits = L"High code units: \xA7B5 \xA7B7"; + TextBuffer::_AppendRTFText(contentStream, highCodeUnits); + VERIFY_ARE_EQUAL("High code units: \\u-22603? \\u-22601?", contentStream.str()); + } + { + std::ostringstream contentStream; + // "Surrogates: 🍦 👾 👀" in UTF-16 + const auto surrogates = L"Surrogates: \xD83C\xDF66 \xD83D\xDC7E \xD83D\xDC40"; + TextBuffer::_AppendRTFText(contentStream, surrogates); + VERIFY_ARE_EQUAL("Surrogates: \\u-10180?\\u-8346? \\u-10179?\\u-9090? \\u-10179?\\u-9152?", contentStream.str()); + } +} + void TextBufferTests::WriteLinesToBuffer(const std::vector& text, TextBuffer& buffer) { const auto bufferSize = buffer.GetSize(); From 9367b22270b28390034ea7001e36af24b54eb844 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 2 Mar 2022 11:57:52 -0600 Subject: [PATCH 18/75] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a22cb89ea3d..fef18a43983 100644 --- a/README.md +++ b/README.md @@ -274,7 +274,7 @@ If you would like to ask a question that you feel doesn't warrant an issue * Dustin Howett, Engineering Lead: [@dhowett](https://twitter.com/DHowett) * Michael Niksa, Senior Developer: [@michaelniksa](https://twitter.com/MichaelNiksa) -* Mike Griese, Developer: [@zadjii](https://twitter.com/zadjii) +* Mike Griese, Senior Developer: [@zadjii](https://twitter.com/zadjii) * Carlos Zamora, Developer: [@cazamor_msft](https://twitter.com/cazamor_msft) * Leon Liang, Developer: [@leonmsft](https://twitter.com/leonmsft) * Pankaj Bhojwani, Developer From 34d81b1c461b1dc1f5950ab75a95092fb5ee2784 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 2 Mar 2022 00:02:08 +0100 Subject: [PATCH 19/75] Fix fail-fast due to unlocked FreeProcessData call (#12599) 2b202ce6 introduced a bug, where FreeProcessData was called without the console lock being held. The previous code can be found in 40e3dea, on line 441-454. ## PR Checklist * [x] Closes MSFT:21372705 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed None, as this fix is purely theoretic, but it matches the stack trace and 40e3dea clearly wasn't correctly ported to strict C++ either. --- src/server/IoDispatchers.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index 1488b2b71d8..0c90da80581 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -287,8 +287,6 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API LockConsole(); const auto cleanup = wil::scope_exit([&]() noexcept { - UnlockConsole(); - if (!NT_SUCCESS(Status)) { pReceiveMsg->SetReplyStatus(Status); @@ -298,6 +296,9 @@ PCONSOLE_API_MSG IoDispatchers::ConsoleHandleConnectionRequest(_In_ PCONSOLE_API gci.ProcessHandleList.FreeProcessData(ProcessData); } } + + // FreeProcessData() above requires the console to be locked. + UnlockConsole(); }); DWORD const dwProcessId = (DWORD)pReceiveMsg->Descriptor.Process; From f4066cd0b0b8d584a4e239394f8e0be938a91430 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 3 Mar 2022 13:04:18 -0800 Subject: [PATCH 20/75] Fix release YML paths for VPACK for Win10/11 split (#12621) --- build/pipelines/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml index abe79847680..69c9b753952 100644 --- a/build/pipelines/release.yml +++ b/build/pipelines/release.yml @@ -569,13 +569,13 @@ jobs: mkdir WindowsTerminal.app mv Microsoft.WindowsTerminal_8wekyb3d8bbwe.msixbundle .\WindowsTerminal.app\ - workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed + workingDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11 - task: PkgESVPack@12 displayName: 'Package ES - VPack' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: - sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed\WindowsTerminal.app + sourceDirectory: $(System.ArtifactsDirectory)\appxbundle-signed-Win11\WindowsTerminal.app description: VPack for the Windows Terminal Application pushPkgName: WindowsTerminal.app owner: conhost From f217f6dc331559f57301dfb84ce3898d20c944d6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 4 Mar 2022 19:39:58 +0100 Subject: [PATCH 21/75] Replace Summon with Show/Hide for improved i18n (#12603) "Summon" was translated as a synonym for "citation" in Spanish instead of treating it as a RPG-related word. "Show/Hide" will hopefully allow an improved automatic translation in the future. Closes #10691 --- .../TerminalSettingsModel/Resources/en-US/Resources.resw | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw index 1f883be9738..5d7d029bfda 100644 --- a/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsModel/Resources/en-US/Resources.resw @@ -464,10 +464,11 @@ Rename window... - Summon the Terminal window + Show/Hide the Terminal window - Summon Quake window + Show/Hide Quake window + Quake is a well-known computer game and isn't related to earthquakes, etc. See https://en.wikipedia.org/wiki/Quake_(video_game) Focus pane {0} From 91f5648b5ef60389d46ced5f82e5650c5906dfff Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 9 Mar 2022 14:49:22 -0800 Subject: [PATCH 22/75] Stop making settings.json backups (#12652) ## Summary of the Pull Request This makes it so that the settings.json backups are no longer created when the user saves their settings via the Settings UI. Closes #11703 --- .../TerminalSettingsModel/CascadiaSettingsSerialization.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 87a7ffdca8d..483f74c6cb6 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -894,12 +894,6 @@ void CascadiaSettings::WriteSettingsToDisk() const { const auto settingsPath = _settingsPath(); - { - // create a timestamped backup file - const auto backupSettingsPath = fmt::format(L"{}.{:%Y-%m-%dT%H-%M-%S}.backup", settingsPath.native(), fmt::localtime(std::time(nullptr))); - LOG_IF_WIN32_BOOL_FALSE(CopyFileW(settingsPath.c_str(), backupSettingsPath.c_str(), TRUE)); - } - // write current settings to current settings file Json::StreamWriterBuilder wbuilder; wbuilder.settings_["indentation"] = " "; From da2cf8c3f667dfa1d1e25d1be083da6b0c47a83e Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Wed, 9 Mar 2022 17:52:07 -0600 Subject: [PATCH 23/75] Make the Scrollbar 16dips again (#12608) BODGY: Controlsv2 changed the size of the scrollbars from 16dips to 12dips. This is harder for folks to hit with the mouse, and isn't consistent with the rest of the scrollbars on the platform (as much as they can be). To work around this, we have to entirely copy the template for the ScrollBar into our XAML file. We're then also re-defining ScrollBarSize here to 16, so that the new template will pick up on the new value. This is kinda a pain, and we have to be careful to be sure to ingest an updated version of the template any time we update MUX. The latest Controlsv2 version of the template can be found at: https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ScrollBar_themeresources.xaml#L218 We're also planning on making this adjustable in the future (GH#9218), where we might need this anyways. ##### after, before: ![image](https://user-images.githubusercontent.com/18356694/156254464-1a9080f6-51ce-4619-b002-2a3c607cdf5f.png) ##### after overlayed on top of before ![image](https://user-images.githubusercontent.com/18356694/156254546-fccc3cee-12a3-4e1a-8fd7-7470f1ec93ad.png) ##### comparison ![image](https://user-images.githubusercontent.com/18356694/156257934-ec4ac840-c8ca-4fca-a848-08a32b1c55c3.png) * reported originally in #12395 * upstream: https://github.com/microsoft/microsoft-ui-xaml/issues/6684 * closes an element of #12400 --- .github/actions/spelling/allow/allow.txt | 4 + src/cascadia/TerminalControl/TermControl.xaml | 1141 +++++++++++++++++ 2 files changed, 1145 insertions(+) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 50d9b75bc70..37a5b7d0f28 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -21,6 +21,8 @@ downside downsides dze dzhe +EDDB +EDDC Enum'd Fitt formattings @@ -74,6 +76,8 @@ runtimes shcha slnt Sos +timeline +timelines timestamped TLDR tokenizes diff --git a/src/cascadia/TerminalControl/TermControl.xaml b/src/cascadia/TerminalControl/TermControl.xaml index 663ccf7ce1a..589f9d4c3f7 100644 --- a/src/cascadia/TerminalControl/TermControl.xaml +++ b/src/cascadia/TerminalControl/TermControl.xaml @@ -5,6 +5,8 @@ + + + + + 16 + 0 + + + + + Color="#0a0a0a" /> @@ -79,14 +84,10 @@ + Color="#dadada" /> From c820af46b7c2e1c1362dd7a5eac4500d345bede0 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 10 Mar 2022 13:27:19 +0100 Subject: [PATCH 25/75] Fix activation context generation for conhost (#12653) This regressed in 2b202ce6, which removed `ACTCTX_FLAG_RESOURCE_NAME_VALID` during `CreateActCtxW` under the assumption that an executable only has one manifest. conhost has two however and we need to pick the correct one. On OpenConsole this causes the expected `ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET`. Closes MSFT:38355907 --- src/host/init.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/host/init.cpp b/src/host/init.cpp index 8ab26062fe5..76c4a8b050a 100644 --- a/src/host/init.cpp +++ b/src/host/init.cpp @@ -32,7 +32,8 @@ void InitSideBySide() actctx.cbSize = sizeof(actctx); // We set ACTCTX_FLAG_HMODULE_VALID, but leave hModule as nullptr. // A nullptr HMODULE refers to the current process/executable. - actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_HMODULE_VALID; + actctx.lpResourceName = MAKEINTRESOURCE(IDR_SYSTEM_MANIFEST); + actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_HMODULE_VALID; HANDLE const hActCtx = CreateActCtxW(&actctx); @@ -44,8 +45,9 @@ void InitSideBySide() { auto const error = GetLastError(); - // Don't log if it's already set. This whole ordeal is to make sure one is set if there isn't one already. - // If one is already set... good! + // OpenConsole ships with a single manifest at ID 1, while conhost ships with 2 at ID 1 + // and IDR_SYSTEM_MANIFEST. If we call CreateActCtxW() with IDR_SYSTEM_MANIFEST inside + // OpenConsole anyways, nothing happens and we get ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET. if (ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET != error) { RIPMSG1(RIP_WARNING, "InitSideBySide failed create an activation context. Error: %d\r\n", error); From f84ccad42d37e531f6e7fba55aa54229722b05e8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 10 Mar 2022 13:36:44 +0100 Subject: [PATCH 26/75] Fix Windows 10 support for nearby font loading (#12554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By replacing `IDWriteFontSetBuilder2::AddFontFile` with `IDWriteFactory5::CreateFontFileReference` and `IDWriteFontSetBuilder1::AddFontFile` we add nearby font loading support for Windows 10, build 15021. This commit also fixes font fallback for AtlasEngine, which was crashing during testing. Finally it fixes a bug in DxEngine, where we only created a "nearby" font collection if we couldn't find the font in the system collection. This doesn't fix the bug, if the font is locked or broken in the system collection. This is related to #11648. ## PR Checklist * [x] Closes #12420 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Build a Debug version of Windows Terminal * Put Jetbrains Mono into the writeable AppX directory * Jetbrains Mono is present in the settings UI ✅ * DxEngine works with Jetbrains Mono ✅ * AtlasEngine works with Jetbrains Mono ✅ --- .../ProfileViewModel.cpp | 60 +------- src/features.xml | 9 ++ src/renderer/atlas/AtlasEngine.api.cpp | 87 ++++++++---- src/renderer/atlas/AtlasEngine.cpp | 3 +- src/renderer/atlas/AtlasEngine.h | 4 +- src/renderer/base/FontCache.h | 96 +++++++++++++ src/renderer/base/lib/base.vcxproj | 1 + src/renderer/base/lib/base.vcxproj.filters | 3 + src/renderer/dx/DxFontInfo.cpp | 132 ++---------------- src/renderer/dx/DxFontInfo.h | 15 +- src/renderer/dx/DxFontRenderData.cpp | 15 +- src/renderer/dx/DxFontRenderData.h | 3 +- src/renderer/dx/DxRenderer.cpp | 2 + 13 files changed, 196 insertions(+), 234 deletions(-) create mode 100644 src/renderer/base/FontCache.h diff --git a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp index baa44c2bb15..86eefe56868 100644 --- a/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/ProfileViewModel.cpp @@ -7,55 +7,8 @@ #include "EnumEntry.h" #include -#include "..\WinRTUtils\inc\Utils.h" - -// This function is a copy of DxFontInfo::_NearbyCollection() with -// * the call to DxFontInfo::s_GetNearbyFonts() inlined -// * checkForUpdates for GetSystemFontCollection() set to true -static wil::com_ptr NearbyCollection(IDWriteFactory* dwriteFactory) -{ - // The convenience interfaces for loading fonts from files - // are only available on Windows 10+. - wil::com_ptr factory6; - // wil's query() facilities don't work inside WinRT land at the moment. - // They produce a compilation error due to IUnknown and winrt::Windows::Foundation::IUnknown being ambiguous. - if (!SUCCEEDED(dwriteFactory->QueryInterface(__uuidof(IDWriteFactory6), factory6.put_void()))) - { - return nullptr; - } - - wil::com_ptr systemFontCollection; - THROW_IF_FAILED(factory6->GetSystemFontCollection(false, systemFontCollection.addressof(), true)); - - wil::com_ptr systemFontSet; - THROW_IF_FAILED(systemFontCollection->GetFontSet(systemFontSet.addressof())); - - wil::com_ptr fontSetBuilder2; - THROW_IF_FAILED(factory6->CreateFontSetBuilder(fontSetBuilder2.addressof())); - - THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.get())); - - { - const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; - const auto folder{ module.parent_path() }; - - for (const auto& p : std::filesystem::directory_iterator(folder)) - { - if (til::ends_with(p.path().native(), L".ttf")) - { - fontSetBuilder2->AddFontFile(p.path().c_str()); - } - } - } - - wil::com_ptr fontSet; - THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(fontSet.addressof())); - - wil::com_ptr fontCollection; - THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.get(), &fontCollection)); - - return fontCollection; -} +#include "../WinRTUtils/inc/Utils.h" +#include "../../renderer/base/FontCache.h" using namespace winrt::Windows::UI::Text; using namespace winrt::Windows::UI::Xaml; @@ -166,15 +119,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation std::vector fontList; std::vector monospaceFontList; - // get a DWriteFactory - com_ptr factory; - THROW_IF_FAILED(DWriteCreateFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<::IUnknown**>(factory.put()))); - // get the font collection; subscribe to updates - const auto fontCollection = NearbyCollection(factory.get()); + const auto fontCollection = ::Microsoft::Console::Render::FontCache::GetFresh(); for (UINT32 i = 0; i < fontCollection->GetFontFamilyCount(); ++i) { diff --git a/src/features.xml b/src/features.xml index 2a04dd1b0b0..d9bc3a87e90 100644 --- a/src/features.xml +++ b/src/features.xml @@ -67,6 +67,15 @@ + + Feature_NearbyFontLoading + Controls whether fonts in the same directory as the binary are used during rendering. Disabled for conhost so that it doesn't iterate the entire system32 directory. + AlwaysEnabled + + WindowsInbox + + + Feature_AdjustIndistinguishableText If enabled, the foreground color will, when necessary, be automatically adjusted to make it more visible. diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 2d0fa45e78a..2e0db997333 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -4,6 +4,8 @@ #include "pch.h" #include "AtlasEngine.h" +#include "../base/FontCache.h" + // #### NOTE #### // If you see any code in here that contains "_r." you might be seeing a race condition. // The AtlasEngine::Present() method is called on a background thread without any locks, @@ -227,7 +229,7 @@ try } #endif - _resolveFontMetrics(fontInfoDesired, fontInfo); + _resolveFontMetrics(nullptr, fontInfoDesired, fontInfo); return S_OK; } CATCH_RETURN() @@ -401,7 +403,50 @@ void AtlasEngine::ToggleShaderEffects() noexcept } [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) noexcept -try +{ + static constexpr std::array fallbackFaceNames{ static_cast(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; + auto it = fallbackFaceNames.begin(); + const auto end = fallbackFaceNames.end(); + + for (;;) + { + try + { + _updateFont(*it, fontInfoDesired, fontInfo, features, axes); + return S_OK; + } + catch (...) + { + ++it; + if (it == end) + { + RETURN_CAUGHT_EXCEPTION(); + } + else + { + LOG_CAUGHT_EXCEPTION(); + } + } + } +} + +void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept +{ + _api.hyperlinkHoveredId = hoveredId; +} + +#pragma endregion + +void AtlasEngine::_resolveAntialiasingMode() noexcept +{ + // If the user asks for ClearType, but also for a transparent background + // (which our ClearType shader doesn't simultaneously support) + // then we need to sneakily force the renderer to grayscale AA. + const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; + _api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; +} + +void AtlasEngine::_updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes) { std::vector fontFeatures; if (!features.empty()) @@ -478,7 +523,7 @@ try } const auto previousCellSize = _api.fontMetrics.cellSize; - _resolveFontMetrics(fontInfoDesired, fontInfo, &_api.fontMetrics); + _resolveFontMetrics(faceName, fontInfoDesired, fontInfo, &_api.fontMetrics); _api.fontFeatures = std::move(fontFeatures); _api.fontAxisValues = std::move(fontAxisValues); @@ -489,37 +534,21 @@ try _api.cellCount = _api.sizeInPixel / _api.fontMetrics.cellSize; WI_SetFlag(_api.invalidations, ApiInvalidations::Size); } - - return S_OK; -} -CATCH_RETURN() - -void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept -{ - _api.hyperlinkHoveredId = hoveredId; } -#pragma endregion - -void AtlasEngine::_resolveAntialiasingMode() noexcept +void AtlasEngine::_resolveFontMetrics(const wchar_t* requestedFaceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics) const { - // If the user asks for ClearType, but also for a transparent background - // (which our ClearType shader doesn't simultaneously support) - // then we need to sneakily force the renderer to grayscale AA. - const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; - _api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; -} - -void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics) const -{ - auto requestedFaceName = fontInfoDesired.GetFaceName().c_str(); const auto requestedFamily = fontInfoDesired.GetFamily(); auto requestedWeight = fontInfoDesired.GetWeight(); auto requestedSize = fontInfoDesired.GetEngineSize(); if (!requestedFaceName) { - requestedFaceName = L"Consolas"; + requestedFaceName = fontInfoDesired.GetFaceName().c_str(); + if (!requestedFaceName) + { + requestedFaceName = L"Consolas"; + } } if (!requestedSize.Y) { @@ -530,16 +559,15 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo requestedWeight = DWRITE_FONT_WEIGHT_NORMAL; } - wil::com_ptr systemFontCollection; - THROW_IF_FAILED(_sr.dwriteFactory->GetSystemFontCollection(systemFontCollection.addressof(), false)); + auto fontCollection = FontCache::GetCached(); u32 index = 0; BOOL exists = false; - THROW_IF_FAILED(systemFontCollection->FindFamilyName(requestedFaceName, &index, &exists)); + THROW_IF_FAILED(fontCollection->FindFamilyName(requestedFaceName, &index, &exists)); THROW_HR_IF(DWRITE_E_NOFONT, !exists); wil::com_ptr fontFamily; - THROW_IF_FAILED(systemFontCollection->GetFontFamily(index, fontFamily.addressof())); + THROW_IF_FAILED(fontCollection->GetFontFamily(index, fontFamily.addressof())); wil::com_ptr font; THROW_IF_FAILED(fontFamily->GetFirstMatchingFont(static_cast(requestedWeight), DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, font.addressof())); @@ -601,6 +629,7 @@ void AtlasEngine::_resolveFontMetrics(const FontInfoDesired& fontInfoDesired, Fo // NOTE: From this point onward no early returns or throwing code should exist, // as we might cause _api to be in an inconsistent state otherwise. + fontMetrics->fontCollection = std::move(fontCollection); fontMetrics->fontName = std::move(fontName); fontMetrics->fontSizeInDIP = static_cast(fontSizeInPx / static_cast(_api.dpi) * 96.0); fontMetrics->baselineInDIP = static_cast(baseline / static_cast(_api.dpi) * 96.0); diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index dd9267698b3..bf108602862 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -7,6 +7,7 @@ #include #include +#include "../base/FontCache.h" #include "../../interactivity/win32/CustomWindowMessages.h" // #### NOTE #### @@ -1023,7 +1024,7 @@ void AtlasEngine::_recreateFontDependentResources() const auto fontStyle = italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; auto& textFormat = _r.textFormats[italic][bold]; - THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.get(), nullptr, fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put())); + THROW_IF_FAILED(_sr.dwriteFactory->CreateTextFormat(_api.fontMetrics.fontName.get(), _api.fontMetrics.fontCollection.get(), fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL, _api.fontMetrics.fontSizeInDIP, L"", textFormat.put())); textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); textFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index df4f095e42c..5c417b7386b 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -376,6 +376,7 @@ namespace Microsoft::Console::Render struct FontMetrics { + wil::com_ptr fontCollection; wil::unique_process_heap_string fontName; float baselineInDIP = 0.0f; float fontSizeInDIP = 0.0f; @@ -615,7 +616,8 @@ namespace Microsoft::Console::Render // AtlasEngine.api.cpp void _resolveAntialiasingMode() noexcept; - void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics = nullptr) const; + void _updateFont(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map& features, const std::unordered_map& axes); + void _resolveFontMetrics(const wchar_t* faceName, const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontMetrics* fontMetrics = nullptr) const; // AtlasEngine.r.cpp void _setShaderResources() const; diff --git a/src/renderer/base/FontCache.h b/src/renderer/base/FontCache.h new file mode 100644 index 00000000000..e7a5c4134ca --- /dev/null +++ b/src/renderer/base/FontCache.h @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace Microsoft::Console::Render::FontCache +{ + namespace details + { + inline const std::vector>& getNearbyFontFiles(IDWriteFactory5* factory5) + { + static const auto fontFiles = [=]() { + std::vector> files; + + const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; + const auto folder{ module.parent_path() }; + + for (const auto& p : std::filesystem::directory_iterator(folder)) + { + if (til::ends_with(p.path().native(), L".ttf")) + { + wil::com_ptr fontFile; + if (SUCCEEDED_LOG(factory5->CreateFontFileReference(p.path().c_str(), nullptr, fontFile.addressof()))) + { + files.emplace_back(std::move(fontFile)); + } + } + } + + files.shrink_to_fit(); + return files; + }(); + return fontFiles; + } + + inline wil::com_ptr getFontCollection(bool forceUpdate) + { + wil::com_ptr factory; + THROW_IF_FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(factory), reinterpret_cast<::IUnknown**>(factory.addressof()))); + + wil::com_ptr systemFontCollection; + THROW_IF_FAILED(factory->GetSystemFontCollection(systemFontCollection.addressof(), forceUpdate)); + + if constexpr (Feature_NearbyFontLoading::IsEnabled()) + { + // IDWriteFactory5 is supported since Windows 10, build 15021. + const auto factory5 = factory.try_query(); + if (!factory5) + { + return systemFontCollection; + } + + const auto& nearbyFontFiles = getNearbyFontFiles(factory5.get()); + if (nearbyFontFiles.empty()) + { + return systemFontCollection; + } + + wil::com_ptr systemFontSet; + // IDWriteFontCollection1 is supported since Windows 7. + THROW_IF_FAILED(systemFontCollection.query()->GetFontSet(systemFontSet.addressof())); + + wil::com_ptr fontSetBuilder; + THROW_IF_FAILED(factory5->CreateFontSetBuilder(fontSetBuilder.addressof())); + THROW_IF_FAILED(fontSetBuilder->AddFontSet(systemFontSet.get())); + + for (const auto& file : nearbyFontFiles) + { + LOG_IF_FAILED(fontSetBuilder->AddFontFile(file.get())); + } + + wil::com_ptr fontSet; + THROW_IF_FAILED(fontSetBuilder->CreateFontSet(fontSet.addressof())); + + wil::com_ptr fontCollection; + THROW_IF_FAILED(factory5->CreateFontCollectionFromFontSet(fontSet.get(), fontCollection.addressof())); + + return std::move(fontCollection); + } + else + { + return systemFontCollection; + } + } + } + + inline wil::com_ptr GetCached() + { + return details::getFontCollection(false); + } + + inline wil::com_ptr GetFresh() + { + return details::getFontCollection(true); + } +} diff --git a/src/renderer/base/lib/base.vcxproj b/src/renderer/base/lib/base.vcxproj index 1bcfe8dddb0..5d7bc816c41 100644 --- a/src/renderer/base/lib/base.vcxproj +++ b/src/renderer/base/lib/base.vcxproj @@ -34,6 +34,7 @@ + diff --git a/src/renderer/base/lib/base.vcxproj.filters b/src/renderer/base/lib/base.vcxproj.filters index bda75e18fe8..4e8f12c1821 100644 --- a/src/renderer/base/lib/base.vcxproj.filters +++ b/src/renderer/base/lib/base.vcxproj.filters @@ -89,6 +89,9 @@ Header Files\inc + + Header Files + diff --git a/src/renderer/dx/DxFontInfo.cpp b/src/renderer/dx/DxFontInfo.cpp index e5c6749f4be..ba3eca1a864 100644 --- a/src/renderer/dx/DxFontInfo.cpp +++ b/src/renderer/dx/DxFontInfo.cpp @@ -2,11 +2,9 @@ // Licensed under the MIT license. #include "precomp.h" - #include "DxFontInfo.h" -#include "unicode.hpp" - +#include #include static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" }; @@ -14,7 +12,6 @@ static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Luci using namespace Microsoft::Console::Render; DxFontInfo::DxFontInfo() noexcept : - _familyName(), _weight(DWRITE_FONT_WEIGHT_NORMAL), _style(DWRITE_FONT_STYLE_NORMAL), _stretch(DWRITE_FONT_STRETCH_NORMAL), @@ -96,11 +93,6 @@ bool DxFontInfo::GetFallback() const noexcept return _didFallback; } -IDWriteFontCollection* DxFontInfo::GetNearbyCollection() const noexcept -{ - return _nearbyCollection.Get(); -} - void DxFontInfo::SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, @@ -122,8 +114,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // - localeName - Locale to search for appropriate fonts // Return Value: // - Smart pointer holding interface reference for queryable font data. -[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::ResolveFontFaceWithFallback(gsl::not_null dwriteFactory, - std::wstring& localeName) +[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::ResolveFontFaceWithFallback(IDWriteFontCollection* fontCollection, std::wstring& localeName) { // First attempt to find exactly what the user asked for. _didFallback = false; @@ -134,7 +125,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // method. We still want to fall back to a font that's reasonable, below. try { - face = _FindFontFace(dwriteFactory, localeName, true); + face = _FindFontFace(fontCollection, localeName); if (!face) { @@ -161,7 +152,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, _familyName = _familyName.substr(0, lastSpace); // Try to find it with the shortened family name - face = _FindFontFace(dwriteFactory, localeName, true); + face = _FindFontFace(fontCollection, localeName); } } } @@ -175,25 +166,8 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, for (const auto fallbackFace : FALLBACK_FONT_FACES) { _familyName = fallbackFace; - // With these fonts, don't attempt the nearby lookup. We're looking - // for system fonts only. If one of the nearby fonts is causing us - // problems (like in GH#10211), then we don't want to go anywhere - - // near it in this part. - face = _FindFontFace(dwriteFactory, localeName, false); - - if (face) - { - _didFallback = true; - break; - } - - _familyName = fallbackFace; - _weight = DWRITE_FONT_WEIGHT_NORMAL; - _stretch = DWRITE_FONT_STRETCH_NORMAL; - _style = DWRITE_FONT_STYLE_NORMAL; - face = _FindFontFace(dwriteFactory, localeName, false); + face = _FindFontFace(fontCollection, localeName); if (face) { _didFallback = true; @@ -214,27 +188,15 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // - localeName - Locale to search for appropriate fonts // Return Value: // - Smart pointer holding interface reference for queryable font data. -[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::_FindFontFace(gsl::not_null dwriteFactory, std::wstring& localeName, const bool withNearbyLookup) +#pragma warning(suppress : 26429) // C26429: Symbol 'fontCollection' is never tested for nullness, it can be marked as not_null (f.23). +[[nodiscard]] Microsoft::WRL::ComPtr DxFontInfo::_FindFontFace(IDWriteFontCollection* fontCollection, std::wstring& localeName) { Microsoft::WRL::ComPtr fontFace; - Microsoft::WRL::ComPtr fontCollection; - THROW_IF_FAILED(dwriteFactory->GetSystemFontCollection(&fontCollection, false)); - UINT32 familyIndex; BOOL familyExists; - THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); - // If the system collection missed, try the files sitting next to our binary. - if (withNearbyLookup && !familyExists) - { - // May be null on OS below Windows 10. If null, just skip the attempt. - if (const auto nearbyCollection = _NearbyCollection(dwriteFactory)) - { - THROW_IF_FAILED(nearbyCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); - fontCollection = nearbyCollection; - } - } + THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); if (familyExists) { @@ -327,81 +289,3 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, // and return it. return retVal; } - -// Routine Description: -// - Creates a DirectWrite font collection of font files that are sitting next to the running -// binary (in the same directory as the EXE). -// Arguments: -// - dwriteFactory - The DWrite factory to use -// Return Value: -// - DirectWrite font collection. May be null if one cannot be created. -[[nodiscard]] IDWriteFontCollection* DxFontInfo::_NearbyCollection(gsl::not_null dwriteFactory) -{ - if (_nearbyCollection) - { - return _nearbyCollection.Get(); - } - - // The convenience interfaces for loading fonts from files - // are only available on Windows 10+. - ::Microsoft::WRL::ComPtr factory6; - if (FAILED(dwriteFactory->QueryInterface(&factory6))) - { - return nullptr; - } - - ::Microsoft::WRL::ComPtr systemFontCollection; - THROW_IF_FAILED(factory6->GetSystemFontCollection(false, &systemFontCollection, 0)); - - ::Microsoft::WRL::ComPtr systemFontSet; - THROW_IF_FAILED(systemFontCollection->GetFontSet(&systemFontSet)); - - ::Microsoft::WRL::ComPtr fontSetBuilder2; - THROW_IF_FAILED(factory6->CreateFontSetBuilder(&fontSetBuilder2)); - - THROW_IF_FAILED(fontSetBuilder2->AddFontSet(systemFontSet.Get())); - - // Magic static so we only attempt to grovel the hard disk once no matter how many instances - // of the font collection itself we require. - static const auto knownPaths = s_GetNearbyFonts(); - for (auto& p : knownPaths) - { - fontSetBuilder2->AddFontFile(p.c_str()); - } - - ::Microsoft::WRL::ComPtr fontSet; - THROW_IF_FAILED(fontSetBuilder2->CreateFontSet(&fontSet)); - - ::Microsoft::WRL::ComPtr fontCollection; - THROW_IF_FAILED(factory6->CreateFontCollectionFromFontSet(fontSet.Get(), &fontCollection)); - - _nearbyCollection = fontCollection; - return _nearbyCollection.Get(); -} - -// Routine Description: -// - Digs through the directory that the current executable is running within to find -// any TTF files sitting next to it. -// Arguments: -// - -// Return Value: -// - Iterable collection of filesystem paths, one per font file that was found -[[nodiscard]] std::vector DxFontInfo::s_GetNearbyFonts() -{ - std::vector paths; - - // Find the directory we're running from then enumerate all the TTF files - // sitting next to us. - const std::filesystem::path module{ wil::GetModuleFileNameW(nullptr) }; - const auto folder{ module.parent_path() }; - - for (const auto& p : std::filesystem::directory_iterator(folder)) - { - if (til::ends_with(p.path().native(), L".ttf")) - { - paths.push_back(p.path()); - } - } - - return paths; -} diff --git a/src/renderer/dx/DxFontInfo.h b/src/renderer/dx/DxFontInfo.h index 9b336accd0c..0e8bbf837c2 100644 --- a/src/renderer/dx/DxFontInfo.h +++ b/src/renderer/dx/DxFontInfo.h @@ -41,30 +41,19 @@ namespace Microsoft::Console::Render bool GetFallback() const noexcept; - IDWriteFontCollection* GetNearbyCollection() const noexcept; - void SetFromEngine(const std::wstring_view familyName, const DWRITE_FONT_WEIGHT weight, const DWRITE_FONT_STYLE style, const DWRITE_FONT_STRETCH stretch); - [[nodiscard]] ::Microsoft::WRL::ComPtr ResolveFontFaceWithFallback(gsl::not_null dwriteFactory, - std::wstring& localeName); + [[nodiscard]] ::Microsoft::WRL::ComPtr ResolveFontFaceWithFallback(IDWriteFontCollection* fontCollection, std::wstring& localeName); private: - [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(gsl::not_null dwriteFactory, - std::wstring& localeName, - const bool withNearbyLookup); + [[nodiscard]] ::Microsoft::WRL::ComPtr _FindFontFace(IDWriteFontCollection* fontCollection, std::wstring& localeName); [[nodiscard]] std::wstring _GetFontFamilyName(gsl::not_null const fontFamily, std::wstring& localeName); - [[nodiscard]] IDWriteFontCollection* _NearbyCollection(gsl::not_null dwriteFactory); - - [[nodiscard]] static std::vector s_GetNearbyFonts(); - - ::Microsoft::WRL::ComPtr _nearbyCollection; - // The font name we should be looking for std::wstring _familyName; diff --git a/src/renderer/dx/DxFontRenderData.cpp b/src/renderer/dx/DxFontRenderData.cpp index c251b7bf0c6..869a1d4fc45 100644 --- a/src/renderer/dx/DxFontRenderData.cpp +++ b/src/renderer/dx/DxFontRenderData.cpp @@ -2,22 +2,21 @@ // Licensed under the MIT license. #include "precomp.h" - #include "DxFontRenderData.h" -#include "unicode.hpp" - #include +#include "../base/FontCache.h" + static constexpr float POINTS_PER_INCH = 72.0f; -static constexpr std::wstring_view FALLBACK_FONT_FACES[] = { L"Consolas", L"Lucida Console", L"Courier New" }; static constexpr std::wstring_view FALLBACK_LOCALE = L"en-us"; static constexpr size_t TAG_LENGTH = 4; using namespace Microsoft::Console::Render; -DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) noexcept : +DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) : _dwriteFactory(dwriteFactory), + _nearbyCollection{ FontCache::GetCached() }, _fontSize{}, _glyphCell{}, _lineMetrics{}, @@ -165,7 +164,7 @@ DxFontRenderData::DxFontRenderData(::Microsoft::WRL::ComPtr dwr fontInfo.SetStretch(stretch); std::wstring fontLocaleName = UserLocaleName(); - Microsoft::WRL::ComPtr fontFace = fontInfo.ResolveFontFaceWithFallback(_dwriteFactory.Get(), fontLocaleName); + Microsoft::WRL::ComPtr fontFace = fontInfo.ResolveFontFaceWithFallback(_nearbyCollection.get(), fontLocaleName); _fontFaceMap.emplace(_ToMapKey(weight, style, stretch), fontFace); return fontFace; @@ -711,7 +710,7 @@ void DxFontRenderData::_BuildFontRenderData(const FontInfoDesired& desired, Font // This is the first attempt to resolve font face after `UpdateFont`. // Note that the following line may cause property changes _inside_ `_defaultFontInfo` because the desired font may not exist. // See the implementation of `ResolveFontFaceWithFallback` for details. - const Microsoft::WRL::ComPtr face = _defaultFontInfo.ResolveFontFaceWithFallback(_dwriteFactory.Get(), fontLocaleName); + const Microsoft::WRL::ComPtr face = _defaultFontInfo.ResolveFontFaceWithFallback(_nearbyCollection.get(), fontLocaleName); DWRITE_FONT_METRICS1 fontMetrics; face->GetMetrics(&fontMetrics); @@ -898,7 +897,7 @@ Microsoft::WRL::ComPtr DxFontRenderData::_BuildTextFormat(con { Microsoft::WRL::ComPtr format; THROW_IF_FAILED(_dwriteFactory->CreateTextFormat(fontInfo.GetFamilyName().data(), - fontInfo.GetNearbyCollection(), + _nearbyCollection.get(), fontInfo.GetWeight(), fontInfo.GetStyle(), fontInfo.GetStretch(), diff --git a/src/renderer/dx/DxFontRenderData.h b/src/renderer/dx/DxFontRenderData.h index ce69169c53a..b2a0e7053bd 100644 --- a/src/renderer/dx/DxFontRenderData.h +++ b/src/renderer/dx/DxFontRenderData.h @@ -39,7 +39,7 @@ namespace Microsoft::Console::Render float strikethroughWidth; }; - DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory) noexcept; + DxFontRenderData(::Microsoft::WRL::ComPtr dwriteFactory); // DirectWrite text analyzer from the factory [[nodiscard]] Microsoft::WRL::ComPtr Analyzer(); @@ -132,6 +132,7 @@ namespace Microsoft::Console::Render ::Microsoft::WRL::ComPtr _dwriteFactory; ::Microsoft::WRL::ComPtr _dwriteTextAnalyzer; + wil::com_ptr _nearbyCollection; std::wstring _userLocaleName; DxFontInfo _defaultFontInfo; til::size _glyphCell; diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 68ba9ff684e..18b607fb70c 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -2083,10 +2083,12 @@ float DxEngine::GetScaling() const noexcept [[nodiscard]] HRESULT DxEngine::GetProposedFont(const FontInfoDesired& pfiFontInfoDesired, FontInfo& pfiFontInfo, int const iDpi) noexcept +try { DxFontRenderData fontRenderData(_dwriteFactory); return fontRenderData.UpdateFont(pfiFontInfoDesired, pfiFontInfo, iDpi); } +CATCH_RETURN(); // Routine Description: // - Gets the area that we currently believe is dirty within the character cell grid From 2e78ebfdc7626509242475b710640b3ca2f6ca60 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 10 Mar 2022 08:16:10 -0600 Subject: [PATCH 27/75] Fix showing a dialog multiple times (#12625) After the dialog is displayed, always clear it out. If we don't, we won't be able to display another! * regressed in #12517. * [x] Fixes #12622. --- src/cascadia/TerminalApp/AppLogic.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 71698f7c4c2..0d8cb34fa2f 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -348,7 +348,11 @@ namespace winrt::TerminalApp::implementation } _dialog = dialog; - + // GH#12622: After the dialog is displayed, always clear it out. If we + // don't, we won't be able to display another! + const auto cleanup = wil::scope_exit([this]() { + _dialog = nullptr; + }); // 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. From 78ec74a723d568604e41e456c435368b6fed0747 Mon Sep 17 00:00:00 2001 From: Sowmya A <52818452+sowmya-hub@users.noreply.github.com> Date: Fri, 11 Mar 2022 00:31:29 +0530 Subject: [PATCH 28/75] Remove trailing commas that break the json schema (#12644) Fixes #12639 --- doc/cascadia/profiles.schema.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index b7abd93b661..3a401661150 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -485,7 +485,7 @@ "type": "boolean", "default": false, "description": "This will override the profile's `elevate` setting." - }, + } }, "type": "object" }, @@ -861,7 +861,7 @@ } } } - ], + ] }, "SetFullScreenAction": { "description": "Arguments for a setFullScreen action", @@ -881,7 +881,7 @@ } } } - ], + ] }, "SetMaximizedAction": { "description": "Arguments for a setMaximized action", @@ -901,7 +901,7 @@ } } } - ], + ] }, "WtAction": { "description": "Arguments corresponding to a wt Action", @@ -2379,4 +2379,4 @@ ] } ] -} +} \ No newline at end of file From 460a991334eadbe4e6176a3da1e54f7abc838210 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 10 Mar 2022 16:56:39 -0600 Subject: [PATCH 29/75] Add some disclaimer text to the schemes page (#12663) People get confused about this. This should help. It doesn't really fix it, but it should help. * [x] Does enough for #9775 to get it out of 1.14 * [x] I work here * [x] Screenshot below. ![image](https://user-images.githubusercontent.com/18356694/157732913-86f0af51-8c37-4827-9d21-5775d0bfdeb7.png) * [ ] todo: Discuss the text here. @cinnamon-msft this sound good? --- src/cascadia/TerminalSettingsEditor/ColorSchemes.xaml | 4 ++++ .../TerminalSettingsEditor/Resources/en-US/Resources.resw | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/cascadia/TerminalSettingsEditor/ColorSchemes.xaml b/src/cascadia/TerminalSettingsEditor/ColorSchemes.xaml index d4237e364cc..3515a213745 100644 --- a/src/cascadia/TerminalSettingsEditor/ColorSchemes.xaml +++ b/src/cascadia/TerminalSettingsEditor/ColorSchemes.xaml @@ -98,6 +98,10 @@ Margin="{StaticResource StandardIndentMargin}" Spacing="24"> + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index a069c7de6ec..3a22ea0bcf4 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -1210,6 +1210,10 @@ Cancel rename Text label for a button that can be used to cancel a rename operation during the renaming process. + + Schemes defined here can be applied to your profiles under the "Appearances" section of the profile settings pages. + A disclaimer presented at the top of a page. "Appearances" should match Profile_Appearance.Header. + Settings defined here will apply to all profiles unless they are overridden by a profile's settings. A disclaimer presented at the top of a page. See "Nav_ProfileDefaults.Content" for a description on what the defaults layer does in the app. From b55f3ab24b2d1764aadf0cb6308fe623d86059bf Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 10 Mar 2022 17:16:02 -0600 Subject: [PATCH 30/75] Round our Maximize button to match ControlsV2 styles (#12660) Just look at the screenshot. Above is before, below is now. ![image](https://user-images.githubusercontent.com/18356694/157717931-f3e3167e-0234-425a-a8eb-02303f386dc6.png) These paths were taken straight from WinUI versions of these icons, thanks @pratikone for the alley oop. * [x] Closes #12433 * [x] Tested manually by _lookin at it_ --- src/cascadia/TerminalApp/MinMaxCloseControl.xaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalApp/MinMaxCloseControl.xaml b/src/cascadia/TerminalApp/MinMaxCloseControl.xaml index 684831586bc..cbfe7d9928e 100644 --- a/src/cascadia/TerminalApp/MinMaxCloseControl.xaml +++ b/src/cascadia/TerminalApp/MinMaxCloseControl.xaml @@ -247,9 +247,10 @@ Click="_MaximizeClick" Style="{StaticResource CaptionButton}"> + - M 0 0 H 10 V 10 H 0 V 0 - M 0 2 h 8 v 8 h -8 v -8 M 2 2 v -2 h 8 v 8 h -2 + M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001 + M 1.516 -0.001 L 7.451 0.009 C 8.751 0.019 9 1 8.981 1.477 L 9.002 7.558 M 11 6 L 11 2 C 11 0 10 -2 8.011 -1.946 L 7.06 -1.969 L 3 -2 M 9.002 7.547 C 8.929 8.669 8 9 7.43 9.015 L 1.464 9.005 C 0.374 8.973 0 8 -0.004 7.484 L -0.004 1.477 C 0 1 0.415 0.009 1.527 -0.001 From 814386f2c38e89ade70a3c7b2cf93b0bbc155c10 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 10 Mar 2022 18:30:24 -0500 Subject: [PATCH 31/75] Revert "Make sure Terminal Stable shows up default on 22544+" (#12664) This reverts commit 457738e388a60e3d2d62736e006e4e407d5e81e8. --- src/internal/stubs.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/internal/stubs.cpp b/src/internal/stubs.cpp index 5a047da742e..da6423d14ec 100644 --- a/src/internal/stubs.cpp +++ b/src/internal/stubs.cpp @@ -40,16 +40,9 @@ void EdpPolicy::AuditClipboard(const std::wstring_view /*destinationName*/) noex [[nodiscard]] HRESULT DefaultApp::CheckShouldTerminalBeDefault(bool& isEnabled) noexcept { - // Since we toggled this feature on in Windows, Terminal (and Terminal Preview) need to - // agree -- otherwise, they will present UI that suggests Terminal is NOT the default, - // like the info bar. - OSVERSIONINFOEXW osver{}; - osver.dwOSVersionInfoSize = sizeof(osver); - osver.dwBuildNumber = 22544; - - DWORDLONG dwlConditionMask = 0; - VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL); - - isEnabled = VerifyVersionInfoW(&osver, VER_BUILDNUMBER, dwlConditionMask) != FALSE; + // False since setting Terminal as the default app is an OS feature and probably + // should not be done in the open source conhost. We can always decide to turn it + // on in the future though. + isEnabled = false; return S_OK; } From c481dd4d1087a01a9288abb880f0f4076d6a2814 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Thu, 10 Mar 2022 23:45:27 +0000 Subject: [PATCH 32/75] Merged PR 7045816: clean up outdated dirs files Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev cba7e7f88987f549c6c6d47649e77a6b8d4cb874 --- src/dirs | 1 - src/terminal/input/dirs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/dirs b/src/dirs index f8132613980..3b6880002ae 100644 --- a/src/dirs +++ b/src/dirs @@ -2,7 +2,6 @@ DIRS=\ staging \ buffer \ interactivity \ - dep \ host \ propsheet \ propslib \ diff --git a/src/terminal/input/dirs b/src/terminal/input/dirs index dc14638bf5f..a723a0cec35 100644 --- a/src/terminal/input/dirs +++ b/src/terminal/input/dirs @@ -1,2 +1 @@ DIRS=lib \ - ut_adapter \ From 472d462f69d11aecf030536af3729f4675c10a19 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 10 Mar 2022 13:27:19 +0100 Subject: [PATCH 33/75] Fix activation context generation for conhost (#12653) This regressed in 2b202ce6, which removed `ACTCTX_FLAG_RESOURCE_NAME_VALID` during `CreateActCtxW` under the assumption that an executable only has one manifest. conhost has two however and we need to pick the correct one. On OpenConsole this causes the expected `ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET`. Closes MSFT:38355907 (cherry picked from commit c820af46b7c2e1c1362dd7a5eac4500d345bede0) --- src/host/init.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/host/init.cpp b/src/host/init.cpp index 8ab26062fe5..76c4a8b050a 100644 --- a/src/host/init.cpp +++ b/src/host/init.cpp @@ -32,7 +32,8 @@ void InitSideBySide() actctx.cbSize = sizeof(actctx); // We set ACTCTX_FLAG_HMODULE_VALID, but leave hModule as nullptr. // A nullptr HMODULE refers to the current process/executable. - actctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_HMODULE_VALID; + actctx.lpResourceName = MAKEINTRESOURCE(IDR_SYSTEM_MANIFEST); + actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_SET_PROCESS_DEFAULT | ACTCTX_FLAG_HMODULE_VALID; HANDLE const hActCtx = CreateActCtxW(&actctx); @@ -44,8 +45,9 @@ void InitSideBySide() { auto const error = GetLastError(); - // Don't log if it's already set. This whole ordeal is to make sure one is set if there isn't one already. - // If one is already set... good! + // OpenConsole ships with a single manifest at ID 1, while conhost ships with 2 at ID 1 + // and IDR_SYSTEM_MANIFEST. If we call CreateActCtxW() with IDR_SYSTEM_MANIFEST inside + // OpenConsole anyways, nothing happens and we get ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET. if (ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET != error) { RIPMSG1(RIP_WARNING, "InitSideBySide failed create an activation context. Error: %d\r\n", error); From cfaa315de7078e356a3861b2184bc1584749e71f Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Mar 2022 00:47:13 +0100 Subject: [PATCH 34/75] Fix compilation under and enable C++20 (#12658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit enables `/std:c++20` for local development under VS17. Our CIs will continue to use VS16 and C++17 for now in order to reduce the likelihood of regressions during the current development cycle. It's expected that we'll migrate to VS17 soon, as this is what conhost is already being built with anyways. ## PR Checklist * [x] Closes #12510 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Everything compiles under `/std:c++20` ✅ --- dep/CLI11/CLI11.hpp | 176 +++++++++++------- dep/CLI11/README.md | 5 +- .../ColorSchemeTests.cpp | 2 +- .../TerminalSettingsTests.cpp | 8 +- src/cascadia/TerminalApp/AppLogic.cpp | 2 +- src/cascadia/TerminalApp/TabBase.cpp | 2 +- src/cascadia/TerminalApp/TerminalTab.cpp | 4 +- src/cascadia/TerminalControl/TermControl.cpp | 2 +- .../TerminalSettingsModel/FileUtils.cpp | 2 +- src/common.build.pre.props | 3 +- src/host/ft_host/InitTests.cpp | 2 +- src/inc/til/bit.h | 4 +- src/inc/til/enumset.h | 15 +- .../parser/ft_fuzzer/fuzzing_directed.h | 36 ++-- 14 files changed, 151 insertions(+), 112 deletions(-) diff --git a/dep/CLI11/CLI11.hpp b/dep/CLI11/CLI11.hpp index a7efa607c92..9e502bf960e 100644 --- a/dep/CLI11/CLI11.hpp +++ b/dep/CLI11/CLI11.hpp @@ -1,11 +1,11 @@ #pragma once -// CLI11: Version 1.9.0 +// CLI11: Version 1.9.1 // Originally designed by Henry Schreiner // https://github.com/CLIUtils/CLI11 // // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts -// from: v1.9.0 +// from: v1.9.1 // // From LICENSE: // @@ -60,14 +60,14 @@ #include #include -// Verbatim copy from CLI/Version.hpp: +// Verbatim copy from Version.hpp: #define CLI11_VERSION_MAJOR 1 #define CLI11_VERSION_MINOR 9 -#define CLI11_VERSION_PATCH 0 -#define CLI11_VERSION "1.9.0" +#define CLI11_VERSION_PATCH 1 +#define CLI11_VERSION "1.9.1" -// Verbatim copy from CLI/Macros.hpp: +// Verbatim copy from Macros.hpp: // The following version macro is very similar to the one in PyBind11 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) @@ -102,7 +102,7 @@ #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason))) #endif -// Verbatim copy from CLI/Validators.hpp: +// Verbatim copy from Validators.hpp: // C standard library // Only needed for existence checking @@ -114,7 +114,14 @@ #else #include #if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703 +#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9 #define CLI11_HAS_FILESYSTEM 1 +#elif defined(__GLIBCXX__) +// if we are using gcc and Version <9 default to no filesystem +#define CLI11_HAS_FILESYSTEM 0 +#else +#define CLI11_HAS_FILESYSTEM 1 +#endif #else #define CLI11_HAS_FILESYSTEM 0 #endif @@ -129,11 +136,11 @@ #include #endif -// From CLI/Version.hpp: +// From Version.hpp: -// From CLI/Macros.hpp: +// From Macros.hpp: -// From CLI/StringTools.hpp: +// From StringTools.hpp: namespace CLI { @@ -570,7 +577,7 @@ namespace CLI } // namespace CLI -// From CLI/Error.hpp: +// From Error.hpp: namespace CLI { @@ -844,11 +851,11 @@ public: { CLI11_ERROR_DEF(ParseError, ArgumentMismatch) CLI11_ERROR_SIMPLE(ArgumentMismatch) - ArgumentMismatch(std::string name, int expected, std::size_t recieved) : + ArgumentMismatch(std::string name, int expected, std::size_t received) : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + - ", got " + std::to_string(recieved)) : + ", got " + std::to_string(received)) : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + - ", got " + std::to_string(recieved)), + ", got " + std::to_string(received)), ExitCodes::ArgumentMismatch) {} static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) @@ -949,7 +956,7 @@ public: } // namespace CLI -// From CLI/TypeTools.hpp: +// From TypeTools.hpp: namespace CLI { @@ -1237,16 +1244,26 @@ namespace CLI }; /// Convert an object to a string (directly forward if this can become a string) - template::value, detail::enabler> = detail::dummy> + template::value, detail::enabler> = detail::dummy> auto to_string(T&& value) -> decltype(std::forward(value)) { return std::forward(value); } + /// Construct a string from the object + template::value && !std::is_convertible::value, + detail::enabler> = detail::dummy> + std::string to_string(const T& value) + { + return std::string(value); + } + /// Convert an object to a string (streaming must be supported for that type) template::value && is_ostreamable::value, detail::enabler> = - detail::dummy> + enable_if_t::value && !std::is_constructible::value && + is_ostreamable::value, + detail::enabler> = detail::dummy> std::string to_string(T&& value) { std::stringstream stream; @@ -1624,7 +1641,7 @@ namespace CLI // Lexical cast /// Convert a flag into an integer value typically binary flags - inline int64_t to_flag_value(std::string val) + inline std::int64_t to_flag_value(std::string val) { static const std::string trueString("true"); static const std::string falseString("false"); @@ -1637,12 +1654,12 @@ namespace CLI return -1; } val = detail::to_lower(val); - int64_t ret; + std::int64_t ret; if (val.size() == 1) { if (val[0] >= '1' && val[0] <= '9') { - return (static_cast(val[0]) - '0'); + return (static_cast(val[0]) - '0'); } switch (val[0]) { @@ -2127,7 +2144,7 @@ namespace CLI enable_if_t::value && std::is_unsigned::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector& flags, T& output) { - int64_t count{ 0 }; + std::int64_t count{ 0 }; for (auto& flag : flags) { count += detail::to_flag_value(flag); @@ -2144,7 +2161,7 @@ namespace CLI enable_if_t::value && std::is_signed::value, detail::enabler> = detail::dummy> void sum_flag_vector(const std::vector& flags, T& output) { - int64_t count{ 0 }; + std::int64_t count{ 0 }; for (auto& flag : flags) { count += detail::to_flag_value(flag); @@ -2155,7 +2172,7 @@ namespace CLI } // namespace detail } // namespace CLI -// From CLI/Split.hpp: +// From Split.hpp: namespace CLI { @@ -2307,7 +2324,7 @@ namespace CLI } // namespace detail } // namespace CLI -// From CLI/ConfigFwd.hpp: +// From ConfigFwd.hpp: namespace CLI { @@ -2436,7 +2453,7 @@ namespace CLI }; } // namespace CLI -// From CLI/Validators.hpp: +// From Validators.hpp: namespace CLI { @@ -2504,7 +2521,7 @@ namespace CLI } } return retstring; - }; + } /// This is the required operator for a Validator - provided to help /// users (CLI11 uses the member `func` directly) @@ -2512,7 +2529,7 @@ namespace CLI { std::string value = str; return (active_) ? func_(value) : std::string{}; - }; + } /// Specify the type string Validator& description(std::string validator_desc) @@ -2576,14 +2593,14 @@ namespace CLI { application_index_ = app_index; return *this; - }; + } /// Specify the application index of a validator Validator application_index(int app_index) const { Validator newval(*this); newval.application_index_ = app_index; return newval; - }; + } /// Get the current value of the application index int get_application_index() const { return application_index_; } /// Get a boolean if the validator is active @@ -2699,7 +2716,7 @@ namespace CLI /// CLI enumeration of different file types enum class path_type { - nonexistant, + nonexistent, file, directory }; @@ -2712,13 +2729,13 @@ namespace CLI auto stat = std::filesystem::status(file, ec); if (ec) { - return path_type::nonexistant; + return path_type::nonexistent; } switch (stat.type()) { case std::filesystem::file_type::none: case std::filesystem::file_type::not_found: - return path_type::nonexistant; + return path_type::nonexistent; case std::filesystem::file_type::directory: return path_type::directory; case std::filesystem::file_type::symlink: @@ -2749,7 +2766,7 @@ namespace CLI return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; } #endif - return path_type::nonexistant; + return path_type::nonexistent; } #endif /// Check for an existing file (returns error message if check fails) @@ -2761,7 +2778,7 @@ namespace CLI { func_ = [](std::string& filename) { auto path_result = check_path(filename.c_str()); - if (path_result == path_type::nonexistant) + if (path_result == path_type::nonexistent) { return "File does not exist: " + filename; } @@ -2783,7 +2800,7 @@ namespace CLI { func_ = [](std::string& filename) { auto path_result = check_path(filename.c_str()); - if (path_result == path_type::nonexistant) + if (path_result == path_type::nonexistent) { return "Directory does not exist: " + filename; } @@ -2805,7 +2822,7 @@ namespace CLI { func_ = [](std::string& filename) { auto path_result = check_path(filename.c_str()); - if (path_result == path_type::nonexistant) + if (path_result == path_type::nonexistent) { return "Path does not exist: " + filename; } @@ -2823,7 +2840,7 @@ namespace CLI { func_ = [](std::string& filename) { auto path_result = check_path(filename.c_str()); - if (path_result != path_type::nonexistant) + if (path_result != path_type::nonexistent) { return "Path already exists: " + filename; } @@ -3310,7 +3327,7 @@ namespace CLI // if the type does not have first_type and second_type, these are both value_type using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map - using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones // (const char * to std::string) // Make a local copy of the filter function, using a std::function if not one already @@ -3381,10 +3398,9 @@ namespace CLI // if the type does not have first_type and second_type, these are both value_type using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map - using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones + using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones // (const char * to std::string) - using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair // - // the type of the object pair + using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair // Make a local copy of the filter function, using a std::function if not one already std::function filter_fn = filter_function; @@ -3625,7 +3641,7 @@ namespace CLI class AsSizeValue : public AsNumberWithUnit { public: - using result_t = uint64_t; + using result_t = std::uint64_t; /// If kb_is_1000 is true, /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 @@ -3721,7 +3737,7 @@ namespace CLI } // namespace CLI -// From CLI/FormatterFwd.hpp: +// From FormatterFwd.hpp: namespace CLI { @@ -3735,9 +3751,9 @@ namespace CLI enum class AppFormatMode { - Normal, //< The normal, detailed help - All, //< A fully expanded help - Sub, //< Used when printed as part of expanded subcommand + Normal, ///< The normal, detailed help + All, ///< A fully expanded help + Sub, ///< Used when printed as part of expanded subcommand }; /// This is the minimum requirements to run a formatter. @@ -3897,7 +3913,7 @@ namespace CLI } // namespace CLI -// From CLI/Option.hpp: +// From Option.hpp: namespace CLI { @@ -4441,7 +4457,7 @@ namespace CLI template Option* needs(std::string opt_name) { - auto opt = dynamic_cast(parent_)->get_option_no_throw(opt_name); + auto opt = static_cast(parent_)->get_option_no_throw(opt_name); if (opt == nullptr) { throw IncorrectConstruction::MissingOption(opt_name); @@ -4492,7 +4508,7 @@ namespace CLI template Option* excludes(std::string opt_name) { - auto opt = dynamic_cast(parent_)->get_option_no_throw(opt_name); + auto opt = static_cast(parent_)->get_option_no_throw(opt_name); if (opt == nullptr) { throw IncorrectConstruction::MissingOption(opt_name); @@ -4538,7 +4554,7 @@ namespace CLI if (!ignore_case_ && value) { ignore_case_ = value; - auto* parent = dynamic_cast(parent_); + auto* parent = static_cast(parent_); for (const Option_p& opt : parent->options_) { if (opt.get() == this) @@ -4570,7 +4586,7 @@ namespace CLI if (!ignore_underscore_ && value) { ignore_underscore_ = value; - auto* parent = dynamic_cast(parent_); + auto* parent = static_cast(parent_); for (const Option_p& opt : parent->options_) { if (opt.get() == this) @@ -4698,9 +4714,9 @@ namespace CLI /// Will include / prefer the positional name if positional is true. /// If all_options is false, pick just the most descriptive name to show. /// Use `get_name(true)` to get the positional name (replaces `get_pname`) - std::string get_name(bool positional = false, //<[input] Show the positional name - bool all_options = false //<[input] Show every option - ) const + std::string get_name(bool positional = false, ///< Show the positional name + bool all_options = false ///< Show every option + ) const { if (get_group().empty()) return {}; // Hidden @@ -5000,7 +5016,7 @@ namespace CLI { if (!default_str_.empty()) { - //_add_results takes an rvalue only + // _add_results takes an rvalue only _add_result(std::string(default_str_), res); _validate_results(res); results_t extra; @@ -5379,7 +5395,7 @@ namespace CLI } // namespace CLI -// From CLI/App.hpp: +// From App.hpp: namespace CLI { @@ -6221,8 +6237,9 @@ namespace CLI } /// Vector version to capture multiple flags. - template, T>::value, detail::enabler> = detail::dummy> + template< + typename T, + enable_if_t, T>::value, detail::enabler> = detail::dummy> Option* add_flag(std::string flag_name, std::vector& flag_results, ///< A vector of values with the flag results std::string flag_description = "") @@ -6260,11 +6277,11 @@ namespace CLI /// Add option for callback with an integer value Option* add_flag_function(std::string flag_name, - std::function function, ///< A function to call, void(int) + std::function function, ///< A function to call, void(int) std::string flag_description = "") { CLI::callback_t fun = [function](const CLI::results_t& res) { - int64_t flag_count = 0; + std::int64_t flag_count = 0; detail::sum_flag_vector(res, flag_count); function(flag_count); return true; @@ -6276,7 +6293,7 @@ namespace CLI #ifdef CLI11_CPP14 /// Add option for callback (C++14 or better only) Option* add_flag(std::string flag_name, - std::function function, ///< A function to call, void(int64_t) + std::function function, ///< A function to call, void(std::int64_t) std::string flag_description = "") { return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description)); @@ -6452,7 +6469,7 @@ namespace CLI template T* add_option_group(std::string group_name, std::string group_description = "") { - auto option_group = std::make_shared(std::move(group_description), group_name, nullptr); + auto option_group = std::make_shared(std::move(group_description), group_name, this); auto ptr = option_group.get(); // move to App_p for overload resolution on older gcc versions App_p app_ptr = std::dynamic_pointer_cast(option_group); @@ -6461,7 +6478,7 @@ namespace CLI } ///@} - /// @name Subcommmands + /// @name Subcommands ///@{ /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag @@ -6837,16 +6854,16 @@ namespace CLI int exit(const Error& e, std::ostream& out = std::cout, std::ostream& err = std::cerr) const { /// Avoid printing anything if this is a CLI::RuntimeError - if (dynamic_cast(&e) != nullptr) + if (e.get_name() == "RuntimeError") return e.get_exit_code(); - if (dynamic_cast(&e) != nullptr) + if (e.get_name() == "CallForHelp") { out << help(); return e.get_exit_code(); } - if (dynamic_cast(&e) != nullptr) + if (e.get_name() == "CallForAllHelp") { out << help("", AppFormatMode::All); return e.get_exit_code(); @@ -7080,7 +7097,12 @@ namespace CLI /// Access the config formatter as a configBase pointer std::shared_ptr get_config_formatter_base() const { + // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase +#if defined(__cpp_rtti) || (defined(__GXX_RTTI) && __GXX_RTTI) || (defined(_HAS_STATIC_RTTI) && (_HAS_STATIC_RTTI == 0)) return std::dynamic_pointer_cast(config_formatter_); +#else + return std::static_pointer_cast(config_formatter_); +#endif } /// Get the app or subcommand description @@ -8908,6 +8930,21 @@ namespace CLI /// This class is simply to allow tests access to App's protected functions struct AppFriend { +#ifdef CLI11_CPP14 + /// Wrap _parse_short, perfectly forward arguments and return + template + static decltype(auto) parse_arg(App* app, Args&&... args) + { + return app->_parse_arg(std::forward(args)...); + } + + /// Wrap _parse_subcommand, perfectly forward arguments and return + template + static decltype(auto) parse_subcommand(App* app, Args&&... args) + { + return app->_parse_subcommand(std::forward(args)...); + } +#else /// Wrap _parse_short, perfectly forward arguments and return template static auto parse_arg(App* app, Args&&... args) -> @@ -8923,6 +8960,7 @@ namespace CLI { return app->_parse_subcommand(std::forward(args)...); } +#endif /// Wrap the fallthrough parent function to make sure that is working correctly static App* get_fallthrough_parent(App* app) { return app->_get_fallthrough_parent(); } }; @@ -8930,7 +8968,7 @@ namespace CLI } // namespace CLI -// From CLI/Config.hpp: +// From Config.hpp: namespace CLI { @@ -9360,7 +9398,7 @@ namespace CLI } // namespace CLI -// From CLI/Formatter.hpp: +// From Formatter.hpp: namespace CLI { diff --git a/dep/CLI11/README.md b/dep/CLI11/README.md index a6e04c18013..023ffc1e3b5 100644 --- a/dep/CLI11/README.md +++ b/dep/CLI11/README.md @@ -1,5 +1,4 @@ # CLI11 -Taken from [release v1.9.0](https://github.com/CLIUtils/CLI11/releases/tag/v1.9.0), source commit -[dd0d8e4](https://github.com/CLIUtils/CLI11/commit/dd0d8e4fe729e5b1110232c7a5c9566dad884686) - +Taken from [release v1.9.1](https://github.com/CLIUtils/CLI11/releases/tag/v1.9.1), source commit +[5cb3efa](https://github.com/CLIUtils/CLI11/commit/5cb3efabce007c3a0230e4cc2e27da491c646b6c) diff --git a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp index c13bcbdc8b6..fac134c9519 100644 --- a/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/ColorSchemeTests.cpp @@ -84,7 +84,7 @@ namespace SettingsModelLocalTests for (size_t i = 0; i < expectedCampbellTable.size(); i++) { - const auto& expected = expectedCampbellTable.at(i); + const til::color expected{ expectedCampbellTable.at(i) }; const til::color actual{ scheme->Table().at(static_cast(i)) }; VERIFY_ARE_EQUAL(expected, actual); } diff --git a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp index ad2463bbb70..4f064a0e6cb 100644 --- a/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/TerminalSettingsTests.cpp @@ -795,11 +795,11 @@ namespace SettingsModelLocalTests const auto terminalSettings4 = createTerminalSettings(activeProfiles.GetAt(4), colorSchemes); const auto terminalSettings5 = createTerminalSettings(activeProfiles.GetAt(5), colorSchemes); - VERIFY_ARE_EQUAL(RGB(0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme + VERIFY_ARE_EQUAL(til::color(0x12, 0x34, 0x56), terminalSettings0->CursorColor()); // from color scheme VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings1->CursorColor()); // default - VERIFY_ARE_EQUAL(RGB(0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme) - VERIFY_ARE_EQUAL(RGB(0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme) - VERIFY_ARE_EQUAL(RGB(0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme) + VERIFY_ARE_EQUAL(til::color(0x23, 0x45, 0x67), terminalSettings2->CursorColor()); // from profile (trumps color scheme) + VERIFY_ARE_EQUAL(til::color(0x34, 0x56, 0x78), terminalSettings3->CursorColor()); // from profile (not set in color scheme) + VERIFY_ARE_EQUAL(til::color(0x45, 0x67, 0x89), terminalSettings4->CursorColor()); // from profile (no color scheme) VERIFY_ARE_EQUAL(DEFAULT_CURSOR_COLOR, terminalSettings5->CursorColor()); // default } diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 0d8cb34fa2f..fe6636a4f93 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -1007,7 +1007,7 @@ namespace winrt::TerminalApp::implementation const auto package{ GetCurrentPackageNoThrow() }; if (package == nullptr) { - return; + co_return; } const auto tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin(); diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp index 6cda2158ae3..6667f760ea7 100644 --- a/src/cascadia/TerminalApp/TabBase.cpp +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -176,7 +176,7 @@ namespace winrt::TerminalApp::implementation if (_keyChord == keyChordText) { - return; + co_return; } _keyChord = keyChordText; diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index bacba947c5f..5dc52596177 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -274,7 +274,7 @@ namespace winrt::TerminalApp::implementation // Don't reload our icon if it hasn't changed. if (iconPath == _lastIconPath) { - return; + co_return; } _lastIconPath = iconPath; @@ -283,7 +283,7 @@ namespace winrt::TerminalApp::implementation // for when we show the icon again) if (_iconHidden) { - return; + co_return; } auto weakThis{ get_weak() }; diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index b6bb9e6b98b..d329f1132f1 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -2274,7 +2274,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation { if (_IsClosing()) { - return; + co_return; } if (e.DataView().Contains(StandardDataFormats::ApplicationLink())) diff --git a/src/cascadia/TerminalSettingsModel/FileUtils.cpp b/src/cascadia/TerminalSettingsModel/FileUtils.cpp index 4535c23be06..30104ff28a6 100644 --- a/src/cascadia/TerminalSettingsModel/FileUtils.cpp +++ b/src/cascadia/TerminalSettingsModel/FileUtils.cpp @@ -12,7 +12,7 @@ #include #include -static constexpr std::string_view Utf8Bom{ u8"\uFEFF" }; +static constexpr std::string_view Utf8Bom{ "\xEF\xBB\xBF", 3 }; static constexpr std::wstring_view UnpackagedSettingsFolderName{ L"Microsoft\\Windows Terminal\\" }; namespace winrt::Microsoft::Terminal::Settings::Model diff --git a/src/common.build.pre.props b/src/common.build.pre.props index 30d7fb285fc..4d494f911ab 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -118,7 +118,8 @@ false true true - stdcpp17 + stdcpp17 + stdcpp20 stdc17 %(AdditionalOptions) /utf-8 /Zc:externConstexpr /Zc:lambda /Zc:throwingNew Guard diff --git a/src/host/ft_host/InitTests.cpp b/src/host/ft_host/InitTests.cpp index ea738dd8fa3..f3d062c8878 100644 --- a/src/host/ft_host/InitTests.cpp +++ b/src/host/ft_host/InitTests.cpp @@ -25,7 +25,7 @@ static FILE* std_in = nullptr; // This will automatically try to terminate the job object (and all of the // binaries under test that are children) whenever this class gets shut down. // also closes the FILE pointers created by reopening stdin and stdout. -auto OnAppExitKillJob = wil::scope_exit([&] { +auto OnAppExitKillJob = wil::scope_exit([] { if (std_out != nullptr) { fclose(std_out); diff --git a/src/inc/til/bit.h b/src/inc/til/bit.h index fd8d9c44ff3..dc15d4bce44 100644 --- a/src/inc/til/bit.h +++ b/src/inc/til/bit.h @@ -8,9 +8,7 @@ namespace til template, std::is_trivially_copyable, std::is_trivially_copyable>, int> = 0> [[nodiscard]] constexpr To bit_cast(const From& _Val) noexcept { -#ifdef __cpp_lib_bit_cast -#warning "Replace til::bit_cast and __builtin_bit_cast with std::bit_cast" -#endif + // TODO: Replace til::bit_cast and __builtin_bit_cast with std::bit_cast return __builtin_bit_cast(To, _Val); } } diff --git a/src/inc/til/enumset.h b/src/inc/til/enumset.h index dcb5e1a330a..5ee47a71675 100644 --- a/src/inc/til/enumset.h +++ b/src/inc/til/enumset.h @@ -124,13 +124,14 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" template static constexpr UnderlyingType to_underlying(Args... positions) noexcept { - return ((UnderlyingType{ 1 } << static_cast(positions)) | ...); - } - - template<> - static constexpr UnderlyingType to_underlying() noexcept - { - return 0; + if constexpr (sizeof...(positions) == 0) + { + return 0; + } + else + { + return ((UnderlyingType{ 1 } << static_cast(positions)) | ...); + } } UnderlyingType _data{}; diff --git a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h index aef4f3fb298..a48ed247339 100644 --- a/src/terminal/parser/ft_fuzzer/fuzzing_directed.h +++ b/src/terminal/parser/ft_fuzzer/fuzzing_directed.h @@ -253,25 +253,27 @@ namespace fuzz template static _Type GetRandom(__in _Type tMin, __in _Type tMax) { - std::mt19937 engine(m_rd()); // Mersenne twister MT19937 - std::uniform_int_distribution<_Type> distribution(tMin, tMax); - auto generator = std::bind(distribution, engine); - return generator(); + if constexpr (std::is_same_v<_Type, BYTE>) + { + // uniform_int_distribution only works with _Is_IntType types, which do not + // currently include char or unsigned char, so here is a specialization + // specifically for BYTE (unsigned char). + std::mt19937 engine(m_rd()); // Mersenne twister MT19937 + // BYTE is unsigned, so we want to also use an unsigned type to avoid sign + // extension of tMin and tMax. + std::uniform_int_distribution distribution(tMin, tMax); + auto generator = std::bind(distribution, engine); + return static_cast(generator()); + } + else + { + std::mt19937 engine(m_rd()); // Mersenne twister MT19937 + std::uniform_int_distribution<_Type> distribution(tMin, tMax); + auto generator = std::bind(distribution, engine); + return generator(); + } } - // uniform_int_distribution only works with _Is_IntType types, which do not - // currently include char or unsigned char, so here is a specialization - // specifically for BYTE (unsigned char). - template<> - static BYTE GetRandom(__in BYTE tMin, __in BYTE tMax) - { - std::mt19937 engine(m_rd()); // Mersenne twister MT19937 - // BYTE is unsigned, so we want to also use an unsigned type to avoid sign - // extension of tMin and tMax. - std::uniform_int_distribution distribution(tMin, tMax); - auto generator = std::bind(distribution, engine); - return static_cast(generator()); - } #ifdef __min_collision__ #undef __min_collision__ #define min(a, b) (((a) < (b)) ? (a) : (b)) From f17c2babd4f22c21b9ad0897f884b2b3dbe173f1 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Mar 2022 17:51:15 +0100 Subject: [PATCH 35/75] AtlasEngine: Fix ConstBuffer invalidation for background color changes (#12667) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `bg != _r.backgroundColor` invalidation check wasn't symmetric with us setting `_r.backgroundColor` to `bg | _api.backgroundOpaqueMixin`. Due to this, when the `backgroundOpaqueMixin` changed, we didn't always update the const buffer appropriately. ## PR Checklist * [x] Closes #11773 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Enable window transparency (`backgroundOpaqueMixin == 0x00000000`) * Maximize window (ensure we have gutters in the first place) * Disable window transparency (`backgroundOpaqueMixin == 0xff000000`) * Gutters are filled ✅ --- src/renderer/atlas/AtlasEngine.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index bf108602862..bd30b04faee 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -566,7 +566,9 @@ CATCH_RETURN() [[nodiscard]] HRESULT AtlasEngine::UpdateDrawingBrushes(const TextAttribute& textAttributes, const RenderSettings& renderSettings, const gsl::not_null /*pData*/, const bool usingSoftFont, const bool isSettingDefaultBrushes) noexcept try { - const auto [fg, bg] = renderSettings.GetAttributeColorsWithAlpha(textAttributes); + auto [fg, bg] = renderSettings.GetAttributeColorsWithAlpha(textAttributes); + fg |= 0xff000000; + bg |= _api.backgroundOpaqueMixin; if (!isSettingDefaultBrushes) { @@ -588,7 +590,7 @@ try WI_ClearAllFlags(flags, CellFlags::UnderlineDotted | CellFlags::UnderlineDouble); } - const u32x2 newColors{ gsl::narrow_cast(fg | 0xff000000), gsl::narrow_cast(bg | _api.backgroundOpaqueMixin) }; + const u32x2 newColors{ gsl::narrow_cast(fg), gsl::narrow_cast(bg) }; const AtlasKeyAttributes attributes{ 0, textAttributes.IsIntense(), textAttributes.IsItalic(), 0 }; if (_api.attributes != attributes) @@ -602,7 +604,7 @@ try } else if (textAttributes.BackgroundIsDefault() && bg != _r.backgroundColor) { - _r.backgroundColor = bg | _api.backgroundOpaqueMixin; + _r.backgroundColor = bg; WI_SetFlag(_r.invalidations, RenderInvalidations::ConstBuffer); } From f507d9f491745857f523d6ce0b11bbd5c35fcba0 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 11 Mar 2022 13:50:02 -0600 Subject: [PATCH 36/75] Fix a pair of crashes, likely related to defterm (#12666) This fixes a pair of inbox bugs, hopefully. * MSFT:35731327 * There's a small window where a peasant is being created when a monarch is exiting. When that happens, the new peasant will try to tell itself (the new monarch) when the peasant was last activated, but because the window hasn't actually finished instantiating, the peasant doesn't yet have a LastActivatedArgs to tell the monarch about. * MSFT:32518679 (ARM version) / MSFT:32279047 (AMD64 version) * This one's tricky. Not totally sure this is the fix, bug assuming my hypothesis is correct, this should fix it. Regardless, this does fix a bug that was in the code. * If the king dies right as another window is starting, right while the new window is starting to ProposeCommandline to the monarch, the monarch could die. If it does, the new window just explodes too. Not what you want. Vaguely tested the second bug manually, by setting breakpoints in the monarch, starting a defterm, then exiting the monarch while the handoff was in process. That now creates a new window, so that's at least something. `RemotingTests::TestProposeCommandlineWithDeadMonarch` was the closest I could get to testing that. The first bug only got an eye check. Not sure how to repro, but I figured yeet and hopefully we get it. * [x] Closes #12624 --- .github/actions/spelling/allow/apis.txt | 1 + src/cascadia/Remoting/Monarch.cpp | 11 ++ src/cascadia/Remoting/Monarch.idl | 9 +- src/cascadia/Remoting/WindowManager.cpp | 184 ++++++++++++++---- src/cascadia/Remoting/WindowManager.h | 4 + .../UnitTests_Remoting/RemotingTests.cpp | 115 +++++++++-- 6 files changed, 265 insertions(+), 59 deletions(-) diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 13d32233b58..daf1feb13af 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -72,6 +72,7 @@ IFACEMETHOD IFile IInheritable IMap +IMonarch IObject iosfwd IPackage diff --git a/src/cascadia/Remoting/Monarch.cpp b/src/cascadia/Remoting/Monarch.cpp index eed247ab41a..ba1f76a1a5f 100644 --- a/src/cascadia/Remoting/Monarch.cpp +++ b/src/cascadia/Remoting/Monarch.cpp @@ -353,6 +353,14 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - void Monarch::HandleActivatePeasant(const Remoting::WindowActivatedArgs& args) { + if (args == nullptr) + { + // MSFT:35731327, GH #12624. There's a chance that the way the + // window gets set up for defterm, the ActivatedArgs haven't been + // created for this window yet. Check here and just ignore them if + // they're null. They'll come back with real args soon + return; + } // Start by making a local copy of these args. It's easier for us if our // tracking of these args is all in-proc. That way, the only thing that // could fail due to the peasant dying is _this first copy_. @@ -418,6 +426,9 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation // - void Monarch::_doHandleActivatePeasant(const winrt::com_ptr& localArgs) { + // We're sure that localArgs isn't null here, we checked before in our + // one caller (in Monarch::HandleActivatePeasant) + const auto newLastActiveTime = localArgs->ActivatedTime().time_since_epoch().count(); // * Check all the current lists to look for this peasant. diff --git a/src/cascadia/Remoting/Monarch.idl b/src/cascadia/Remoting/Monarch.idl index f60b3997a64..08c7b63cbda 100644 --- a/src/cascadia/Remoting/Monarch.idl +++ b/src/cascadia/Remoting/Monarch.idl @@ -44,8 +44,8 @@ namespace Microsoft.Terminal.Remoting String TabTitle; }; - [default_interface] runtimeclass Monarch { - Monarch(); + interface IMonarch + { UInt64 GetPID(); UInt64 AddPeasant(IPeasant peasant); @@ -67,4 +67,9 @@ namespace Microsoft.Terminal.Remoting event Windows.Foundation.TypedEventHandler WindowClosed; event Windows.Foundation.TypedEventHandler QuitAllRequested; }; + + [default_interface] runtimeclass Monarch : IMonarch + { + Monarch(); + }; } diff --git a/src/cascadia/Remoting/WindowManager.cpp b/src/cascadia/Remoting/WindowManager.cpp index 8ce04464cda..89f337830bf 100644 --- a/src/cascadia/Remoting/WindowManager.cpp +++ b/src/cascadia/Remoting/WindowManager.cpp @@ -77,57 +77,161 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation } } - void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) + void WindowManager::_proposeToMonarch(const Remoting::CommandlineArgs& args, + std::optional& givenID, + winrt::hstring& givenName) { - // If we're the king, we _definitely_ want to process the arguments, we were - // launched with them! - // - // Otherwise, the King will tell us if we should make a new window - _shouldCreateWindow = _isKing; - std::optional givenID; - winrt::hstring givenName{}; - if (!_isKing) + // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare below. + static constexpr HRESULT RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); + static constexpr HRESULT RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); + + // The monarch may respond back "you should be a new + // window, with ID,name of (id, name)". Really the responses are: + // * You should not create a new window + // * Create a new window (but without a given ID or name). The + // Monarch will assign your ID/name later + // * Create a new window, and you'll have this ID or name + // - This is the case where the user provides `wt -w 1`, and + // there's no existing window 1 + + // You can emulate the monarch dying by: starting a terminal, sticking a + // breakpoint in + // TerminalApp!winrt::TerminalApp::implementation::AppLogic::_doFindTargetWindow, + // starting a defterm, and when that BP gets hit, kill the original + // monarch, and see what happens here. + + bool proposedCommandline = false; + Remoting::ProposeCommandlineResult result{ nullptr }; + while (!proposedCommandline) { - // The monarch may respond back "you should be a new - // window, with ID,name of (id, name)". Really the responses are: - // * You should not create a new window - // * Create a new window (but without a given ID or name). The - // Monarch will assign your ID/name later - // * Create a new window, and you'll have this ID or name - // - This is the case where the user provides `wt -w 1`, and - // there's no existing window 1 - - const auto result = _monarch.ProposeCommandline(args); - _shouldCreateWindow = result.ShouldCreateWindow(); - if (result.Id()) + try { - givenID = result.Id().Value(); + result = _monarch.ProposeCommandline(args); + proposedCommandline = true; } - givenName = result.WindowName(); - // TraceLogging doesn't have a good solution for logging an - // optional. So we have to repeat the calls here: - if (givenID) + catch (const winrt::hresult_error& e) { - TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), - TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), - TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + // We did not successfully ask the king what to do. They + // hopefully just died here. That's okay, let's just go ask the + // next in the line of succession. At the very worst, we'll find + // _us_, (likely last in the line). + // + // If the king returned some _other_ error here, than lets + // bubble that up because that's a real issue. + // + // I'm checking both these here. I had previously got a + // RPC_S_CALL_FAILED about here once. + if (e.code() == RPC_SERVER_UNAVAILABLE_HR || e.code() == RPC_CALL_FAILED_HR) + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_kingDied", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // We failed to ask the monarch. It must have died. Try and + // find the real monarch. Don't perform an election, that + // assumes we have a peasant, which we don't yet. + _createMonarchAndCallbacks(); + // _createMonarchAndCallbacks will initialize _isKing + if (_isKing) + { + // We became the king. We don't need to ProposeCommandline to ourself, we're just + // going to do it. + // + // Return early, because there's nothing else for us to do here. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_becameKing", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + // In WindowManager::ProposeCommandline, had we been the + // king originally, we would have started by setting + // this to true. We became the monarch here, so set it + // here as well. + _shouldCreateWindow = true; + return; + } + + // Here, we created the new monarch, it wasn't us, so we're + // gonna go through the while loop again and ask the new + // king. + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_tryAgain", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_proposeToMonarch_unexpectedResultFromKing", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + LOG_CAUGHT_EXCEPTION(); + throw; + } } - else + catch (...) { + // If the monarch (maybe us) failed for _any other reason_ than + // them dying. This IS quite unexpected. Let this bubble out. TraceLoggingWrite(g_hRemotingProvider, - "WindowManager_ProposeCommandline", - TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), - TraceLoggingPointer(nullptr, "Id", "No ID provided"), - TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + "WindowManager_proposeToMonarch_unexpectedExceptionFromKing", TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + + LOG_CAUGHT_EXCEPTION(); + throw; } } + + // Here, the monarch (not us) has replied to the message. Get the + // valuables out of the response: + _shouldCreateWindow = result.ShouldCreateWindow(); + if (result.Id()) + { + givenID = result.Id().Value(); + } + givenName = result.WindowName(); + + // TraceLogging doesn't have a good solution for logging an + // optional. So we have to repeat the calls here: + if (givenID) + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingUInt64(givenID.value(), "Id", "The ID we should assign our peasant"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } else + { + TraceLoggingWrite(g_hRemotingProvider, + "WindowManager_ProposeCommandline", + TraceLoggingBoolean(_shouldCreateWindow, "CreateWindow", "true iff we should create a new window"), + TraceLoggingPointer(nullptr, "Id", "No ID provided"), + TraceLoggingWideString(givenName.c_str(), "Name", "The name we should assign this window"), + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingKeyword(TIL_KEYWORD_TRACE)); + } + } + void WindowManager::ProposeCommandline(const Remoting::CommandlineArgs& args) + { + // If we're the king, we _definitely_ want to process the arguments, we were + // launched with them! + // + // Otherwise, the King will tell us if we should make a new window + _shouldCreateWindow = _isKing; + std::optional givenID; + winrt::hstring givenName{}; + if (!_isKing) + { + _proposeToMonarch(args, givenID, givenName); + } + + // During _proposeToMonarch, it's possible that we found that the king was dead, and we're the new king. Cool! Do this now. + if (_isKing) { // We're the monarch, we don't need to propose anything. We're just // going to do it. @@ -195,6 +299,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation _createPeasantThread(); } + // This right here will just tell us to stash the args away for the + // future. The AppHost hasnt yet set up the callbacks, and the rest + // of the app hasn't started at all. We'll note them and come back + // later. _peasant.ExecuteCommandline(args); } // Otherwise, we'll do _nothing_. diff --git a/src/cascadia/Remoting/WindowManager.h b/src/cascadia/Remoting/WindowManager.h index 0604c34d557..5ea2c56eb6f 100644 --- a/src/cascadia/Remoting/WindowManager.h +++ b/src/cascadia/Remoting/WindowManager.h @@ -82,6 +82,10 @@ namespace winrt::Microsoft::Terminal::Remoting::implementation void _waitOnMonarchThread(); void _raiseFindTargetWindowRequested(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Microsoft::Terminal::Remoting::FindTargetWindowArgs& args); + + void _proposeToMonarch(const Remoting::CommandlineArgs& args, + std::optional& givenID, + winrt::hstring& givenName); }; } diff --git a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp index 2112c58a6bc..2f0c536837d 100644 --- a/src/cascadia/UnitTests_Remoting/RemotingTests.cpp +++ b/src/cascadia/UnitTests_Remoting/RemotingTests.cpp @@ -46,6 +46,11 @@ namespace RemotingUnitTests std::function pfnIsWindowOnCurrentVirtualDesktop; }; +#define DIE \ + { \ + throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); \ + }; + // This is a silly helper struct. // It will always throw an hresult_error of "RPC server is unavailable" on any of its methods. // The monarch uses this particular error code to check for a dead peasant vs another exception. @@ -57,28 +62,29 @@ namespace RemotingUnitTests // class can be used to replace a peasant inside a Monarch, to emulate that // peasant process dying. Any time the monarch tries to do something to this // peasant, it'll throw an exception. + struct DeadPeasant : implements { DeadPeasant() = default; - void AssignID(uint64_t /*id*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - uint64_t GetID() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - winrt::hstring WindowName() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - winrt::hstring ActiveTabTitle() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void ActiveTabTitle(const winrt::hstring& /*value*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - uint64_t GetPID() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); } - void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); } - void RequestIdentifyWindows() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void DisplayWindowId() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - Remoting::CommandlineArgs InitialArgs() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); } - Remoting::WindowActivatedArgs GetLastActivatedArgs() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); } - void RequestRename(const Remoting::RenameRequestArgs& /*args*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); } - void Summon(const Remoting::SummonWindowBehavior& /*args*/) { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void RequestShowNotificationIcon() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void RequestHideNotificationIcon() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - winrt::hstring GetWindowLayout() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void RequestQuitAll() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; - void Quit() { throw winrt::hresult_error(winrt::hresult{ (int32_t)0x800706ba }); }; + void AssignID(uint64_t /*id*/) DIE; + uint64_t GetID() DIE; + winrt::hstring WindowName() DIE; + winrt::hstring ActiveTabTitle() DIE; + void ActiveTabTitle(const winrt::hstring& /*value*/) DIE; + uint64_t GetPID() DIE; + bool ExecuteCommandline(const Remoting::CommandlineArgs& /*args*/) DIE; + void ActivateWindow(const Remoting::WindowActivatedArgs& /*args*/) DIE; + void RequestIdentifyWindows() DIE; + void DisplayWindowId() DIE; + Remoting::CommandlineArgs InitialArgs() DIE; + Remoting::WindowActivatedArgs GetLastActivatedArgs() DIE; + void RequestRename(const Remoting::RenameRequestArgs& /*args*/) DIE; + void Summon(const Remoting::SummonWindowBehavior& /*args*/) DIE; + void RequestShowNotificationIcon() DIE; + void RequestHideNotificationIcon() DIE; + winrt::hstring GetWindowLayout() DIE; + void RequestQuitAll() DIE; + void Quit() DIE; TYPED_EVENT(WindowActivated, winrt::Windows::Foundation::IInspectable, Remoting::WindowActivatedArgs); TYPED_EVENT(ExecuteCommandlineRequested, winrt::Windows::Foundation::IInspectable, Remoting::CommandlineArgs); TYPED_EVENT(IdentifyWindowsRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); @@ -92,6 +98,31 @@ namespace RemotingUnitTests TYPED_EVENT(GetWindowLayoutRequested, winrt::Windows::Foundation::IInspectable, Remoting::GetWindowLayoutArgs); }; + // Same idea. + struct DeadMonarch : implements + { + DeadMonarch() = default; + uint64_t GetPID() DIE; + uint64_t AddPeasant(Remoting::IPeasant /*peasant*/) DIE; + uint64_t GetNumberOfPeasants() DIE; + Remoting::ProposeCommandlineResult ProposeCommandline(Remoting::CommandlineArgs /*args*/) DIE; + void HandleActivatePeasant(Remoting::WindowActivatedArgs /*args*/) DIE; + void SummonWindow(Remoting::SummonWindowSelectionArgs /*args*/) DIE; + void SignalClose(uint64_t /*peasantId*/) DIE; + + void SummonAllWindows() DIE; + bool DoesQuakeWindowExist() DIE; + winrt::Windows::Foundation::Collections::IVectorView GetPeasantInfos() DIE; + winrt::Windows::Foundation::Collections::IVector GetAllWindowLayouts() DIE; + + TYPED_EVENT(FindTargetWindowRequested, winrt::Windows::Foundation::IInspectable, Remoting::FindTargetWindowArgs); + TYPED_EVENT(ShowNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(HideNotificationIconRequested, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(WindowCreated, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(WindowClosed, winrt::Windows::Foundation::IInspectable, winrt::Windows::Foundation::IInspectable); + TYPED_EVENT(QuitAllRequested, winrt::Windows::Foundation::IInspectable, Remoting::QuitAllRequestedArgs); + }; + class RemotingTests { BEGIN_TEST_CLASS(RemotingTests) @@ -145,6 +176,8 @@ namespace RemotingUnitTests TEST_METHOD(TestSummonAfterWindowClose); + TEST_METHOD(TestProposeCommandlineWithDeadMonarch); + TEST_CLASS_SETUP(ClassSetup) { return true; @@ -2522,4 +2555,48 @@ namespace RemotingUnitTests VERIFY_IS_TRUE(args.FoundMatch()); } + void RemotingTests::TestProposeCommandlineWithDeadMonarch() + { + Log::Comment(L"MSFT:32279047 - It's possible for a window to start " + L"right as the monarch is exiting. In that case, " + L"WindowManager relies on getting a " + L"RPC_E_SERVER_UNAVAILABLE when the process dies. "); + + const winrt::guid guid1{ Utils::GuidFromString(L"{11111111-1111-1111-1111-111111111111}") }; + const winrt::guid guid2{ Utils::GuidFromString(L"{22222222-2222-2222-2222-222222222222}") }; + + constexpr auto monarch0PID = 12345u; + + auto m0 = make_private(monarch0PID); + + { + Remoting::CommandlineArgs args{ { L"wt.exe" }, { L"-Embedding" } }; + const auto result = m0->ProposeCommandline(args); + auto shouldCreateWindow = result.ShouldCreateWindow(); + VERIFY_IS_TRUE(shouldCreateWindow); + } + + auto m1 = make_self(); + { + Remoting::CommandlineArgs args{ { L"wt.exe" }, { L"-Embedding" } }; + + try + { + const auto result = m1->ProposeCommandline(args); + VERIFY_IS_FALSE(true, L"This should have thrown"); + } + catch (const winrt::hresult_error& e) + { + // these two errors are Win32 errors, convert them to HRESULTS so we can actually compare here. + constexpr HRESULT RPC_SERVER_UNAVAILABLE_HR = HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE); + constexpr HRESULT RPC_CALL_FAILED_HR = HRESULT_FROM_WIN32(RPC_S_CALL_FAILED); + + // This is the same check in WindowManager::_proposeToMonarch. + VERIFY_IS_TRUE(e.code() == RPC_SERVER_UNAVAILABLE_HR || e.code() == RPC_CALL_FAILED_HR); + } + // just don't catch other types of exceptions. They'll take out + // TAEF, which will count as a failure. + } + } + } From a4a6dfcc8d691dafd5601c82ff2014aec823a7d5 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Mar 2022 22:14:06 +0100 Subject: [PATCH 37/75] Fix overflow in Viewport::FromDimensions (#12669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes one source of potential integer overflows from the Viewport class. Other parts were left untouched, as this entire class of overflow issues gets fixed all at once, as soon as we replace COORD with til::coord (etc.). ## PR Checklist * [x] Closes #5271 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Call `ScrollConsoleScreenBufferW` with out of bounds coordinates * Doesn't crash ✅ --- src/renderer/base/renderer.cpp | 3 +-- src/types/viewport.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index f175ce010f7..e58b71fa3f0 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -677,8 +677,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) for (const auto& dirtyRect : dirtyAreas) { - // Shortcut: don't bother redrawing if the width is 0. - if (dirtyRect.left == dirtyRect.right) + if (!dirtyRect) { continue; } diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index 9038a60cca5..aa30501501f 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -46,7 +46,12 @@ Viewport Viewport::FromDimensions(const COORD origin, const short width, const short height) noexcept { - return Viewport::FromExclusive({ origin.X, origin.Y, origin.X + width, origin.Y + height }); + return Viewport::FromInclusive({ + origin.X, + origin.Y, + base::saturated_cast(origin.X + width - 1), + base::saturated_cast(origin.Y + height - 1), + }); } // Function Description: @@ -60,7 +65,12 @@ Viewport Viewport::FromDimensions(const COORD origin, Viewport Viewport::FromDimensions(const COORD origin, const COORD dimensions) noexcept { - return Viewport::FromExclusive({ origin.X, origin.Y, origin.X + dimensions.X, origin.Y + dimensions.Y }); + return Viewport::FromInclusive({ + origin.X, + origin.Y, + base::saturated_cast(origin.X + dimensions.X - 1), + base::saturated_cast(origin.Y + dimensions.Y - 1), + }); } // Function Description: From 15874d980fb9e644ad72ecad40be8bab2ce245cd Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 11 Mar 2022 16:56:14 -0500 Subject: [PATCH 38/75] Fix the restore failure caused by pulling in two copies of Xaml (#12672) Closes #12671 --- build/packages.config | 2 ++ common.openconsole.props | 4 ++++ src/cascadia/TerminalApp/packages.config | 1 - 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build/packages.config b/build/packages.config index 7b2f271d521..189017052d2 100644 --- a/build/packages.config +++ b/build/packages.config @@ -3,4 +3,6 @@ + + diff --git a/common.openconsole.props b/common.openconsole.props index 400555dbdd5..8df0c31d091 100644 --- a/common.openconsole.props +++ b/common.openconsole.props @@ -15,11 +15,15 @@ For the Windows 10 build, we're targeting the prerelease version of Microsoft.UI.Xaml. This version emits every XAML DLL directly into our package. This is a workaround for us not having deliverable MSFT-21242953 on this version of Windows. + + This version should be tracked in all project packages.config files for projects that depend on Xaml. --> 2.7.0-prerelease.210913003 2.7.0 diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config index 62c921a4fac..e039fa4ae47 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -1,7 +1,6 @@ - From 7fdcd6f5b3a73f5805c5c09b8fca3cbf3c1a3924 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Fri, 11 Mar 2022 17:30:31 -0600 Subject: [PATCH 39/75] When we delete a profile, focus the delete button automatically (#12558) This sure is bodgy, but it makes sense. Right now, when we delete a profile, we load in a totally new content for the new profile's settings. That one resets the scroll view and the focus, and now the "delete" button is obviously not focused. Instead, this PR will manually re-focus the delete button of a profile page when the page is navigated to _because we deleted another profile_. * [x] This will take care of #11971 --- .../TerminalSettingsEditor/MainPage.cpp | 5 +++-- src/cascadia/TerminalSettingsEditor/MainPage.h | 2 +- src/cascadia/TerminalSettingsEditor/Profiles.h | 1 + .../TerminalSettingsEditor/Profiles.idl | 1 + .../TerminalSettingsEditor/Profiles_Base.cpp | 17 +++++++++++++++++ .../TerminalSettingsEditor/Profiles_Base.h | 1 + .../TerminalSettingsEditor/Profiles_Base.xaml | 3 ++- 7 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index 08855aa0336..6fb61e1314f 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -410,11 +410,12 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // - NOTE: this does not update the selected item. // Arguments: // - profile - the profile object we are getting a view of - void MainPage::_Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage) + void MainPage::_Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage, const bool focusDeleteButton) { auto state{ winrt::make(profile, _settingsClone.GlobalSettings().ColorSchemes(), *this) }; + state.FocusDeleteButton(focusDeleteButton); _PreNavigateHelper(); @@ -592,7 +593,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // navigate to the profile next to this one const auto newSelectedItem{ menuItems.GetAt(index < menuItems.Size() - 1 ? index : index - 1) }; SettingsNav().SelectedItem(newSelectedItem); - _Navigate(newSelectedItem.try_as().Tag().try_as(), BreadcrumbSubPage::None); + _Navigate(newSelectedItem.try_as().Tag().try_as(), BreadcrumbSubPage::None, true); } IObservableVector MainPage::Breadcrumbs() noexcept diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h index 2fea7ef72ed..b17cc3efbfe 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.h +++ b/src/cascadia/TerminalSettingsEditor/MainPage.h @@ -61,7 +61,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void _PreNavigateHelper(); void _Navigate(hstring clickedItemTag, BreadcrumbSubPage subPage); - void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage); + void _Navigate(const Editor::ProfileViewModel& profile, BreadcrumbSubPage subPage, const bool focusDeleteButton = false); winrt::Microsoft::Terminal::Settings::Editor::ColorSchemesPageNavigationState _colorSchemesNavState{ nullptr }; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.h b/src/cascadia/TerminalSettingsEditor/Profiles.h index 8b245b8fe28..45721540f96 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles.h @@ -33,5 +33,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } WINRT_PROPERTY(Editor::ProfileViewModel, Profile, nullptr); + WINRT_PROPERTY(bool, FocusDeleteButton, false); }; }; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.idl b/src/cascadia/TerminalSettingsEditor/Profiles.idl index a3a1dafd1cf..9bedfd36523 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.idl +++ b/src/cascadia/TerminalSettingsEditor/Profiles.idl @@ -9,5 +9,6 @@ namespace Microsoft.Terminal.Settings.Editor runtimeclass ProfilePageNavigationState { ProfileViewModel Profile { get; }; + Boolean FocusDeleteButton; }; } diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp index 1c75efabfc9..5555abfcfd0 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.cpp @@ -37,6 +37,23 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { StartingDirectoryUseParentCheckbox().IsChecked(true); } + + _layoutUpdatedRevoker = LayoutUpdated(winrt::auto_revoke, [state, this](auto /*s*/, auto /*e*/) { + // This event fires every time the layout changes, but it is always the last one to fire + // in any layout change chain. That gives us great flexibility in finding the right point + // at which to initialize our renderer (and our terminal). + // Any earlier than the last layout update and we may not know the terminal's starting size. + + // Only let this succeed once. + _layoutUpdatedRevoker.revoke(); + + if (state.FocusDeleteButton()) + { + DeleteButton().Focus(FocusState::Programmatic); + state.FocusDeleteButton(false); + ProfilesBase_ScrollView().ChangeView(nullptr, ProfilesBase_ScrollView().ScrollableHeight(), nullptr); + } + }); } void Profiles_Base::OnNavigatedFrom(const NavigationEventArgs& /*e*/) diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h index 778a2f675cc..6a56554daca 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Base.h +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.h @@ -29,6 +29,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation private: Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker _ViewModelChangedRevoker; + winrt::Windows::UI::Xaml::Controls::SwapChainPanel::LayoutUpdated_revoker _layoutUpdatedRevoker; }; }; diff --git a/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml index 4f593012110..567bb0b9725 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles_Base.xaml @@ -31,7 +31,8 @@ Margin="{StaticResource StandardIndentMargin}" Style="{StaticResource DisclaimerStyle}" Visibility="{x:Bind Profile.IsBaseLayer}" /> - From f9be1720bd03716cc13fc00e59ea1239f187ca77 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Fri, 11 Mar 2022 15:50:47 -0800 Subject: [PATCH 40/75] Use UIA notifications for text output (#12358) ## Summary of the Pull Request This change makes Windows Terminal raise a `RaiseNotificationEvent()` ([docs](https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.automation.peers.automationpeer.raisenotificationevent?view=winrt-22000)) for new text output to the buffer. This is intended to help Narrator identify what new output appears and reduce the workload of diffing the buffer when a `TextChanged` event occurs. ## Detailed Description of the Pull Request / Additional comments The flow of the event occurs as follows: - `Terminal::_WriteBuffer()` - New text is output to the text buffer. Notify the renderer that we have new text (and what that text is). - `Renderer::TriggerNewTextNotification()` - Cycle through all the rendering engines and tell them to notify handle the new text output. - None of the rendering engines _except_ `UiaEngine` has it implemented, so really we're just notifying UIA. - `UiaEngine::NotifyNewText()` - Concatenate any new output into a string. - When we're done painting, tell the notification system to actually notify of new events occurring and clear any stored output text. That way, we're ready for the next renderer frame. - `InteractivityAutomationPeer::NotifyNewOutput()` --> `TermControlAutomationPeer::NotifyNewOutput` - NOTE: these are split because of the in-proc and out-of-proc separation of the buffer. - Actually `RaiseNotificationEvent()` for the new text output. Additionally, we had to handle the "local echo" problem: when a key is pressed, the character is said twice (once for the keyboard event, and again for the character being written to the buffer). To accomplish this, we did the following: - `TermControl`: - here, we already handle keyboard events, so I added a line saying "if we have an automation peer attached, record the keyboard event in the automation peer". - `TermControlAutomationPeer`: - just before the notification is dispatched, check if the string of recent keyboard events match the beginning of the string of new output. If that's the case, we can assume that the common prefix was the "local echo". This is a fairly naive heuristic, but it's been working. Closes the following ADO bugs: - https://dev.azure.com/microsoft/OS/_workitems/edit/36506838 - (Probably) https://dev.azure.com/microsoft/OS/_workitems/edit/38011453 ## Test cases - [x] Base case: "echo hello" - [x] Partial line change - [x] Scrolling (should be unaffected) - [x] Large output - [x] "local echo": keyboard events read input character twice --- .github/actions/spelling/expect/expect.txt | 1 + .../InteractivityAutomationPeer.cpp | 5 + .../InteractivityAutomationPeer.h | 2 + .../InteractivityAutomationPeer.idl | 1 + src/cascadia/TerminalControl/TermControl.cpp | 5 + .../TermControlAutomationPeer.cpp | 107 ++++++++++++++++++ .../TermControlAutomationPeer.h | 3 + src/cascadia/TerminalCore/Terminal.cpp | 5 + .../UnitTests_TerminalCore/ScrollTest.cpp | 1 + src/host/ScreenBufferRenderTarget.cpp | 10 ++ src/host/ScreenBufferRenderTarget.hpp | 1 + src/renderer/atlas/AtlasEngine.api.cpp | 5 + src/renderer/atlas/AtlasEngine.h | 1 + src/renderer/base/RenderEngineBase.cpp | 5 + src/renderer/base/renderer.cpp | 8 ++ src/renderer/base/renderer.hpp | 2 + src/renderer/inc/DummyRenderTarget.hpp | 1 + src/renderer/inc/IRenderEngine.hpp | 1 + src/renderer/inc/IRenderTarget.hpp | 2 + src/renderer/inc/RenderEngineBase.hpp | 2 + src/renderer/uia/UiaRenderer.cpp | 81 +++++++++---- src/renderer/uia/UiaRenderer.hpp | 3 + src/types/IUiaEventDispatcher.h | 1 + 23 files changed, 232 insertions(+), 21 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 6706e4ef049..b08ed0b9011 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2084,6 +2084,7 @@ rxvt safearray SAFECAST safemath +sapi sba SBCS SBCSDBCS diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp index d2247311d1e..00621afcab5 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.cpp @@ -93,6 +93,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation _CursorChangedHandlers(*this, nullptr); } + void InteractivityAutomationPeer::NotifyNewOutput(std::wstring_view newOutput) + { + _NewOutputHandlers(*this, hstring{ newOutput }); + } + #pragma region ITextProvider com_array InteractivityAutomationPeer::GetSelection() { diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h index 57c3a85fc8d..9f0d96fead5 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.h +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.h @@ -49,6 +49,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SignalSelectionChanged() override; void SignalTextChanged() override; void SignalCursorChanged() override; + void NotifyNewOutput(std::wstring_view newOutput) override; #pragma endregion #pragma region ITextProvider Pattern @@ -73,6 +74,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation TYPED_EVENT(SelectionChanged, IInspectable, IInspectable); TYPED_EVENT(TextChanged, IInspectable, IInspectable); TYPED_EVENT(CursorChanged, IInspectable, IInspectable); + TYPED_EVENT(NewOutput, IInspectable, hstring); private: Windows::UI::Xaml::Automation::Provider::ITextRangeProvider _CreateXamlUiaTextRange(::ITextRangeProvider* returnVal) const; diff --git a/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl b/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl index 43d4bd35eac..9eb79a6329c 100644 --- a/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl +++ b/src/cascadia/TerminalControl/InteractivityAutomationPeer.idl @@ -14,5 +14,6 @@ namespace Microsoft.Terminal.Control event Windows.Foundation.TypedEventHandler SelectionChanged; event Windows.Foundation.TypedEventHandler TextChanged; event Windows.Foundation.TypedEventHandler CursorChanged; + event Windows.Foundation.TypedEventHandler NewOutput; } } diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index d329f1132f1..2410f585b7d 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -1097,6 +1097,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation keyDown) : true; + if (vkey && keyDown && _automationPeer) + { + get_self(_automationPeer)->RecordKeyEvent(vkey); + } + if (_cursorTimer) { // Manually show the cursor when a key is pressed. Restarting diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp index d910b70bb55..b0203d24208 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.cpp @@ -28,6 +28,43 @@ namespace XamlAutomation using winrt::Windows::UI::Xaml::Automation::Provider::ITextRangeProvider; } +static constexpr wchar_t UNICODE_NEWLINE{ L'\n' }; + +// Method Description: +// - creates a copy of the provided text with all of the control characters removed +// Arguments: +// - text: the string we're sanitizing +// Return Value: +// - a copy of "sanitized" with all of the control characters removed +static std::wstring Sanitize(std::wstring_view text) +{ + std::wstring sanitized{ text }; + sanitized.erase(std::remove_if(sanitized.begin(), sanitized.end(), [](wchar_t c) { + return (c < UNICODE_SPACE && c != UNICODE_NEWLINE) || c == 0x7F /*DEL*/; + }), + sanitized.end()); + return sanitized; +} + +// Method Description: +// - verifies if a given string has text that would be read by a screen reader. +// - a string of control characters, for example, would not be read. +// Arguments: +// - text: the string we're validating +// Return Value: +// - true, if the text is readable. false, otherwise. +static constexpr bool IsReadable(std::wstring_view text) +{ + for (const auto c : text) + { + if (c > UNICODE_SPACE) + { + return true; + } + } + return false; +} + namespace winrt::Microsoft::Terminal::Control::implementation { TermControlAutomationPeer::TermControlAutomationPeer(TermControl* owner, @@ -45,6 +82,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _contentAutomationPeer.SelectionChanged([this](auto&&, auto&&) { SignalSelectionChanged(); }); _contentAutomationPeer.TextChanged([this](auto&&, auto&&) { SignalTextChanged(); }); _contentAutomationPeer.CursorChanged([this](auto&&, auto&&) { SignalCursorChanged(); }); + _contentAutomationPeer.NewOutput([this](auto&&, hstring newOutput) { NotifyNewOutput(newOutput); }); _contentAutomationPeer.ParentProvider(*this); }; @@ -68,6 +106,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation _contentAutomationPeer.SetControlPadding(padding); } + void TermControlAutomationPeer::RecordKeyEvent(const WORD vkey) + { + if (const auto charCode{ MapVirtualKey(vkey, MAPVK_VK_TO_CHAR) }) + { + if (const auto keyEventChar{ gsl::narrow_cast(charCode) }; IsReadable({ &keyEventChar, 1 })) + { + _keyEvents.emplace_back(keyEventChar); + } + } + } + // Method Description: // - Signals the ui automation client that the terminal's selection has changed and should be updated // Arguments: @@ -142,8 +191,66 @@ namespace winrt::Microsoft::Terminal::Control::implementation }); } + void TermControlAutomationPeer::NotifyNewOutput(std::wstring_view newOutput) + { + // Try to suppress any events (or event data) + // that is just the keypress the user made + auto sanitized{ Sanitize(newOutput) }; + while (!_keyEvents.empty() && IsReadable(sanitized)) + { + if (til::toupper_ascii(sanitized.front()) == _keyEvents.front()) + { + // the key event's character (i.e. the "A" key) matches + // the output character (i.e. "a" or "A" text). + // We can assume that the output character resulted from + // the pressed key, so we can ignore it. + sanitized = sanitized.substr(1); + _keyEvents.pop_front(); + } + else + { + // The output doesn't match, + // so clear the input stack and + // move on to fire the event. + _keyEvents.clear(); + break; + } + } + + // Suppress event if the remaining text is not readable + if (!IsReadable(sanitized)) + { + return; + } + + auto dispatcher{ Dispatcher() }; + if (!dispatcher) + { + return; + } + + // IMPORTANT: + // [1] make sure the scope returns a copy of "sanitized" so that it isn't accidentally deleted + // [2] AutomationNotificationProcessing::All --> ensures it can be interrupted by keyboard events + // [3] Do not "RunAsync(...).get()". For whatever reason, this causes NVDA to just not receive "SignalTextChanged()"'s events. + dispatcher.RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, [weakThis{ get_weak() }, sanitizedCopy{ hstring{ sanitized } }]() { + if (auto strongThis{ weakThis.get() }) + { + try + { + strongThis->RaiseNotificationEvent(AutomationNotificationKind::ActionCompleted, + AutomationNotificationProcessing::All, + sanitizedCopy, + L"TerminalTextOutput"); + } + CATCH_LOG(); + } + }); + } + hstring TermControlAutomationPeer::GetClassNameCore() const { + // IMPORTANT: Do NOT change the name. Screen readers like JAWS may be dependent on this being "TermControl". return L"TermControl"; } diff --git a/src/cascadia/TerminalControl/TermControlAutomationPeer.h b/src/cascadia/TerminalControl/TermControlAutomationPeer.h index d912a1deca0..758864bc38f 100644 --- a/src/cascadia/TerminalControl/TermControlAutomationPeer.h +++ b/src/cascadia/TerminalControl/TermControlAutomationPeer.h @@ -48,6 +48,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void UpdateControlBounds(); void SetControlPadding(const Core::Padding padding); + void RecordKeyEvent(const WORD vkey); #pragma region FrameworkElementAutomationPeer hstring GetClassNameCore() const; @@ -64,6 +65,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation void SignalSelectionChanged() override; void SignalTextChanged() override; void SignalCursorChanged() override; + void NotifyNewOutput(std::wstring_view newOutput) override; #pragma endregion #pragma region ITextProvider Pattern @@ -78,5 +80,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation private: winrt::Microsoft::Terminal::Control::implementation::TermControl* _termControl; Control::InteractivityAutomationPeer _contentAutomationPeer; + std::deque _keyEvents; }; } diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index d03bcbb3abb..073d5c71273 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -1002,6 +1002,11 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView) _AdjustCursorPosition(proposedCursorPosition); } + // Notify UIA of new text. + // It's important to do this here instead of in TextBuffer, because here you have access to the entire line of text, + // whereas TextBuffer writes it one character at a time via the OutputCellIterator. + _buffer->GetRenderTarget().TriggerNewTextNotification(stringView); + cursor.EndDeferDrawing(); } diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index 0365f83a2a2..6513fbfbc9f 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -53,6 +53,7 @@ namespace }; virtual void TriggerCircling(){}; void TriggerTitleChange(){}; + void TriggerNewTextNotification(const std::wstring_view){}; private: std::optional _triggerScrollDelta; diff --git a/src/host/ScreenBufferRenderTarget.cpp b/src/host/ScreenBufferRenderTarget.cpp index 87f98ce29aa..ed9ebc91608 100644 --- a/src/host/ScreenBufferRenderTarget.cpp +++ b/src/host/ScreenBufferRenderTarget.cpp @@ -110,3 +110,13 @@ void ScreenBufferRenderTarget::TriggerTitleChange() pRenderer->TriggerTitleChange(); } } + +void ScreenBufferRenderTarget::TriggerNewTextNotification(const std::wstring_view newText) +{ + auto* pRenderer = ServiceLocator::LocateGlobals().pRender; + const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); + if (pRenderer != nullptr && pActive == &_owner) + { + pRenderer->TriggerNewTextNotification(newText); + } +} diff --git a/src/host/ScreenBufferRenderTarget.hpp b/src/host/ScreenBufferRenderTarget.hpp index 09a189691c7..7f7588faba4 100644 --- a/src/host/ScreenBufferRenderTarget.hpp +++ b/src/host/ScreenBufferRenderTarget.hpp @@ -39,6 +39,7 @@ class ScreenBufferRenderTarget final : public Microsoft::Console::Render::IRende void TriggerScroll(const COORD* const pcoordDelta) override; void TriggerCircling() override; void TriggerTitleChange() override; + void TriggerNewTextNotification(const std::wstring_view newText) override; private: SCREEN_INFORMATION& _owner; diff --git a/src/renderer/atlas/AtlasEngine.api.cpp b/src/renderer/atlas/AtlasEngine.api.cpp index 2e0db997333..7c49fb99675 100644 --- a/src/renderer/atlas/AtlasEngine.api.cpp +++ b/src/renderer/atlas/AtlasEngine.api.cpp @@ -143,6 +143,11 @@ constexpr HRESULT vec2_narrow(U x, U y, AtlasEngine::vec2& out) noexcept return S_OK; } +[[nodiscard]] HRESULT AtlasEngine::NotifyNewText(const std::wstring_view newText) noexcept +{ + return S_OK; +} + [[nodiscard]] HRESULT AtlasEngine::UpdateFont(const FontInfoDesired& fontInfoDesired, _Out_ FontInfo& fontInfo) noexcept { return UpdateFont(fontInfoDesired, fontInfo, {}, {}); diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 5c417b7386b..74114ba54f7 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -35,6 +35,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept override; [[nodiscard]] HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept override; + [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; [[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override; [[nodiscard]] HRESULT ResetLineTransform() noexcept override; [[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept override; diff --git a/src/renderer/base/RenderEngineBase.cpp b/src/renderer/base/RenderEngineBase.cpp index cba14c72639..ca506b9d2ab 100644 --- a/src/renderer/base/RenderEngineBase.cpp +++ b/src/renderer/base/RenderEngineBase.cpp @@ -36,6 +36,11 @@ HRESULT RenderEngineBase::UpdateTitle(const std::wstring_view newTitle) noexcept return hr; } +HRESULT RenderEngineBase::NotifyNewText(const std::wstring_view /*newText*/) noexcept +{ + return S_FALSE; +} + HRESULT RenderEngineBase::UpdateSoftFont(const gsl::span /*bitPattern*/, const SIZE /*cellSize*/, const size_t /*centeringHint*/) noexcept diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index e58b71fa3f0..66bbcc6b5ad 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -493,6 +493,14 @@ void Renderer::TriggerTitleChange() NotifyPaintFrame(); } +void Renderer::TriggerNewTextNotification(const std::wstring_view newText) +{ + FOREACH_ENGINE(pEngine) + { + LOG_IF_FAILED(pEngine->NotifyNewText(newText)); + } +} + // Routine Description: // - Update the title for a particular engine. // Arguments: diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index b4c1d05ca54..e53afd2a25f 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -54,6 +54,8 @@ namespace Microsoft::Console::Render void TriggerCircling() override; void TriggerTitleChange() override; + void TriggerNewTextNotification(const std::wstring_view newText) override; + void TriggerFontChange(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo); diff --git a/src/renderer/inc/DummyRenderTarget.hpp b/src/renderer/inc/DummyRenderTarget.hpp index e00a22a1646..fb105e599ba 100644 --- a/src/renderer/inc/DummyRenderTarget.hpp +++ b/src/renderer/inc/DummyRenderTarget.hpp @@ -31,4 +31,5 @@ class DummyRenderTarget final : public Microsoft::Console::Render::IRenderTarget void TriggerScroll(const COORD* const /*pcoordDelta*/) override {} void TriggerCircling() override {} void TriggerTitleChange() override {} + void TriggerNewTextNotification(const std::wstring_view) override {} }; diff --git a/src/renderer/inc/IRenderEngine.hpp b/src/renderer/inc/IRenderEngine.hpp index 85be8e90e8c..3d13595aabc 100644 --- a/src/renderer/inc/IRenderEngine.hpp +++ b/src/renderer/inc/IRenderEngine.hpp @@ -69,6 +69,7 @@ namespace Microsoft::Console::Render [[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept = 0; [[nodiscard]] virtual HRESULT InvalidateTitle(std::wstring_view proposedTitle) noexcept = 0; + [[nodiscard]] virtual HRESULT NotifyNewText(const std::wstring_view newText) noexcept = 0; [[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0; [[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0; [[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept = 0; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp index f12f243e1a3..6f32c40bd84 100644 --- a/src/renderer/inc/IRenderTarget.hpp +++ b/src/renderer/inc/IRenderTarget.hpp @@ -45,6 +45,8 @@ namespace Microsoft::Console::Render virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; virtual void TriggerCircling() = 0; virtual void TriggerTitleChange() = 0; + + virtual void TriggerNewTextNotification(const std::wstring_view newText) = 0; }; inline Microsoft::Console::Render::IRenderTarget::~IRenderTarget() {} diff --git a/src/renderer/inc/RenderEngineBase.hpp b/src/renderer/inc/RenderEngineBase.hpp index 6ad59fec343..452dd0cc39f 100644 --- a/src/renderer/inc/RenderEngineBase.hpp +++ b/src/renderer/inc/RenderEngineBase.hpp @@ -38,6 +38,8 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override; + [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; + [[nodiscard]] HRESULT UpdateSoftFont(const gsl::span bitPattern, const SIZE cellSize, const size_t centeringHint) noexcept override; diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index 2d0f1580ae9..0a87ce0e619 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -182,6 +182,18 @@ CATCH_RETURN(); return S_FALSE; } +[[nodiscard]] HRESULT UiaEngine::NotifyNewText(const std::wstring_view newText) noexcept +try +{ + if (!newText.empty()) + { + _newOutput.append(newText); + _newOutput.push_back(L'\n'); + } + return S_OK; +} +CATCH_LOG_RETURN_HR(E_FAIL); + // Routine Description: // - This is unused by this renderer. // Arguments: @@ -207,7 +219,7 @@ CATCH_RETURN(); RETURN_HR_IF(S_FALSE, !_isEnabled); // add more events here - const bool somethingToDo = _selectionChanged || _textBufferChanged || _cursorChanged; + const bool somethingToDo = _selectionChanged || _textBufferChanged || _cursorChanged || !_queuedOutput.empty(); // If there's nothing to do, quick return RETURN_HR_IF(S_FALSE, !somethingToDo); @@ -227,6 +239,34 @@ CATCH_RETURN(); RETURN_HR_IF(S_FALSE, !_isEnabled); RETURN_HR_IF(E_INVALIDARG, !_isPainting); // invalid to end paint when we're not painting + // Snap this now while we're still under lock + // so present can work on the copy while another + // thread might start filling the next "frame" + // worth of text data. + std::swap(_queuedOutput, _newOutput); + _newOutput.clear(); + return S_OK; +} + +// RenderEngineBase defines a WaitUntilCanRender() that sleeps for 8ms to throttle rendering. +// But UiaEngine is never the only engine running. Overriding this function prevents +// us from sleeping 16ms per frame, when the other engine also sleeps for 8ms. +void UiaEngine::WaitUntilCanRender() noexcept +{ +} + +// Routine Description: +// - Used to perform longer running presentation steps outside the lock so the +// other threads can continue. +// - Not currently used by UiaEngine. +// Arguments: +// - +// Return Value: +// - S_FALSE since we do nothing. +[[nodiscard]] HRESULT UiaEngine::Present() noexcept +{ + RETURN_HR_IF(S_FALSE, !_isEnabled); + // Fire UIA Events here if (_selectionChanged) { @@ -252,35 +292,34 @@ CATCH_RETURN(); } CATCH_LOG(); } + try + { + // The speech API is limited to 1000 characters at a time. + // Break up the output into 1000 character chunks to ensure + // the output isn't cut off. + static constexpr size_t sapiLimit{ 1000 }; + const std::wstring_view output{ _queuedOutput }; + for (size_t offset = 0;; offset += sapiLimit) + { + const auto croppedText{ output.substr(offset, sapiLimit) }; + if (croppedText.empty()) + { + break; + } + _dispatcher->NotifyNewOutput(croppedText); + } + } + CATCH_LOG(); _selectionChanged = false; _textBufferChanged = false; _cursorChanged = false; _isPainting = false; + _queuedOutput.clear(); return S_OK; } -// RenderEngineBase defines a WaitUntilCanRender() that sleeps for 8ms to throttle rendering. -// But UiaEngine is never the only engine running. Overriding this function prevents -// us from sleeping 16ms per frame, when the other engine also sleeps for 8ms. -void UiaEngine::WaitUntilCanRender() noexcept -{ -} - -// Routine Description: -// - Used to perform longer running presentation steps outside the lock so the -// other threads can continue. -// - Not currently used by UiaEngine. -// Arguments: -// - -// Return Value: -// - S_FALSE since we do nothing. -[[nodiscard]] HRESULT UiaEngine::Present() noexcept -{ - return S_FALSE; -} - // Routine Description: // - This is currently unused. // Arguments: diff --git a/src/renderer/uia/UiaRenderer.hpp b/src/renderer/uia/UiaRenderer.hpp index 1f5d1ecfe9d..8f1603aeac6 100644 --- a/src/renderer/uia/UiaRenderer.hpp +++ b/src/renderer/uia/UiaRenderer.hpp @@ -47,6 +47,7 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT InvalidateScroll(const COORD* const pcoordDelta) noexcept override; [[nodiscard]] HRESULT InvalidateAll() noexcept override; [[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept override; + [[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override; [[nodiscard]] HRESULT PaintBackground() noexcept override; [[nodiscard]] HRESULT PaintBufferLine(gsl::span const clusters, const COORD coord, const bool fTrimLeft, const bool lineWrapped) noexcept override; [[nodiscard]] HRESULT PaintBufferGridLines(const GridLineSet lines, const COLORREF color, const size_t cchLine, const COORD coordTarget) noexcept override; @@ -70,6 +71,8 @@ namespace Microsoft::Console::Render bool _selectionChanged; bool _textBufferChanged; bool _cursorChanged; + std::wstring _newOutput; + std::wstring _queuedOutput; Microsoft::Console::Types::IUiaEventDispatcher* _dispatcher; diff --git a/src/types/IUiaEventDispatcher.h b/src/types/IUiaEventDispatcher.h index 1d9fc11e8ec..845e5770c69 100644 --- a/src/types/IUiaEventDispatcher.h +++ b/src/types/IUiaEventDispatcher.h @@ -23,5 +23,6 @@ namespace Microsoft::Console::Types virtual void SignalSelectionChanged() = 0; virtual void SignalTextChanged() = 0; virtual void SignalCursorChanged() = 0; + virtual void NotifyNewOutput(std::wstring_view newOutput) = 0; }; } From 5964060b403842a34dee45c15f186e6c04227777 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 14 Mar 2022 14:38:59 +0100 Subject: [PATCH 41/75] AtlasEngine: Reduce shader power draw with explicit branching (#12552) Many articles I read while writing this engine claimed that GPUs can't do branches like CPUs can. One common approach to branching in GPUs is apparently to "mask" out results, a technique called branch predication. The GPU will simply execute all instructions in your shader linearly, but if a branch isn't taken, it'll ignore the computation results. This is unfortunate for our shader, since most branches we have are only very seldomly taken. The cursor for instance is only drawn on a single cell and underlines are seldomly used. But apparently modern GPUs (2010s and later?) are actually entirely capable of branching, _if_ all lanes ("pixels") processed by a wave (""GPU core"") take the same branch. On both my Nvidia GPU (RTX 3080) and Intel iGPU (Intel HD Graphics 530) this change has a positive impact on power draw. Most noticeably on the latter this reduces power draw from 900mW down to 600mW at 60 FPS. ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed It seems to work fine on Intel and Nvidia GPUs. Unfortunately I don't have a AMD GPU to test this on, but I suspect it can't be worse. --- src/renderer/atlas/AtlasEngine.cpp | 2 - src/renderer/atlas/AtlasEngine.h | 2 +- src/renderer/atlas/dwrite.hlsl | 4 +- src/renderer/atlas/shader_ps.hlsl | 73 +++++++++++++++++++----------- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.cpp b/src/renderer/atlas/AtlasEngine.cpp index bd30b04faee..caac790ab78 100644 --- a/src/renderer/atlas/AtlasEngine.cpp +++ b/src/renderer/atlas/AtlasEngine.cpp @@ -791,8 +791,6 @@ void AtlasEngine::_createSwapChain() if (_api.hwnd) { - desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; - if (FAILED(dxgiFactory->CreateSwapChainForHwnd(_r.device.get(), _api.hwnd, &desc, nullptr, nullptr, _r.swapChain.put()))) { // Platform Update for Windows 7: diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 74114ba54f7..11ad2026a43 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -565,7 +565,7 @@ namespace Microsoft::Console::Render alignas(sizeof(u32)) u32 backgroundColor = 0; alignas(sizeof(u32)) u32 cursorColor = 0; alignas(sizeof(u32)) u32 selectionColor = 0; - alignas(sizeof(u32)) u32 useClearType = 0; + alignas(sizeof(u32)) bool useClearType = 0; #pragma warning(suppress : 4324) // 'ConstBuffer': structure was padded due to alignment specifier }; diff --git a/src/renderer/atlas/dwrite.hlsl b/src/renderer/atlas/dwrite.hlsl index 561b395684b..0f5c683bc5b 100644 --- a/src/renderer/atlas/dwrite.hlsl +++ b/src/renderer/atlas/dwrite.hlsl @@ -79,7 +79,7 @@ float4 DWrite_GrayscaleBlend(float4 gammaRatios, float grayscaleEnhancedContrast float3 foregroundStraight = DWrite_UnpremultiplyColor(foregroundColor); float contrastBoost = isThinFont ? 0.5f : 0.0f; float blendEnhancedContrast = contrastBoost + DWrite_ApplyLightOnDarkContrastAdjustment(grayscaleEnhancedContrast, foregroundStraight); - float intensity = DWrite_CalcColorIntensity(foregroundColor.rgb); + float intensity = DWrite_CalcColorIntensity(foregroundStraight); float contrasted = DWrite_EnhanceContrast(glyphAlpha, blendEnhancedContrast); return foregroundColor * DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); } @@ -120,7 +120,7 @@ float4 DWrite_GrayscaleBlend(float4 gammaRatios, float grayscaleEnhancedContrast // overscale (meaning: the glyph is rasterized with 6x the required resolution in the X axis) and thus // only 7 different RGB combinations can exist in this texture (black/white and 5 states in between). // If you wanted to you could just store these in a A8 texture and restore the RGB values in this shader. -float4 DWrite_CleartypeBlend(float4 gammaRatios, float enhancedContrast, bool isThinFont, float4 backgroundColor, float4 foregroundColor, float4 glyphColor) +float4 DWrite_ClearTypeBlend(float4 gammaRatios, float enhancedContrast, bool isThinFont, float4 backgroundColor, float4 foregroundColor, float4 glyphColor) { float3 foregroundStraight = DWrite_UnpremultiplyColor(foregroundColor); float contrastBoost = isThinFont ? 0.5f : 0.0f; diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index a6f74899a90..787959ad736 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -48,7 +48,7 @@ cbuffer ConstBuffer : register(b0) uint backgroundColor; uint cursorColor; uint selectionColor; - uint useClearType; + bool useClearType; }; StructuredBuffer cells : register(t0); Texture2D glyphs : register(t1); @@ -76,18 +76,13 @@ float4 alphaBlendPremultiplied(float4 bottom, float4 top) float4 main(float4 pos: SV_Position): SV_Target // clang-format on { - if (any(pos.xy < viewport.xy) || any(pos.xy >= viewport.zw)) + // We need to fill the entire render target with pixels, but only our "viewport" + // has cells we want to draw. The rest gets treated with the background color. + [branch] if (any(pos.xy < viewport.xy || pos.xy >= viewport.zw)) { return decodeRGBA(backgroundColor); } - // If you want to write test a before/after change simultaneously - // you can turn the image into a checkerboard by writing: - // if ((uint(pos.x) ^ uint(pos.y)) / 4 & 1) { return float4(1, 0, 0, 1); } - // This will generate a checkerboard of 4*4px red squares. - // Of course you wouldn't just return a red color there, but instead - // for instance run your new code and compare it with the old. - uint2 viewportPos = pos.xy - viewport.xy; uint2 cellIndex = viewportPos / cellSize; uint2 cellPos = viewportPos % cellSize; @@ -100,45 +95,69 @@ float4 main(float4 pos: SV_Position): SV_Target // Layer 1 (optional): // Colored cursors are drawn "in between" the background color and the text of a cell. - if ((cell.flags & CellFlags_Cursor) && cursorColor != INVALID_COLOR) + [branch] if (cell.flags & CellFlags_Cursor) { - // The cursor texture is stored at the top-left-most glyph cell. - // Cursor pixels are either entirely transparent or opaque. - // --> We can just use .a as a mask to flip cursor pixels on or off. - color = alphaBlendPremultiplied(color, decodeRGBA(cursorColor) * glyphs[cellPos].a); + [flatten] if (cursorColor != INVALID_COLOR) + { + // The cursor texture is stored at the top-left-most glyph cell. + // Cursor pixels are either entirely transparent or opaque. + // --> We can just use .a as a mask to flip cursor pixels on or off. + color = alphaBlendPremultiplied(color, decodeRGBA(cursorColor) * glyphs[cellPos].a); + } } // Layer 2: // Step 1: Underlines - if ((cell.flags & CellFlags_Underline) && cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y) + [branch] if (cell.flags & CellFlags_Underline) { - color = alphaBlendPremultiplied(color, fg); + [flatten] if (cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y) + { + color = alphaBlendPremultiplied(color, fg); + } } - if ((cell.flags & CellFlags_UnderlineDotted) && cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y && (viewportPos.x / (underlinePos.y - underlinePos.x) & 3) == 0) + [branch] if (cell.flags & CellFlags_UnderlineDotted) { - color = alphaBlendPremultiplied(color, fg); + [flatten] if (cellPos.y >= underlinePos.x && cellPos.y < underlinePos.y && (viewportPos.x / (underlinePos.y - underlinePos.x) & 3) == 0) + { + color = alphaBlendPremultiplied(color, fg); + } } // Step 2: The cell's glyph, potentially drawn in the foreground color { float4 glyph = glyphs[decodeU16x2(cell.glyphPos) + cellPos]; - if (cell.flags & CellFlags_ColoredGlyph) + [branch] if (cell.flags & CellFlags_ColoredGlyph) { color = alphaBlendPremultiplied(color, glyph); } - else if (useClearType) - { - color = DWrite_CleartypeBlend(gammaRatios, enhancedContrast, false, color, fg, glyph); - } else { - color = alphaBlendPremultiplied(color, DWrite_GrayscaleBlend(gammaRatios, enhancedContrast, false, fg, glyph.a)); + float3 foregroundStraight = DWrite_UnpremultiplyColor(fg); + float blendEnhancedContrast = DWrite_ApplyLightOnDarkContrastAdjustment(enhancedContrast, foregroundStraight); + + [branch] if (useClearType) + { + // See DWrite_ClearTypeBlend + float3 contrasted = DWrite_EnhanceContrast3(glyph.rgb, blendEnhancedContrast); + float3 alphaCorrected = DWrite_ApplyAlphaCorrection3(contrasted, foregroundStraight, gammaRatios); + color = float4(lerp(color.rgb, foregroundStraight, alphaCorrected * fg.a), 1.0f); + } + else + { + // See DWrite_GrayscaleBlend + float intensity = DWrite_CalcColorIntensity(foregroundStraight); + float contrasted = DWrite_EnhanceContrast(glyph.a, blendEnhancedContrast); + color = fg * DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); + } } } // Step 3: Lines, but not "under"lines - if ((cell.flags & CellFlags_Strikethrough) && cellPos.y >= strikethroughPos.x && cellPos.y < strikethroughPos.y) + [branch] if (cell.flags & CellFlags_Strikethrough) { - color = alphaBlendPremultiplied(color, fg); + [flatten] if (cellPos.y >= strikethroughPos.x && cellPos.y < strikethroughPos.y) + { + color = alphaBlendPremultiplied(color, fg); + } } // Layer 3 (optional): @@ -153,7 +172,7 @@ float4 main(float4 pos: SV_Position): SV_Target // Layer 4: // The current selection is drawn semi-transparent on top. - if (cell.flags & CellFlags_Selected) + [branch] if (cell.flags & CellFlags_Selected) { color = alphaBlendPremultiplied(color, decodeRGBA(selectionColor)); } From f0c2ef361a0354e0c0a34117a99985cef1ba7804 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 14 Mar 2022 16:04:18 +0100 Subject: [PATCH 42/75] Fix broken TestHostApp build (#12676) This regressed recently in cfaa315. Initially I tried to migrate TestHostApp to C++/WinRT, but due to the unbearable compile times I've reverted it back to C++/CX (>20x difference). Unfortunately I then forgot to fix the underlying issue before submitting a PR. ## PR Checklist * [x] Closes #12673 * [x] I work here * [x] Tests added/passed --- .../LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index a97fe8064b4..896547f5ffd 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -51,7 +51,8 @@ - + + stdcpp17 %(AdditionalOptions) /Zc:twoPhase- 4453;%(DisableSpecificWarnings) %(AdditionalIncludeDirectories);$(IntermediateOutputPath) From 9c6ec75082d23873673d5f8cf20b1e0b94d01358 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Mon, 14 Mar 2022 17:48:29 +0100 Subject: [PATCH 43/75] Fix "Element not found" error during settings loading (#12687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes a stray exception during settings loading, caused by a failure to obtain the app's extension catalog. ## PR Checklist * [x] Closes #12305 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed I'm unable to replicate the issue. ❌ However an error log was provided in #12305 with which the function causing the exception could be determined. --- .github/actions/spelling/expect/expect.txt | 1 + .../CascadiaSettingsSerialization.cpp | 29 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index b08ed0b9011..0ae5cecb6a7 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2575,6 +2575,7 @@ uuidv UVWX UVWXY UWA +UWAs uwp uxtheme vals diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index 483f74c6cb6..e008c5e6d61 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -27,6 +27,8 @@ #include "DefaultTerminal.h" #include "FileUtils.h" +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::ApplicationModel::AppExtensions; using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; @@ -235,10 +237,29 @@ void SettingsLoader::FindFragmentsAndMergeIntoUserSettings() } } - // Search through app extensions - // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings" - const auto catalog = winrt::Windows::ApplicationModel::AppExtensions::AppExtensionCatalog::Open(AppExtensionHostName); - const auto extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + // Search through app extensions. + // Gets the catalog of extensions with the name "com.microsoft.windows.terminal.settings". + // + // GH#12305: Open() can throw an 0x80070490 "Element not found.". + // It's unclear to me under which circumstances this happens as no one on the team + // was able to reproduce the user's issue, even if the application was run unpackaged. + // The error originates from `CallerIdentity::GetCallingProcessAppId` which returns E_NOT_SET. + // A comment can be found, reading: + // > Gets the "strong" AppId from the process token. This works for UWAs and Centennial apps, + // > strongly named processes where the AppId is stored securely in the process token. [...] + // > E_NOT_SET is returned for processes without strong AppIds. + IVectorView extensions; + try + { + const auto catalog = AppExtensionCatalog::Open(AppExtensionHostName); + extensions = extractValueFromTaskWithoutMainThreadAwait(catalog.FindAllAsync()); + } + CATCH_LOG(); + + if (!extensions) + { + return; + } for (const auto& ext : extensions) { From a5194b0c44851384992c6250ba6afbe535358546 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Mon, 14 Mar 2022 13:52:55 -0500 Subject: [PATCH 44/75] Fix the appxbundle version to be the appx version (#12691) The store did not like when I uploaded two Windows Terminal packages built on the same date, because the appxbundle version defaulted to YYYY.MMDD.something. There was a risk that using *this* version number will fail because it is thousands of numbers less than "2022". We'll have to see if the store rolls it out properly. I cannot find any documentation on how the store rolls out *bundle* versions (it is very aware of .appx versions...). A local test with 1.14.72x (Preview) published via the store seems to have worked. --- build/pipelines/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml index 69c9b753952..bbe1d0570b7 100644 --- a/build/pipelines/release.yml +++ b/build/pipelines/release.yml @@ -320,7 +320,7 @@ jobs: displayName: Create WindowsTerminal*.msixbundle inputs: filePath: build\scripts\Create-AppxBundle.ps1 - arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion 0.0.0.0 -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" + arguments: -InputPath "$(System.ArtifactsDirectory)" -ProjectName CascadiaPackage -BundleVersion $(XES_APPXMANIFESTVERSION) -OutputPath "$(System.ArtifactsDirectory)\Microsoft.WindowsTerminal_$(TerminalTargetWindowsVersion)_$(XES_APPXMANIFESTVERSION)_8wekyb3d8bbwe.msixbundle" - task: EsrpCodeSigning@1 displayName: Submit *.msixbundle to ESRP for code signing inputs: From bf551db44ac6fd9ca7cd56482e46231cb4f86496 Mon Sep 17 00:00:00 2001 From: David Machaj <46852402+dmachaj@users.noreply.github.com> Date: Mon, 14 Mar 2022 12:34:26 -0700 Subject: [PATCH 45/75] Update .NET from unsupported 4.5 to 4.8 to silence VS2022 (#12674) ## Summary of the Pull Request Closes #12670 Visual Studio 2022 offered to upgrade .NET Framework from an unsupported 4.5 to a supported 4.8. I let it do its thing (and undid a number of whitespace-only changes that aren't necessary). The project still builds after this. The UIA tests fail to run but I think that is preexisting and will be filing a new issue momentarily. ## PR Checklist * [x] Closes #12670 * [x] Tests added/passed ## Validation Steps Performed The solution compiles in VS2022 (except for #12673). --- .../WindowsTerminal.UIA.Tests.csproj | 3 ++- .../WindowsTerminal_UIATests/app.config | 5 +++- src/host/ft_uia/Host.Tests.UIA.csproj | 3 ++- src/host/ft_uia/app.config | 5 +++- src/tools/vtapp/App.config | 2 +- src/tools/vtapp/VTApp.csproj | 24 ++++++++++++++++--- 6 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj b/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj index 50e93883f0c..1437278da66 100644 --- a/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj +++ b/src/cascadia/WindowsTerminal_UIATests/WindowsTerminal.UIA.Tests.csproj @@ -6,7 +6,7 @@ Properties WindowsTerminal.UIA.Tests WindowsTerminal.UIA.Tests - v4.5 + v4.8 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -19,6 +19,7 @@ 4 + ARM64 diff --git a/src/cascadia/WindowsTerminal_UIATests/app.config b/src/cascadia/WindowsTerminal_UIATests/app.config index b92dd5ef316..422f8f0f4d8 100644 --- a/src/cascadia/WindowsTerminal_UIATests/app.config +++ b/src/cascadia/WindowsTerminal_UIATests/app.config @@ -12,4 +12,7 @@ - \ No newline at end of file + + + + diff --git a/src/host/ft_uia/Host.Tests.UIA.csproj b/src/host/ft_uia/Host.Tests.UIA.csproj index 0b920110dfa..1f71e6a4919 100644 --- a/src/host/ft_uia/Host.Tests.UIA.csproj +++ b/src/host/ft_uia/Host.Tests.UIA.csproj @@ -6,7 +6,7 @@ Properties Conhost.UIA.Tests Conhost.UIA.Tests - v4.5 + v4.8 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -19,6 +19,7 @@ 4 + ARM64 diff --git a/src/host/ft_uia/app.config b/src/host/ft_uia/app.config index b92dd5ef316..422f8f0f4d8 100644 --- a/src/host/ft_uia/app.config +++ b/src/host/ft_uia/app.config @@ -12,4 +12,7 @@ - \ No newline at end of file + + + + diff --git a/src/tools/vtapp/App.config b/src/tools/vtapp/App.config index 2c307fa3148..5ffd8f8ee21 100644 --- a/src/tools/vtapp/App.config +++ b/src/tools/vtapp/App.config @@ -1,6 +1,6 @@ - + diff --git a/src/tools/vtapp/VTApp.csproj b/src/tools/vtapp/VTApp.csproj index b896821f7bd..bac290878bb 100644 --- a/src/tools/vtapp/VTApp.csproj +++ b/src/tools/vtapp/VTApp.csproj @@ -7,7 +7,7 @@ Properties VTApp VTApp - v4.0 + v4.8 512 true @@ -35,6 +35,24 @@ true TRACE + + false + + + false + + + false + + + false + + + false + + + false + @@ -58,11 +76,11 @@ - - \ No newline at end of file + From ce6ce50e7e9eeb2d7377527d9512da9d43674bc0 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Mon, 14 Mar 2022 21:59:48 +0000 Subject: [PATCH 46/75] Eliminate the IRenderTarget interface (#12568) ## Summary of the Pull Request The purpose of the `IRenderTarget` interface was to support the concept of multiple buffers in conhost. When a text buffer needed to trigger a redraw, the render target implementation would be responsible for deciding whether to forward that redraw to the renderer, depending on whether the buffer was active or not. This PR instead introduces a flag in the `TextBuffer` class to track whether it is active or not, and it can then simply check the flag to decide whether it needs to trigger a redraw or not. That way it can work with the `Renderer` directly, and we can avoid a bunch of virtual calls for the various redraw methods. ## PR Checklist * [x] Closes #12551 * [x] CLA signed. * [ ] Tests added/passed * [ ] Documentation updated. * [ ] Schema updated. * [x] I've discussed this with core contributors already. Issue number where discussion took place: #12551 ## Detailed Description of the Pull Request / Additional comments Anywhere that had previously been getting an `IRenderTarget` from the `TextBuffer` class in order to trigger a redraw, will now just call the `TriggerRedraw` method on the `TextBuffer` class itself, since that will handle the active check which used to be the responsibility of the render target. All the redraw methods that were in `IRenderTarget` are now exposed in `TextBuffer` instead. For this to work, though, the `Renderer` needed to be available before the `TextBuffer` could be constructed, which required a change in the conhost initialization order. In the `ConsoleInitializeConnectInfo` function, I had to move the `Renderer` initialization up so it could be constructed before the call to `SetUpConsole` (which initializes the buffer). Once both are ready, the `Renderer::EnablePainting` method is called to start the render thread. The other catch is that the renderer used to setup its initial viewport in the constructor, but with the new initialization order, the viewport size would not be known at that point. I've addressed that problem by moving the viewport initialization into the `EnablePainting` method, since that will only be called after the buffer is setup. ## Validation Steps Performed The changes in architecture required a number of tweaks to the unit tests. Some were using a dummy `IRenderTarget` implementation which now needed to be replaced with a full `Renderer` (albeit with mostly null parameters). In the case of the scroll test, a mock `IRenderTarget` was used to track the scroll delta, which now had to be replaced with a mock `RenderEngine` instead. Some tests previously relied on having just a `TextBuffer` but without a `Renderer`, and now they require both. And tests that were constructing the `TextBuffer` and `Renderer` themselves had to be updated to use the new initialization order, i.e. with the renderer constructed first. Semantically, though, the tests still essentially work the same way as they did before, and they all still pass. --- src/buffer/out/cursor.cpp | 2 +- src/buffer/out/textBuffer.cpp | 100 ++++++++++---- src/buffer/out/textBuffer.hpp | 27 +++- src/buffer/out/ut_textbuffer/ReflowTests.cpp | 10 +- src/cascadia/TerminalControl/ControlCore.cpp | 3 +- src/cascadia/TerminalCore/Terminal.cpp | 29 +++-- src/cascadia/TerminalCore/Terminal.hpp | 5 +- src/cascadia/TerminalCore/TerminalApi.cpp | 4 +- .../TerminalCore/TerminalSelection.cpp | 2 +- .../TerminalCore/terminalrenderdata.cpp | 2 +- .../ConptyRoundtripTests.cpp | 12 +- .../UnitTests_TerminalCore/InputTest.cpp | 6 +- .../ScreenSizeLimitsTest.cpp | 25 ++-- .../UnitTests_TerminalCore/ScrollTest.cpp | 72 +++++++---- .../UnitTests_TerminalCore/SelectionTest.cpp | 90 ++++++------- .../TerminalApiTest.cpp | 42 +++--- .../TerminalBufferTests.cpp | 8 +- src/host/CursorBlinker.cpp | 2 +- src/host/ScreenBufferRenderTarget.cpp | 122 ------------------ src/host/ScreenBufferRenderTarget.hpp | 46 ------- src/host/_stream.cpp | 2 +- src/host/consoleInformation.cpp | 12 +- src/host/host-common.vcxitems | 2 - src/host/lib/hostlib.vcxproj.filters | 6 - src/host/output.cpp | 10 +- src/host/screenInfo.cpp | 24 +--- src/host/screenInfo.hpp | 5 - src/host/server.h | 1 + src/host/sources.inc | 1 - src/host/srvinit.cpp | 24 ++-- src/host/ut_host/ConptyOutputTests.cpp | 4 +- src/host/ut_host/ScreenBufferTests.cpp | 8 +- src/host/ut_host/SearchTests.cpp | 2 + src/host/ut_host/TextBufferTests.cpp | 35 ++--- src/inc/test/CommonState.hpp | 51 +++++++- src/renderer/base/RenderSettings.cpp | 10 +- src/renderer/base/lib/base.vcxproj | 1 - src/renderer/base/lib/base.vcxproj.filters | 3 - src/renderer/base/renderer.cpp | 15 ++- src/renderer/base/renderer.hpp | 28 ++-- src/renderer/inc/DummyRenderTarget.hpp | 35 ----- src/renderer/inc/DummyRenderer.hpp | 25 ++++ src/renderer/inc/IRenderTarget.hpp | 54 -------- src/renderer/inc/RenderSettings.hpp | 2 +- src/types/IBaseData.h | 1 + 45 files changed, 435 insertions(+), 535 deletions(-) delete mode 100644 src/host/ScreenBufferRenderTarget.cpp delete mode 100644 src/host/ScreenBufferRenderTarget.hpp delete mode 100644 src/renderer/inc/DummyRenderTarget.hpp create mode 100644 src/renderer/inc/DummyRenderer.hpp delete mode 100644 src/renderer/inc/IRenderTarget.hpp diff --git a/src/buffer/out/cursor.cpp b/src/buffer/out/cursor.cpp index 4a3aba783ce..b6d003a3b88 100644 --- a/src/buffer/out/cursor.cpp +++ b/src/buffer/out/cursor.cpp @@ -187,7 +187,7 @@ void Cursor::_RedrawCursorAlways() noexcept { try { - _parentBuffer.GetRenderTarget().TriggerRedrawCursor(&_cPosition); + _parentBuffer.TriggerRedrawCursor(_cPosition); } CATCH_LOG(); } diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index a7df90e8c76..66b9980d26c 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -6,6 +6,7 @@ #include "textBuffer.hpp" #include "CharRow.hpp" +#include "../renderer/base/renderer.hpp" #include "../types/inc/utils.hpp" #include "../types/inc/convert.hpp" #include "../../types/inc/Utf16Parser.hpp" @@ -21,23 +22,26 @@ using PointTree = interval_tree::IntervalTree; // Routine Description: // - Creates a new instance of TextBuffer // Arguments: -// - fontInfo - The font to use for this text buffer as specified in the global font cache // - screenBufferSize - The X by Y dimensions of the new screen buffer -// - fill - Uses the .Attributes property to decide which default color to apply to all text in this buffer +// - defaultAttributes - The attributes with which the buffer will be initialized // - cursorSize - The height of the cursor within this buffer +// - isActiveBuffer - Whether this is the currently active buffer +// - renderer - The renderer to use for triggering a redraw // Return Value: // - constructed object // Note: may throw exception TextBuffer::TextBuffer(const COORD screenBufferSize, const TextAttribute defaultAttributes, const UINT cursorSize, - Microsoft::Console::Render::IRenderTarget& renderTarget) : + const bool isActiveBuffer, + Microsoft::Console::Render::Renderer& renderer) : _firstRow{ 0 }, _currentAttributes{ defaultAttributes }, _cursor{ cursorSize, *this }, _storage{}, _unicodeStorage{}, - _renderTarget{ renderTarget }, + _isActiveBuffer{ isActiveBuffer }, + _renderer{ renderer }, _size{}, _currentHyperlinkId{ 1 }, _currentPatternId{ 0 } @@ -387,7 +391,7 @@ OutputCellIterator TextBuffer::WriteLine(const OutputCellIterator givenIt, // Take the cell distance written and notify that it needs to be repainted. const auto written = newIt.GetCellDistance(givenIt); const Viewport paint = Viewport::FromDimensions(target, { gsl::narrow(written), 1 }); - _NotifyPaint(paint); + TriggerRedraw(paint); return newIt; } @@ -556,7 +560,10 @@ bool TextBuffer::IncrementCircularBuffer(const bool inVtMode) { // FirstRow is at any given point in time the array index in the circular buffer that corresponds // to the logical position 0 in the window (cursor coordinates and all other coordinates). - _renderTarget.TriggerCircling(); + if (_isActiveBuffer) + { + _renderer.TriggerCircling(); + } // Prune hyperlinks to delete obsolete references _PruneHyperlinks(); @@ -825,7 +832,7 @@ void TextBuffer::SetCurrentLineRendition(const LineRendition lineRendition) // We also need to make sure the cursor is clamped within the new width. GetCursor().SetPosition(ClampPositionWithinLine(cursorPosition)); } - _NotifyPaint(Viewport::FromDimensions({ 0, rowIndex }, { GetSize().Width(), 1 })); + TriggerRedraw(Viewport::FromDimensions({ 0, rowIndex }, { GetSize().Width(), 1 })); } } @@ -953,6 +960,69 @@ UnicodeStorage& TextBuffer::GetUnicodeStorage() noexcept return _unicodeStorage; } +void TextBuffer::SetAsActiveBuffer(const bool isActiveBuffer) noexcept +{ + _isActiveBuffer = isActiveBuffer; +} + +bool TextBuffer::IsActiveBuffer() const noexcept +{ + return _isActiveBuffer; +} + +Microsoft::Console::Render::Renderer& TextBuffer::GetRenderer() noexcept +{ + return _renderer; +} + +void TextBuffer::TriggerRedraw(const Viewport& viewport) +{ + if (_isActiveBuffer) + { + _renderer.TriggerRedraw(viewport); + } +} + +void TextBuffer::TriggerRedrawCursor(const COORD position) +{ + if (_isActiveBuffer) + { + _renderer.TriggerRedrawCursor(&position); + } +} + +void TextBuffer::TriggerRedrawAll() +{ + if (_isActiveBuffer) + { + _renderer.TriggerRedrawAll(); + } +} + +void TextBuffer::TriggerScroll() +{ + if (_isActiveBuffer) + { + _renderer.TriggerScroll(); + } +} + +void TextBuffer::TriggerScroll(const COORD delta) +{ + if (_isActiveBuffer) + { + _renderer.TriggerScroll(&delta); + } +} + +void TextBuffer::TriggerNewTextNotification(const std::wstring_view newText) +{ + if (_isActiveBuffer) + { + _renderer.TriggerNewTextNotification(newText); + } +} + // Routine Description: // - Method to help refresh all the Row IDs after manipulating the row // by shuffling pointers around. @@ -989,11 +1059,6 @@ void TextBuffer::_RefreshRowIDs(std::optional newRowWidth) _unicodeStorage.Remap(rowMap, newRowWidth); } -void TextBuffer::_NotifyPaint(const Viewport& viewport) const -{ - _renderTarget.TriggerRedraw(viewport); -} - // Routine Description: // - Retrieves the first row from the underlying buffer. // Arguments: @@ -1026,17 +1091,6 @@ ROW& TextBuffer::_GetPrevRowNoWrap(const ROW& Row) return _storage.at(prevRowIndex); } -// Method Description: -// - Retrieves this buffer's current render target. -// Arguments: -// - -// Return Value: -// - This buffer's current render target. -Microsoft::Console::Render::IRenderTarget& TextBuffer::GetRenderTarget() noexcept -{ - return _renderTarget; -} - // Method Description: // - get delimiter class for buffer cell position // - used for double click selection and uia word navigation diff --git a/src/buffer/out/textBuffer.hpp b/src/buffer/out/textBuffer.hpp index fe87ee8822d..6175d37d2db 100644 --- a/src/buffer/out/textBuffer.hpp +++ b/src/buffer/out/textBuffer.hpp @@ -60,7 +60,10 @@ filling in the last row, and updating the screen. #include "../buffer/out/textBufferCellIterator.hpp" #include "../buffer/out/textBufferTextIterator.hpp" -#include "../renderer/inc/IRenderTarget.hpp" +namespace Microsoft::Console::Render +{ + class Renderer; +} class TextBuffer final { @@ -68,7 +71,8 @@ class TextBuffer final TextBuffer(const COORD screenBufferSize, const TextAttribute defaultAttributes, const UINT cursorSize, - Microsoft::Console::Render::IRenderTarget& renderTarget); + const bool isActiveBuffer, + Microsoft::Console::Render::Renderer& renderer); TextBuffer(const TextBuffer& a) = delete; // Used for duplicating properties to another text buffer @@ -139,7 +143,17 @@ class TextBuffer final const UnicodeStorage& GetUnicodeStorage() const noexcept; UnicodeStorage& GetUnicodeStorage() noexcept; - Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept; + void SetAsActiveBuffer(const bool isActiveBuffer) noexcept; + bool IsActiveBuffer() const noexcept; + + Microsoft::Console::Render::Renderer& GetRenderer() noexcept; + + void TriggerRedraw(const Microsoft::Console::Types::Viewport& viewport); + void TriggerRedrawCursor(const COORD position); + void TriggerRedrawAll(); + void TriggerScroll(); + void TriggerScroll(const COORD delta); + void TriggerNewTextNotification(const std::wstring_view newText); const COORD GetWordStart(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional limitOptional = std::nullopt) const; const COORD GetWordEnd(const COORD target, const std::wstring_view wordDelimiters, bool accessibilityMode = false, std::optional limitOptional = std::nullopt) const; @@ -213,14 +227,15 @@ class TextBuffer final // storage location for glyphs that can't fit into the buffer normally UnicodeStorage _unicodeStorage; + bool _isActiveBuffer; + Microsoft::Console::Render::Renderer& _renderer; + std::unordered_map _hyperlinkMap; std::unordered_map _hyperlinkCustomIdMap; uint16_t _currentHyperlinkId; void _RefreshRowIDs(std::optional newRowWidth); - Microsoft::Console::Render::IRenderTarget& _renderTarget; - void _SetFirstRowIndex(const SHORT FirstRowIndex) noexcept; COORD _GetPreviousFromCursor() const; @@ -228,8 +243,6 @@ class TextBuffer final void _SetWrapOnCurrentRow(); void _AdjustWrapOnCurrentRow(const bool fSet); - void _NotifyPaint(const Microsoft::Console::Types::Viewport& viewport) const; - // Assist with maintaining proper buffer state for Double Byte character sequences bool _PrepareForDoubleByteSequence(const DbcsAttribute dbcsAttribute); bool _AssertValidDoubleByteSequence(const DbcsAttribute dbcsAttribute); diff --git a/src/buffer/out/ut_textbuffer/ReflowTests.cpp b/src/buffer/out/ut_textbuffer/ReflowTests.cpp index 645cc347fed..6c567ad017d 100644 --- a/src/buffer/out/ut_textbuffer/ReflowTests.cpp +++ b/src/buffer/out/ut_textbuffer/ReflowTests.cpp @@ -6,7 +6,7 @@ #include "../../inc/consoletaeftemplates.hpp" #include "../textBuffer.hpp" -#include "../../renderer/inc/DummyRenderTarget.hpp" +#include "../../renderer/inc/DummyRenderer.hpp" #include "../../types/inc/Utf16Parser.hpp" #include "../../types/inc/GlyphWidth.hpp" @@ -732,10 +732,10 @@ class ReflowTests { TEST_CLASS(ReflowTests); - static DummyRenderTarget target; + static DummyRenderer renderer; static std::unique_ptr _textBufferFromTestBuffer(const TestBuffer& testBuffer) { - auto buffer = std::make_unique(testBuffer.size, TextAttribute{ 0x7 }, 0, target); + auto buffer = std::make_unique(testBuffer.size, TextAttribute{ 0x7 }, 0, false, renderer); size_t i{}; for (const auto& testRow : testBuffer.rows) @@ -773,7 +773,7 @@ class ReflowTests static std::unique_ptr _textBufferByReflowingTextBuffer(TextBuffer& originalBuffer, const COORD newSize) { - auto buffer = std::make_unique(newSize, TextAttribute{ 0x7 }, 0, target); + auto buffer = std::make_unique(newSize, TextAttribute{ 0x7 }, 0, false, renderer); TextBuffer::Reflow(originalBuffer, *buffer, std::nullopt, std::nullopt); return buffer; } @@ -853,4 +853,4 @@ class ReflowTests } }; -DummyRenderTarget ReflowTests::target{}; +DummyRenderer ReflowTests::renderer{}; diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 6bca14aa18c..21a71077389 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -1397,9 +1397,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation { auto lock = _terminal->LockForWriting(); - auto& renderTarget = *_renderer; auto& renderSettings = _terminal->GetRenderSettings(); - renderSettings.ToggleBlinkRendition(renderTarget); + renderSettings.ToggleBlinkRendition(*_renderer); } void ControlCore::BlinkCursor() diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index 073d5c71273..4b84c73584a 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -77,7 +77,7 @@ Terminal::Terminal() : _renderSettings.SetColorAlias(ColorAlias::DefaultBackground, TextColor::DEFAULT_BACKGROUND, RGB(0, 0, 0)); } -void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget& renderTarget) +void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, Renderer& renderer) { _mutableViewport = Viewport::FromDimensions({ 0, 0 }, viewportSize); _scrollbackLines = scrollbackLines; @@ -85,22 +85,22 @@ void Terminal::Create(COORD viewportSize, SHORT scrollbackLines, IRenderTarget& Utils::ClampToShortMax(viewportSize.Y + scrollbackLines, 1) }; const TextAttribute attr{}; const UINT cursorSize = 12; - _buffer = std::make_unique(bufferSize, attr, cursorSize, renderTarget); + _buffer = std::make_unique(bufferSize, attr, cursorSize, true, renderer); } // Method Description: // - Initializes the Terminal from the given set of settings. // Arguments: // - settings: the set of CoreSettings we need to use to initialize the terminal -// - renderTarget: A render target the terminal can use for paint invalidation. +// - renderer: the Renderer that the terminal can use for paint invalidation. void Terminal::CreateFromSettings(ICoreSettings settings, - IRenderTarget& renderTarget) + Renderer& renderer) { const COORD viewportSize{ Utils::ClampToShortMax(settings.InitialCols(), 1), Utils::ClampToShortMax(settings.InitialRows(), 1) }; // TODO:MSFT:20642297 - Support infinite scrollback here, if HistorySize is -1 - Create(viewportSize, Utils::ClampToShortMax(settings.HistorySize(), 0), renderTarget); + Create(viewportSize, Utils::ClampToShortMax(settings.HistorySize(), 0), renderer); UpdateSettings(settings); } @@ -269,7 +269,8 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) newTextBuffer = std::make_unique(bufferSize, TextAttribute{}, 0, // temporarily set size to 0 so it won't render. - _buffer->GetRenderTarget()); + _buffer->IsActiveBuffer(), + _buffer->GetRenderer()); newTextBuffer->GetCursor().StartDeferDrawing(); @@ -419,7 +420,7 @@ void Terminal::UpdateAppearance(const ICoreAppearance& appearance) // GH#5029 - make sure to InvalidateAll here, so that we'll paint the entire visible viewport. try { - _buffer->GetRenderTarget().TriggerRedrawAll(); + _buffer->TriggerRedrawAll(); } CATCH_LOG(); _NotifyScrollEvent(); @@ -731,7 +732,7 @@ void Terminal::_InvalidateFromCoords(const COORD start, const COORD end) if (start.Y == end.Y) { SMALL_RECT region{ start.X, start.Y, end.X, end.Y }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + _buffer->TriggerRedraw(Viewport::FromInclusive(region)); } else { @@ -739,18 +740,18 @@ void Terminal::_InvalidateFromCoords(const COORD start, const COORD end) // invalidate the first line SMALL_RECT region{ start.X, start.Y, gsl::narrow(rowSize - 1), gsl::narrow(start.Y) }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + _buffer->TriggerRedraw(Viewport::FromInclusive(region)); if ((end.Y - start.Y) > 1) { // invalidate the lines in between the first and last line region = SMALL_RECT{ 0, start.Y + 1, gsl::narrow(rowSize - 1), gsl::narrow(end.Y - 1) }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + _buffer->TriggerRedraw(Viewport::FromInclusive(region)); } // invalidate the last line region = SMALL_RECT{ 0, end.Y, end.X, end.Y }; - _buffer->GetRenderTarget().TriggerRedraw(Viewport::FromInclusive(region)); + _buffer->TriggerRedraw(Viewport::FromInclusive(region)); } } @@ -1005,7 +1006,7 @@ void Terminal::_WriteBuffer(const std::wstring_view& stringView) // Notify UIA of new text. // It's important to do this here instead of in TextBuffer, because here you have access to the entire line of text, // whereas TextBuffer writes it one character at a time via the OutputCellIterator. - _buffer->GetRenderTarget().TriggerNewTextNotification(stringView); + _buffer->TriggerNewTextNotification(stringView); cursor.EndDeferDrawing(); } @@ -1110,7 +1111,7 @@ void Terminal::_AdjustCursorPosition(const COORD proposedPosition) // That didn't change the viewport and therefore the TriggerScroll(void) // method can't detect the delta on its own. COORD delta{ 0, gsl::narrow_cast(-rowsPushedOffTopOfBuffer) }; - _buffer->GetRenderTarget().TriggerScroll(&delta); + _buffer->TriggerScroll(delta); } } @@ -1129,7 +1130,7 @@ void Terminal::UserScrollViewport(const int viewTop) // We can use the void variant of TriggerScroll here because // we adjusted the viewport so it can detect the difference // from the previous frame drawn. - _buffer->GetRenderTarget().TriggerScroll(); + _buffer->TriggerScroll(); } int Terminal::GetScrollOffset() noexcept diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index 5a6d328ecf8..a8b67233396 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -8,6 +8,7 @@ #include "../../inc/DefaultSettings.h" #include "../../buffer/out/textBuffer.hpp" #include "../../types/inc/sgrStack.hpp" +#include "../../renderer/inc/IRenderData.hpp" #include "../../renderer/inc/RenderSettings.hpp" #include "../../terminal/parser/StateMachine.hpp" #include "../../terminal/input/terminalInput.hpp" @@ -67,10 +68,10 @@ class Microsoft::Terminal::Core::Terminal final : void Create(COORD viewportSize, SHORT scrollbackLines, - Microsoft::Console::Render::IRenderTarget& renderTarget); + Microsoft::Console::Render::Renderer& renderer); void CreateFromSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings, - Microsoft::Console::Render::IRenderTarget& renderTarget); + Microsoft::Console::Render::Renderer& renderer); void UpdateSettings(winrt::Microsoft::Terminal::Core::ICoreSettings settings); void UpdateAppearance(const winrt::Microsoft::Terminal::Core::ICoreAppearance& appearance); diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 6e89bce460a..592ce52d540 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -340,7 +340,7 @@ void Terminal::SetColorTableEntry(const size_t tableIndex, const COLORREF color) } // Repaint everything - the colors might have changed - _buffer->GetRenderTarget().TriggerRedrawAll(); + _buffer->TriggerRedrawAll(); } // Method Description: @@ -416,7 +416,7 @@ void Terminal::SetRenderMode(const RenderSettings::Mode mode, const bool enabled _renderSettings.SetRenderMode(mode, enabled); // Repaint everything - the colors will have changed - _buffer->GetRenderTarget().TriggerRedrawAll(); + _buffer->TriggerRedrawAll(); } void Terminal::EnableXtermBracketedPasteMode(const bool enabled) diff --git a/src/cascadia/TerminalCore/TerminalSelection.cpp b/src/cascadia/TerminalCore/TerminalSelection.cpp index 7b7fec12dba..c0b20b64d23 100644 --- a/src/cascadia/TerminalCore/TerminalSelection.cpp +++ b/src/cascadia/TerminalCore/TerminalSelection.cpp @@ -325,7 +325,7 @@ void Terminal::UpdateSelection(SelectionDirection direction, SelectionExpansion _scrollOffset -= amtBelowView; } _NotifyScrollEvent(); - _buffer->GetRenderTarget().TriggerScroll(); + _buffer->TriggerScroll(); } } diff --git a/src/cascadia/TerminalCore/terminalrenderdata.cpp b/src/cascadia/TerminalCore/terminalrenderdata.cpp index a085a3b0548..212551ae21a 100644 --- a/src/cascadia/TerminalCore/terminalrenderdata.cpp +++ b/src/cascadia/TerminalCore/terminalrenderdata.cpp @@ -174,7 +174,7 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd) if (notifyScrollChange) { - _buffer->GetRenderTarget().TriggerScroll(); + _buffer->TriggerScroll(); _NotifyScrollEvent(); } diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index 0d7f72f1c6f..f8b537bc731 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -12,7 +12,7 @@ #include "../../types/inc/Viewport.hpp" #include "../../types/inc/convert.hpp" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "../../renderer/base/Renderer.hpp" #include "../../renderer/vt/Xterm256Engine.hpp" #include "../../renderer/vt/XtermEngine.hpp" @@ -87,7 +87,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final { // STEP 1: Set up the Terminal term = std::make_unique(); - term->Create({ TerminalViewWidth, TerminalViewHeight }, 100, emptyRT); + emptyRenderer = std::make_unique(term.get()); + term->Create({ TerminalViewWidth, TerminalViewHeight }, 100, *emptyRenderer); // STEP 2: Set up the Conpty @@ -100,6 +101,8 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final gci.SetFillAttribute(0x07); // DARK_WHITE on DARK_BLACK gci.CalculateDefaultColorIndices(); + g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); + m_state->PrepareNewTextBufferInfo(true, TerminalViewWidth, TerminalViewHeight); auto& currentBuffer = gci.GetActiveOutputBuffer(); // Make sure a test hasn't left us in the alt buffer on accident @@ -107,8 +110,6 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final VERIFY_SUCCEEDED(currentBuffer.SetViewportOrigin(true, { 0, 0 }, true)); VERIFY_ARE_EQUAL(COORD({ 0, 0 }), currentBuffer.GetTextBuffer().GetCursor().GetPosition()); - g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); - // Set up an xterm-256 renderer for conpty wil::unique_hfile hFile = wil::unique_hfile(INVALID_HANDLE_VALUE); Viewport initialViewport = currentBuffer.GetViewport(); @@ -158,6 +159,7 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final VERIFY_ARE_EQUAL(0u, expectedOutput.size(), L"Tests should drain all the output they push into the expected output buffer."); + emptyRenderer = nullptr; term = nullptr; return true; @@ -237,7 +239,7 @@ class TerminalCoreUnitTests::ConptyRoundtripTests final bool _checkConptyOutput{ true }; // If true, the test class will check that the output from conpty was expected bool _logConpty{ false }; // If true, the test class will log all the output from conpty. Helpful for debugging. - DummyRenderTarget emptyRT; + std::unique_ptr emptyRenderer; std::unique_ptr term; ApiRoutines _apiRoutines; diff --git a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp index 34dd9118b91..3ee7c74784a 100644 --- a/src/cascadia/UnitTests_TerminalCore/InputTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/InputTest.cpp @@ -5,7 +5,7 @@ #include #include "../cascadia/TerminalCore/Terminal.hpp" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "consoletaeftemplates.hpp" using namespace WEX::Logging; @@ -21,8 +21,8 @@ namespace TerminalCoreUnitTests TEST_CLASS(InputTest); TEST_CLASS_SETUP(ClassSetup) { - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer; + term.Create({ 100, 100 }, 0, renderer); auto inputFn = std::bind(&InputTest::_VerifyExpectedInput, this, std::placeholders::_1); term.SetWriteInputCallback(inputFn); return true; diff --git a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp index cbfa3d6514e..10843787b1a 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScreenSizeLimitsTest.cpp @@ -6,7 +6,7 @@ #include "../cascadia/TerminalCore/Terminal.hpp" #include "MockTermSettings.h" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "consoletaeftemplates.hpp" using namespace winrt::Microsoft::Terminal::Core; @@ -35,13 +35,12 @@ using namespace TerminalCoreUnitTests; void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds() { - DummyRenderTarget emptyRenderTarget; - // Negative values for initial visible row count or column count // are clamped to 1. Too-large positive values are clamped to SHRT_MAX. auto negativeColumnsSettings = winrt::make(10000, 9999999, -1234); Terminal negativeColumnsTerminal; - negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, emptyRenderTarget); + DummyRenderer renderer{ &negativeColumnsTerminal }; + negativeColumnsTerminal.CreateFromSettings(negativeColumnsSettings, renderer); COORD actualDimensions = negativeColumnsTerminal.GetViewport().Dimensions(); VERIFY_ARE_EQUAL(actualDimensions.Y, SHRT_MAX, L"Row count clamped to SHRT_MAX == " WCS(SHRT_MAX)); VERIFY_ARE_EQUAL(actualDimensions.X, 1, L"Column count clamped to 1"); @@ -49,7 +48,7 @@ void ScreenSizeLimitsTest::ScreenWidthAndHeightAreClampedToBounds() // Zero values are clamped to 1 as well. auto zeroRowsSettings = winrt::make(10000, 0, 9999999); Terminal zeroRowsTerminal; - zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, emptyRenderTarget); + zeroRowsTerminal.CreateFromSettings(zeroRowsSettings, renderer); actualDimensions = zeroRowsTerminal.GetViewport().Dimensions(); VERIFY_ARE_EQUAL(actualDimensions.Y, 1, L"Row count clamped to 1"); VERIFY_ARE_EQUAL(actualDimensions.X, SHRT_MAX, L"Column count clamped to SHRT_MAX == " WCS(SHRT_MAX)); @@ -62,36 +61,36 @@ void ScreenSizeLimitsTest::ScrollbackHistorySizeIsClampedToBounds() // actually visible on screen at the moment. const unsigned int visibleRowCount = 100; - DummyRenderTarget emptyRenderTarget; // Zero history size is acceptable. auto noHistorySettings = winrt::make(0, visibleRowCount, 100); Terminal noHistoryTerminal; - noHistoryTerminal.CreateFromSettings(noHistorySettings, emptyRenderTarget); + DummyRenderer renderer{ &noHistoryTerminal }; + noHistoryTerminal.CreateFromSettings(noHistorySettings, renderer); VERIFY_ARE_EQUAL(noHistoryTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"History size of 0 is accepted"); // Negative history sizes are clamped to zero. auto negativeHistorySizeSettings = winrt::make(-100, visibleRowCount, 100); Terminal negativeHistorySizeTerminal; - negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, emptyRenderTarget); + negativeHistorySizeTerminal.CreateFromSettings(negativeHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(negativeHistorySizeTerminal.GetTextBuffer().TotalRowCount(), visibleRowCount, L"Negative history size is clamped to 0"); // History size + initial visible rows == SHRT_MAX is acceptable. auto maxHistorySizeSettings = winrt::make(SHRT_MAX - visibleRowCount, visibleRowCount, 100); Terminal maxHistorySizeTerminal; - maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, emptyRenderTarget); + maxHistorySizeTerminal.CreateFromSettings(maxHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(maxHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast(SHRT_MAX), L"History size == SHRT_MAX - initial row count is accepted"); // History size + initial visible rows == SHRT_MAX + 1 will be clamped slightly. auto justTooBigHistorySizeSettings = winrt::make(SHRT_MAX - visibleRowCount + 1, visibleRowCount, 100); Terminal justTooBigHistorySizeTerminal; - justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, emptyRenderTarget); + justTooBigHistorySizeTerminal.CreateFromSettings(justTooBigHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(justTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast(SHRT_MAX), L"History size == 1 + SHRT_MAX - initial row count is clamped to SHRT_MAX - initial row count"); // Ridiculously large history sizes are also clamped. auto farTooBigHistorySizeSettings = winrt::make(99999999, visibleRowCount, 100); Terminal farTooBigHistorySizeTerminal; - farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, emptyRenderTarget); + farTooBigHistorySizeTerminal.CreateFromSettings(farTooBigHistorySizeSettings, renderer); VERIFY_ARE_EQUAL(farTooBigHistorySizeTerminal.GetTextBuffer().TotalRowCount(), static_cast(SHRT_MAX), L"History size that is far too large is clamped to SHRT_MAX - initial row count"); } @@ -106,7 +105,6 @@ void ScreenSizeLimitsTest::ResizeIsClampedToBounds() const unsigned int initialVisibleColCount = 50; const unsigned int initialVisibleRowCount = 50; const auto historySize = SHRT_MAX - (initialVisibleRowCount * 2); - DummyRenderTarget emptyRenderTarget; Log::Comment(L"Watch out - this test takes a while on debug, because " L"ResizeWithReflow takes a while on debug. This is expected."); @@ -114,7 +112,8 @@ void ScreenSizeLimitsTest::ResizeIsClampedToBounds() auto settings = winrt::make(historySize, initialVisibleRowCount, initialVisibleColCount); Log::Comment(L"First create a terminal with fewer than SHRT_MAX lines"); Terminal terminal; - terminal.CreateFromSettings(settings, emptyRenderTarget); + DummyRenderer renderer{ &terminal }; + terminal.CreateFromSettings(settings, renderer); VERIFY_ARE_EQUAL(terminal.GetTextBuffer().TotalRowCount(), static_cast(historySize + initialVisibleRowCount)); Log::Comment(L"Resize the terminal to have exactly SHRT_MAX lines"); diff --git a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp index 6513fbfbc9f..2c94a43ca57 100644 --- a/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ScrollTest.cpp @@ -6,7 +6,7 @@ #include -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "../renderer/base/Renderer.hpp" #include "../renderer/dx/DxRenderer.hpp" @@ -17,6 +17,7 @@ using namespace winrt::Microsoft::Terminal::Core; using namespace Microsoft::Terminal::Core; +using namespace Microsoft::Console::Render; using namespace ::Microsoft::Console::Types; using namespace WEX::Common; @@ -25,11 +26,9 @@ using namespace WEX::TestExecution; namespace { - class MockScrollRenderTarget final : public ::Microsoft::Console::Render::IRenderTarget + class MockScrollRenderEngine final : public RenderEngineBase { public: - ~MockScrollRenderTarget() override{}; - std::optional TriggerScrollDelta() const { return _triggerScrollDelta; @@ -40,20 +39,38 @@ namespace _triggerScrollDelta.reset(); } - virtual void TriggerRedraw(const Microsoft::Console::Types::Viewport&){}; - virtual void TriggerRedraw(const COORD* const){}; - virtual void TriggerRedrawCursor(const COORD* const){}; - virtual void TriggerRedrawAll(){}; - virtual void TriggerTeardown() noexcept {}; - virtual void TriggerSelection(){}; - virtual void TriggerScroll(){}; - virtual void TriggerScroll(const COORD* const delta) + HRESULT StartPaint() noexcept { return S_OK; } + HRESULT EndPaint() noexcept { return S_OK; } + HRESULT Present() noexcept { return S_OK; } + HRESULT PrepareForTeardown(_Out_ bool* /*pForcePaint*/) noexcept { return S_OK; } + HRESULT ScrollFrame() noexcept { return S_OK; } + HRESULT Invalidate(const SMALL_RECT* /*psrRegion*/) noexcept { return S_OK; } + HRESULT InvalidateCursor(const SMALL_RECT* /*psrRegion*/) noexcept { return S_OK; } + HRESULT InvalidateSystem(const RECT* /*prcDirtyClient*/) noexcept { return S_OK; } + HRESULT InvalidateSelection(const std::vector& /*rectangles*/) noexcept { return S_OK; } + HRESULT InvalidateScroll(const COORD* pcoordDelta) noexcept { - _triggerScrollDelta = { *delta }; - }; - virtual void TriggerCircling(){}; - void TriggerTitleChange(){}; - void TriggerNewTextNotification(const std::wstring_view){}; + _triggerScrollDelta = { *pcoordDelta }; + return S_OK; + } + HRESULT InvalidateAll() noexcept { return S_OK; } + HRESULT InvalidateCircling(_Out_ bool* /*pForcePaint*/) noexcept { return S_OK; } + HRESULT PaintBackground() noexcept { return S_OK; } + HRESULT PaintBufferLine(gsl::span /*clusters*/, COORD /*coord*/, bool /*fTrimLeft*/, bool /*lineWrapped*/) noexcept { return S_OK; } + HRESULT PaintBufferGridLines(GridLineSet /*lines*/, COLORREF /*color*/, size_t /*cchLine*/, COORD /*coordTarget*/) noexcept { return S_OK; } + HRESULT PaintSelection(SMALL_RECT /*rect*/) noexcept { return S_OK; } + HRESULT PaintCursor(const CursorOptions& /*options*/) noexcept { return S_OK; } + HRESULT UpdateDrawingBrushes(const TextAttribute& /*textAttributes*/, const RenderSettings& /*renderSettings*/, gsl::not_null /*pData*/, bool /*usingSoftFont*/, bool /*isSettingDefaultBrushes*/) noexcept { return S_OK; } + HRESULT UpdateFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/) noexcept { return S_OK; } + HRESULT UpdateDpi(int /*iDpi*/) noexcept { return S_OK; } + HRESULT UpdateViewport(SMALL_RECT /*srNewViewport*/) noexcept { return S_OK; } + HRESULT GetProposedFont(const FontInfoDesired& /*FontInfoDesired*/, _Out_ FontInfo& /*FontInfo*/, int /*iDpi*/) noexcept { return S_OK; } + HRESULT GetDirtyArea(gsl::span& /*area*/) noexcept { return S_OK; } + HRESULT GetFontSize(_Out_ COORD* /*pFontSize*/) noexcept { return S_OK; } + HRESULT IsGlyphWideByFont(std::wstring_view /*glyph*/, _Out_ bool* /*pResult*/) noexcept { return S_OK; } + + protected: + HRESULT _DoUpdateTitle(const std::wstring_view /*newTitle*/) noexcept { return S_OK; } private: std::optional _triggerScrollDelta; @@ -101,8 +118,10 @@ class TerminalCoreUnitTests::ScrollTest final *scrollBarNotification = { tmp }; }); - _renderTarget = std::make_unique(); - _term->Create({ TerminalViewWidth, TerminalViewHeight }, TerminalHistoryLength, *_renderTarget); + _renderEngine = std::make_unique(); + _renderer = std::make_unique(_term.get()); + _renderer->AddRenderEngine(_renderEngine.get()); + _term->Create({ TerminalViewWidth, TerminalViewHeight }, TerminalHistoryLength, *_renderer); return true; } @@ -114,7 +133,8 @@ class TerminalCoreUnitTests::ScrollTest final private: std::unique_ptr _term; - std::unique_ptr _renderTarget; + std::unique_ptr _renderEngine; + std::unique_ptr _renderer; std::shared_ptr> _scrollBarNotification; }; @@ -152,7 +172,7 @@ void ScrollTest::TestNotifyScrolling() for (; currentRow < totalBufferSize * 2; currentRow++) { *_scrollBarNotification = std::nullopt; - _renderTarget->Reset(); + _renderEngine->Reset(); termSm.ProcessString(L"X\r\n"); @@ -180,18 +200,18 @@ void ScrollTest::TestNotifyScrolling() // call `TriggerScroll` with a delta to tell the renderer about it. if (scrolled && circledBuffer) { - VERIFY_IS_TRUE(_renderTarget->TriggerScrollDelta().has_value(), - fmt::format(L"Expected a 'trigger scroll' notification in RenderTarget for row {}", currentRow).c_str()); + VERIFY_IS_TRUE(_renderEngine->TriggerScrollDelta().has_value(), + fmt::format(L"Expected a 'trigger scroll' notification in Render Engine for row {}", currentRow).c_str()); COORD expectedDelta; expectedDelta.X = 0; expectedDelta.Y = -1; - VERIFY_ARE_EQUAL(expectedDelta, _renderTarget->TriggerScrollDelta().value(), fmt::format(L"Wrong value in 'trigger scroll' notification in RenderTarget for row {}", currentRow).c_str()); + VERIFY_ARE_EQUAL(expectedDelta, _renderEngine->TriggerScrollDelta().value(), fmt::format(L"Wrong value in 'trigger scroll' notification in Render Engine for row {}", currentRow).c_str()); } else { - VERIFY_IS_FALSE(_renderTarget->TriggerScrollDelta().has_value(), - fmt::format(L"Expected to not see a 'trigger scroll' notification in RenderTarget for row {}", currentRow).c_str()); + VERIFY_IS_FALSE(_renderEngine->TriggerScrollDelta().has_value(), + fmt::format(L"Expected to not see a 'trigger scroll' notification in Render Engine for row {}", currentRow).c_str()); } if (_scrollBarNotification->has_value()) diff --git a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp index e0dc7acc5bf..81a6313af3c 100644 --- a/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/SelectionTest.cpp @@ -10,7 +10,7 @@ #include "../cascadia/TerminalCore/Terminal.hpp" #include "../cascadia/UnitTests_TerminalCore/MockTermSettings.h" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "consoletaeftemplates.hpp" using namespace WEX::Logging; @@ -47,8 +47,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectUnit) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Simulate click at (x,y) = (5,10) auto clickPos = COORD{ 5, 10 }; @@ -60,8 +60,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectArea) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Used for two things: // - click y-pos @@ -114,8 +114,8 @@ namespace TerminalCoreUnitTests // Behavior: clamp coord to viewport. auto ValidateSingleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 10, 10 }, scrollback, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 10, 10 }, scrollback, renderer); // NOTE: SetSelectionEnd(COORD) is called within SetSelectionAnchor(COORD) term.SetSelectionAnchor(maxCoord); @@ -127,8 +127,8 @@ namespace TerminalCoreUnitTests // Then, do double click selection. auto ValidateDoubleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 10, 10 }, scrollback, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 10, 10 }, scrollback, renderer); term.MultiClickSelection(maxCoord, Terminal::SelectionExpansion::Word); ValidateSingleRowSelection(term, expected); @@ -139,8 +139,8 @@ namespace TerminalCoreUnitTests // Then, do triple click selection. auto ValidateTripleClickSelection = [&](SHORT scrollback, SMALL_RECT expected) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 10, 10 }, scrollback, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 10, 10 }, scrollback, renderer); term.MultiClickSelection(maxCoord, Terminal::SelectionExpansion::Line); ValidateSingleRowSelection(term, expected); @@ -172,8 +172,8 @@ namespace TerminalCoreUnitTests */ Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 10, 10 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 10, 10 }, 0, renderer); auto viewport = term.GetViewport(); const SHORT leftBoundary = viewport.Left(); @@ -214,8 +214,8 @@ namespace TerminalCoreUnitTests */ Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 10, 10 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 10, 10 }, 0, renderer); auto viewport = term.GetViewport(); const SHORT leftBoundary = 0; @@ -300,8 +300,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectBoxArea) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Used for two things: // - click y-pos @@ -336,9 +336,9 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectAreaAfterScroll) { Terminal term; - DummyRenderTarget emptyRT; + DummyRenderer renderer{ &term }; SHORT scrollbackLines = 5; - term.Create({ 100, 100 }, scrollbackLines, emptyRT); + term.Create({ 100, 100 }, scrollbackLines, renderer); // Used for two things: // - click y-pos @@ -386,8 +386,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyph_Trailing) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // This is the burrito emoji // It's encoded in UTF-16, as needed by the buffer. @@ -409,8 +409,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyph_Leading) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // This is the burrito emoji // It's encoded in UTF-16, as needed by the buffer. @@ -432,8 +432,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(SelectWideGlyphsInBoxSelection) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // This is the burrito emoji // It's encoded in UTF-16, as needed by the buffer. @@ -487,8 +487,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_GeneralCase) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -510,8 +510,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_Delimiter) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -531,8 +531,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClick_DelimiterClass) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -559,8 +559,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClickDrag_Right) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -588,8 +588,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(DoubleClickDrag_Left) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -617,8 +617,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClick_GeneralCase) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Simulate click at (x,y) = (5,10) auto clickPos = COORD{ 5, 10 }; @@ -631,8 +631,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClickDrag_Horizontal) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Simulate click at (x,y) = (5,10) auto clickPos = COORD{ 5, 10 }; @@ -648,8 +648,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(TripleClickDrag_Vertical) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Simulate click at (x,y) = (5,10) auto clickPos = COORD{ 5, 10 }; @@ -676,8 +676,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(ShiftClick) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // set word delimiters for terminal auto settings = winrt::make(0, 100, 100); @@ -793,8 +793,8 @@ namespace TerminalCoreUnitTests TEST_METHOD(Pivot) { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); // Step 1: Create a selection { diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp index ef5d587fb90..362535da374 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalApiTest.cpp @@ -6,7 +6,7 @@ #include "../cascadia/TerminalCore/Terminal.hpp" #include "MockTermSettings.h" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "consoletaeftemplates.hpp" using namespace winrt::Microsoft::Terminal::Core; @@ -49,8 +49,8 @@ using namespace TerminalCoreUnitTests; void TerminalApiTest::SetColorTableEntry() { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto settings = winrt::make(100, 100, 100); term.UpdateSettings(settings); @@ -67,9 +67,9 @@ void TerminalApiTest::SetColorTableEntry() // PrintString() is called with more code units than the buffer width. void TerminalApiTest::PrintStringOfSurrogatePairs() { - DummyRenderTarget renderTarget; Terminal term; - term.Create({ 100, 100 }, 3, renderTarget); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 3, renderer); std::wstring text; text.reserve(600); @@ -135,8 +135,8 @@ void TerminalApiTest::CursorVisibility() { // GH#3093 - Cursor Visibility and On states shouldn't affect each other Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); VERIFY_IS_TRUE(term._buffer->GetCursor().IsVisible()); VERIFY_IS_TRUE(term._buffer->GetCursor().IsOn()); @@ -167,8 +167,8 @@ void TerminalApiTest::CursorVisibilityViaStateMachine() { // This is a nearly literal copy-paste of ScreenBufferTests::TestCursorIsOn, adapted for the Terminal Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& tbi = *(term._buffer); auto& stateMachine = *(term._stateMachine); @@ -218,9 +218,9 @@ void TerminalApiTest::CursorVisibilityViaStateMachine() void TerminalApiTest::CheckDoubleWidthCursor() { - DummyRenderTarget renderTarget; Terminal term; - term.Create({ 100, 100 }, 0, renderTarget); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& tbi = *(term._buffer); auto& stateMachine = *(term._stateMachine); @@ -263,8 +263,8 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlink() // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlink, adapted for the Terminal Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& tbi = *(term._buffer); auto& stateMachine = *(term._stateMachine); @@ -289,8 +289,8 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomId() // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlinkCustomId, adapted for the Terminal Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& tbi = *(term._buffer); auto& stateMachine = *(term._stateMachine); @@ -317,8 +317,8 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomIdDifferentUri() // This is a nearly literal copy-paste of ScreenBufferTests::TestAddHyperlinkCustomId, adapted for the Terminal Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& tbi = *(term._buffer); auto& stateMachine = *(term._stateMachine); @@ -345,8 +345,8 @@ void TerminalCoreUnitTests::TerminalApiTest::AddHyperlinkCustomIdDifferentUri() void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress() { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& stateMachine = *(term._stateMachine); @@ -416,8 +416,8 @@ void TerminalCoreUnitTests::TerminalApiTest::SetTaskbarProgress() void TerminalCoreUnitTests::TerminalApiTest::SetWorkingDirectory() { Terminal term; - DummyRenderTarget emptyRT; - term.Create({ 100, 100 }, 0, emptyRT); + DummyRenderer renderer{ &term }; + term.Create({ 100, 100 }, 0, renderer); auto& stateMachine = *(term._stateMachine); diff --git a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp index a5cbeea6c6f..e9378f50d20 100644 --- a/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/TerminalBufferTests.cpp @@ -4,7 +4,7 @@ #include "pch.h" #include -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" #include "../cascadia/TerminalCore/Terminal.hpp" #include "MockTermSettings.h" #include "consoletaeftemplates.hpp" @@ -57,12 +57,14 @@ class TerminalCoreUnitTests::TerminalBufferTests final { // STEP 1: Set up the Terminal term = std::make_unique(); - term->Create({ TerminalViewWidth, TerminalViewHeight }, TerminalHistoryLength, emptyRT); + emptyRenderer = std::make_unique(term.get()); + term->Create({ TerminalViewWidth, TerminalViewHeight }, TerminalHistoryLength, *emptyRenderer); return true; } TEST_METHOD_CLEANUP(MethodCleanup) { + emptyRenderer = nullptr; term = nullptr; return true; } @@ -71,7 +73,7 @@ class TerminalCoreUnitTests::TerminalBufferTests final void _SetTabStops(std::list columns, bool replace); std::list _GetTabStops(); - DummyRenderTarget emptyRT; + std::unique_ptr emptyRenderer; std::unique_ptr term; }; diff --git a/src/host/CursorBlinker.cpp b/src/host/CursorBlinker.cpp index 939efeaed58..f3aee7a4798 100644 --- a/src/host/CursorBlinker.cpp +++ b/src/host/CursorBlinker.cpp @@ -150,7 +150,7 @@ void CursorBlinker::TimerRoutine(SCREEN_INFORMATION& ScreenInfo) const noexcept } DoBlinkingRenditionAndScroll: - gci.GetRenderSettings().ToggleBlinkRendition(ScreenInfo.GetRenderTarget()); + gci.GetRenderSettings().ToggleBlinkRendition(buffer.GetRenderer()); DoScroll: Scrolling::s_ScrollIfNecessary(ScreenInfo); diff --git a/src/host/ScreenBufferRenderTarget.cpp b/src/host/ScreenBufferRenderTarget.cpp deleted file mode 100644 index ed9ebc91608..00000000000 --- a/src/host/ScreenBufferRenderTarget.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" -#include "ScreenBufferRenderTarget.hpp" -#include "../interactivity/inc/ServiceLocator.hpp" - -using Microsoft::Console::Interactivity::ServiceLocator; -ScreenBufferRenderTarget::ScreenBufferRenderTarget(SCREEN_INFORMATION& owner) : - _owner{ owner } -{ -} - -void ScreenBufferRenderTarget::TriggerRedraw(const Microsoft::Console::Types::Viewport& region) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerRedraw(region); - } -} - -void ScreenBufferRenderTarget::TriggerRedraw(const COORD* const pcoord) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerRedraw(pcoord); - } -} - -void ScreenBufferRenderTarget::TriggerRedrawCursor(const COORD* const pcoord) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerRedrawCursor(pcoord); - } -} - -void ScreenBufferRenderTarget::TriggerRedrawAll() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerRedrawAll(); - } -} - -void ScreenBufferRenderTarget::TriggerTeardown() noexcept -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerTeardown(); - } -} - -void ScreenBufferRenderTarget::TriggerSelection() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerSelection(); - } -} - -void ScreenBufferRenderTarget::TriggerScroll() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerScroll(); - } -} - -void ScreenBufferRenderTarget::TriggerScroll(const COORD* const pcoordDelta) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerScroll(pcoordDelta); - } -} - -void ScreenBufferRenderTarget::TriggerCircling() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerCircling(); - } -} - -void ScreenBufferRenderTarget::TriggerTitleChange() -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerTitleChange(); - } -} - -void ScreenBufferRenderTarget::TriggerNewTextNotification(const std::wstring_view newText) -{ - auto* pRenderer = ServiceLocator::LocateGlobals().pRender; - const auto* pActive = &ServiceLocator::LocateGlobals().getConsoleInformation().GetActiveOutputBuffer().GetActiveBuffer(); - if (pRenderer != nullptr && pActive == &_owner) - { - pRenderer->TriggerNewTextNotification(newText); - } -} diff --git a/src/host/ScreenBufferRenderTarget.hpp b/src/host/ScreenBufferRenderTarget.hpp deleted file mode 100644 index 7f7588faba4..00000000000 --- a/src/host/ScreenBufferRenderTarget.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- ScreenBufferRenderTarget.hpp - -Abstract: -Provides an encapsulation for all of the RenderTarget methods for the SCreenBuffer. -Unfortunately, these cannot be defined directly on the SCREEN_INFORMATION due to -MSFT 9358743. -Adding an interface to SCREEN_INFORMATION makes the ConsoleObjectHeader no -longer the first part of the SCREEN_INFORMATION. -The Screen buffer will pass this object to other objects that need to trigger -redrawing the buffer contents. - -Author(s): -- Mike Griese (migrie) Nov 2018 ---*/ - -#pragma once -#include "../renderer/inc/IRenderTarget.hpp" - -// fwdecl -class SCREEN_INFORMATION; - -class ScreenBufferRenderTarget final : public Microsoft::Console::Render::IRenderTarget -{ -public: - ScreenBufferRenderTarget(SCREEN_INFORMATION& owner); - - void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; - void TriggerRedraw(const COORD* const pcoord) override; - void TriggerRedrawCursor(const COORD* const pcoord) override; - void TriggerRedrawAll() override; - void TriggerTeardown() noexcept override; - void TriggerSelection() override; - void TriggerScroll() override; - void TriggerScroll(const COORD* const pcoordDelta) override; - void TriggerCircling() override; - void TriggerTitleChange() override; - void TriggerNewTextNotification(const std::wstring_view newText) override; - -private: - SCREEN_INFORMATION& _owner; -}; diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index 52152e0f5ff..544b89af995 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -196,7 +196,7 @@ using Microsoft::Console::VirtualTerminal::StateMachine; // back where it started, but everything else moved. // In this case, delta was 1. So the amount that moved is the entire viewport height minus the delta. Viewport invalid = Viewport::FromDimensions(viewport.Origin(), { viewport.Width(), viewport.Height() - delta }); - screenInfo.GetRenderTarget().TriggerRedraw(invalid); + screenInfo.GetTextBuffer().TriggerRedraw(invalid); } // reset where our local viewport is, and recalculate the cursor and diff --git a/src/host/consoleInformation.cpp b/src/host/consoleInformation.cpp index 7313132b73b..b567f2a1ea3 100644 --- a/src/host/consoleInformation.cpp +++ b/src/host/consoleInformation.cpp @@ -146,7 +146,7 @@ ULONG CONSOLE_INFORMATION::GetCSRecursionCount() goto ErrorExit2; } - gci.pCurrentScreenBuffer = gci.ScreenBuffers; + gci.SetActiveOutputBuffer(*gci.ScreenBuffers); gci.GetActiveOutputBuffer().ScrollScale = gci.GetScrollScale(); @@ -214,6 +214,16 @@ const SCREEN_INFORMATION& CONSOLE_INFORMATION::GetActiveOutputBuffer() const return *pCurrentScreenBuffer; } +void CONSOLE_INFORMATION::SetActiveOutputBuffer(SCREEN_INFORMATION& screenBuffer) +{ + if (pCurrentScreenBuffer) + { + pCurrentScreenBuffer->GetTextBuffer().SetAsActiveBuffer(false); + } + pCurrentScreenBuffer = &screenBuffer; + pCurrentScreenBuffer->GetTextBuffer().SetAsActiveBuffer(true); +} + bool CONSOLE_INFORMATION::HasActiveOutputBuffer() const { return (pCurrentScreenBuffer != nullptr); diff --git a/src/host/host-common.vcxitems b/src/host/host-common.vcxitems index 7645ced09a1..46acdde1aea 100644 --- a/src/host/host-common.vcxitems +++ b/src/host/host-common.vcxitems @@ -41,7 +41,6 @@ - @@ -101,7 +100,6 @@ - diff --git a/src/host/lib/hostlib.vcxproj.filters b/src/host/lib/hostlib.vcxproj.filters index 11256b620bb..b8b3df1e25b 100644 --- a/src/host/lib/hostlib.vcxproj.filters +++ b/src/host/lib/hostlib.vcxproj.filters @@ -177,9 +177,6 @@ Source Files - - Source Files - @@ -350,9 +347,6 @@ Header Files - - Header Files - diff --git a/src/host/output.cpp b/src/host/output.cpp index a5f539ccc77..8fb20670154 100644 --- a/src/host/output.cpp +++ b/src/host/output.cpp @@ -280,13 +280,13 @@ static void _ScrollScreen(SCREEN_INFORMATION& screenInfo, const Viewport& source } } - // Get the render target and send it commands. + // Get the text buffer and send it commands. // It will figure out whether or not we're active and where the messages need to go. - auto& render = screenInfo.GetRenderTarget(); + auto& textBuffer = screenInfo.GetTextBuffer(); // Redraw anything in the target area - render.TriggerRedraw(target); + textBuffer.TriggerRedraw(target); // Also redraw anything that was filled. - render.TriggerRedraw(fill); + textBuffer.TriggerRedraw(fill); } // Routine Description: @@ -464,7 +464,7 @@ void ScrollRegion(SCREEN_INFORMATION& screenInfo, void SetActiveScreenBuffer(SCREEN_INFORMATION& screenInfo) { CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.pCurrentScreenBuffer = &screenInfo; + gci.SetActiveOutputBuffer(screenInfo); // initialize cursor GH#4102 - Typically, the cursor is set to on by the // cursor blinker. Unfortunately, in conpty mode, there is no cursor diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 6c786b4aa42..75b1585b5ac 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -56,7 +56,6 @@ SCREEN_INFORMATION::SCREEN_INFORMATION( _fAltWindowChanged{ false }, _PopupAttributes{ popupAttributes }, _virtualBottom{ 0 }, - _renderTarget{ *this }, _currentFont{ fontInfo }, _desiredFont{ fontInfo }, _ignoreLegacyEquivalentVTAttributes{ false } @@ -120,7 +119,8 @@ SCREEN_INFORMATION::~SCREEN_INFORMATION() pScreen->_textBuffer = std::make_unique(coordScreenBufferSize, defaultAttributes, uiCursorSize, - pScreen->_renderTarget); + pScreen->IsActiveScreenBuffer(), + *ServiceLocator::LocateGlobals().pRender); const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); pScreen->_textBuffer->GetCursor().SetType(gci.GetCursorType()); @@ -1443,8 +1443,9 @@ bool SCREEN_INFORMATION::IsMaximizedY() const { newTextBuffer = std::make_unique(coordNewScreenSize, TextAttribute{}, - 0, - _renderTarget); // temporarily set size to 0 so it won't render. + 0, // temporarily set size to 0 so it won't render. + _textBuffer->IsActiveBuffer(), + _textBuffer->GetRenderer()); } catch (...) { @@ -2125,7 +2126,7 @@ void SCREEN_INFORMATION::SetDefaultAttributes(const TextAttribute& attributes, // because the text attributes changed. if (!(gci.IsInVtIoMode())) { - GetRenderTarget().TriggerRedrawAll(); + _textBuffer->TriggerRedrawAll(); } gci.ConsoleIme.RefreshAreaAttributes(); @@ -2295,7 +2296,7 @@ void SCREEN_INFORMATION::SetViewport(const Viewport& newViewport, auto fillData = OutputCellIterator{ fillAttributes, fillLength }; Write(fillData, fillPosition, false); - _textBuffer->GetRenderTarget().TriggerRedrawAll(); + _textBuffer->TriggerRedrawAll(); // Also reset the line rendition for the erased rows. _textBuffer->ResetLineRenditionRange(_viewport.Top(), _viewport.BottomExclusive()); @@ -2678,17 +2679,6 @@ bool SCREEN_INFORMATION::CursorIsDoubleWidth() const return IsGlyphFullWidth(*it); } -// Method Description: -// - Retrieves this buffer's current render target. -// Arguments: -// - -// Return Value: -// - This buffer's current render target. -IRenderTarget& SCREEN_INFORMATION::GetRenderTarget() noexcept -{ - return _renderTarget; -} - // Method Description: // - Gets the current font of the screen buffer. // Arguments: diff --git a/src/host/screenInfo.hpp b/src/host/screenInfo.hpp index 97251bf58a7..56554954bf0 100644 --- a/src/host/screenInfo.hpp +++ b/src/host/screenInfo.hpp @@ -22,7 +22,6 @@ Revision History: #include "conapi.h" #include "settings.hpp" #include "outputStream.hpp" -#include "ScreenBufferRenderTarget.hpp" #include "../buffer/out/OutputCellRect.hpp" #include "../buffer/out/TextAttribute.hpp" @@ -225,8 +224,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console void UpdateBottom(); void MoveToBottom(); - Microsoft::Console::Render::IRenderTarget& GetRenderTarget() noexcept; - FontInfo& GetCurrentFont() noexcept; const FontInfo& GetCurrentFont() const noexcept; @@ -298,8 +295,6 @@ class SCREEN_INFORMATION : public ConsoleObjectHeader, public Microsoft::Console // the viewport to move (SetBufferInfo, WriteConsole, etc) short _virtualBottom; - ScreenBufferRenderTarget _renderTarget; - bool _ignoreLegacyEquivalentVTAttributes; #ifdef UNIT_TESTING diff --git a/src/host/server.h b/src/host/server.h index 39d539158dd..1a7ca9ba821 100644 --- a/src/host/server.h +++ b/src/host/server.h @@ -111,6 +111,7 @@ class CONSOLE_INFORMATION : SCREEN_INFORMATION& GetActiveOutputBuffer() override; const SCREEN_INFORMATION& GetActiveOutputBuffer() const override; + void SetActiveOutputBuffer(SCREEN_INFORMATION& screenBuffer); bool HasActiveOutputBuffer() const; InputBuffer* const GetActiveInputBuffer() const; diff --git a/src/host/sources.inc b/src/host/sources.inc index 042622c404e..b92efbd5c38 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -70,7 +70,6 @@ SOURCES = \ ..\dbcs.cpp \ ..\convarea.cpp \ ..\screenInfo.cpp \ - ..\ScreenBufferRenderTarget.cpp \ ..\_output.cpp \ ..\_stream.cpp \ ..\utils.cpp \ diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index f5fa04f1fe1..055c9ec5546 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -801,12 +801,6 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, CONSOLE_INFORMATION& gci = g.getConsoleInformation(); - NTSTATUS Status = SetUpConsole(&p->ConsoleInfo, p->TitleLength, p->Title, p->CurDir, p->AppName); - if (!NT_SUCCESS(Status)) - { - return Status; - } - // No matter what, create a renderer. try { @@ -823,9 +817,6 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, THROW_IF_FAILED(localPointerToThread->Initialize(g.pRender)); - // Allow the renderer to paint. - g.pRender->EnablePainting(); - // Set up the renderer to be used to calculate the width of a glyph, // should we be unable to figure out its width another way. auto pfn = std::bind(&Renderer::IsGlyphWideByFont, static_cast(g.pRender), std::placeholders::_1); @@ -833,9 +824,22 @@ PWSTR TranslateConsoleTitle(_In_ PCWSTR pwszConsoleTitle, const BOOL fUnexpand, } catch (...) { - Status = NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); + return NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); } + // Note that the order of initialization is important here. SetUpConsole is + // where the TextBuffer is created (ultimately in the SCREEN_INFORMATION + // CreateInstance method), and the TextBuffer needs to be constructed with + // a reference to the renderer, so the renderer must be created first. + NTSTATUS Status = SetUpConsole(&p->ConsoleInfo, p->TitleLength, p->Title, p->CurDir, p->AppName); + if (!NT_SUCCESS(Status)) + { + return Status; + } + + // Allow the renderer to paint once the rest of the console is hooked up. + g.pRender->EnablePainting(); + if (NT_SUCCESS(Status) && ConsoleConnectionDeservesVisibleWindow(p)) { HANDLE Thread = nullptr; diff --git a/src/host/ut_host/ConptyOutputTests.cpp b/src/host/ut_host/ConptyOutputTests.cpp index d40a2fc2073..6e4a9d558a8 100644 --- a/src/host/ut_host/ConptyOutputTests.cpp +++ b/src/host/ut_host/ConptyOutputTests.cpp @@ -72,6 +72,8 @@ class ConptyOutputTests gci.SetFillAttribute(0x07); // DARK_WHITE on DARK_BLACK gci.CalculateDefaultColorIndices(); + g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); + m_state->PrepareNewTextBufferInfo(true, TerminalViewWidth, TerminalViewHeight); auto& currentBuffer = gci.GetActiveOutputBuffer(); // Make sure a test hasn't left us in the alt buffer on accident @@ -79,8 +81,6 @@ class ConptyOutputTests VERIFY_SUCCEEDED(currentBuffer.SetViewportOrigin(true, { 0, 0 }, true)); VERIFY_ARE_EQUAL(COORD({ 0, 0 }), currentBuffer.GetTextBuffer().GetCursor().GetPosition()); - g.pRender = new Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); - // Set up an xterm-256 renderer for conpty wil::unique_hfile hFile = wil::unique_hfile(INVALID_HANDLE_VALUE); Viewport initialViewport = currentBuffer.GetViewport(); diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index 2a963911fce..a75636278f8 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -38,7 +38,8 @@ class ScreenBufferTests m_state = new CommonState(); m_state->InitEvents(); - m_state->PrepareGlobalFont(); + m_state->PrepareGlobalFont({ 1, 1 }); + m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalScreenBuffer(); m_state->PrepareGlobalInputBuffer(); @@ -48,6 +49,7 @@ class ScreenBufferTests TEST_CLASS_CLEANUP(ClassCleanup) { m_state->CleanupGlobalScreenBuffer(); + m_state->CleanupGlobalRenderer(); m_state->CleanupGlobalFont(); m_state->CleanupGlobalInputBuffer(); @@ -535,8 +537,12 @@ std::list _GetTabStops(SCREEN_INFORMATION& screenInfo) void ScreenBufferTests::TestResetClearTabStops() { // Reset the screen buffer to test the defaults. + m_state->CleanupNewTextBufferInfo(); m_state->CleanupGlobalScreenBuffer(); + m_state->CleanupGlobalRenderer(); + m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalScreenBuffer(); + m_state->PrepareNewTextBufferInfo(); CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer(); diff --git a/src/host/ut_host/SearchTests.cpp b/src/host/ut_host/SearchTests.cpp index 38d9f1c1108..b2d5b0f5f28 100644 --- a/src/host/ut_host/SearchTests.cpp +++ b/src/host/ut_host/SearchTests.cpp @@ -25,6 +25,7 @@ class SearchTests m_state = new CommonState(); m_state->PrepareGlobalFont(); + m_state->PrepareGlobalRenderer(); m_state->PrepareGlobalScreenBuffer(); return true; @@ -33,6 +34,7 @@ class SearchTests TEST_CLASS_CLEANUP(ClassCleanup) { m_state->CleanupGlobalScreenBuffer(); + m_state->CleanupGlobalRenderer(); m_state->CleanupGlobalFont(); delete m_state; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 489768461fe..0fc5a374342 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -15,7 +15,7 @@ #include "_stream.h" #include "../interactivity/inc/ServiceLocator.hpp" -#include "../renderer/inc/DummyRenderTarget.hpp" +#include "../renderer/inc/DummyRenderer.hpp" using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity; @@ -26,7 +26,7 @@ using namespace WEX::TestExecution; class TextBufferTests { - DummyRenderTarget _renderTarget; + DummyRenderer _renderer; CommonState* m_state; TEST_CLASS(TextBufferTests); @@ -358,7 +358,8 @@ void TextBufferTests::TestCopyProperties() std::unique_ptr testTextBuffer = std::make_unique(otherTbi.GetSize().Dimensions(), otherTbi._currentAttributes, 12, - otherTbi._renderTarget); + otherTbi.IsActiveBuffer(), + otherTbi._renderer); VERIFY_IS_NOT_NULL(testTextBuffer.get()); // set initial mapping values @@ -1752,7 +1753,7 @@ void TextBufferTests::ResizeTraditional() const COORD smallSize = { 5, 5 }; const TextAttribute defaultAttr(0); - TextBuffer buffer(smallSize, defaultAttr, 12, _renderTarget); + TextBuffer buffer(smallSize, defaultAttr, 12, false, _renderer); Log::Comment(L"Fill buffer with some data and do assorted resize operations."); @@ -1848,7 +1849,7 @@ void TextBufferTests::ResizeTraditionalRotationPreservesHighUnicode() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Get a position inside the buffer const COORD pos{ 2, 1 }; @@ -1890,7 +1891,7 @@ void TextBufferTests::ScrollBufferRotationPreservesHighUnicode() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Get a position inside the buffer const COORD pos{ 2, 1 }; @@ -1930,7 +1931,7 @@ void TextBufferTests::ResizeTraditionalHighUnicodeRowRemoval() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Get a position inside the buffer in the bottom row const COORD pos{ 0, bufferSize.Y - 1 }; @@ -1965,7 +1966,7 @@ void TextBufferTests::ResizeTraditionalHighUnicodeColumnRemoval() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Get a position inside the buffer in the last column const COORD pos{ bufferSize.X - 1, 0 }; @@ -1997,7 +1998,7 @@ void TextBufferTests::TestBurrito() COORD bufferSize{ 80, 9001 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // This is the burrito emoji: 🌯 // It's encoded in UTF-16, as needed by the buffer. @@ -2072,7 +2073,7 @@ void TextBufferTests::GetWordBoundaries() COORD bufferSize{ 80, 9001 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Setup: Write lines of text to the buffer const std::vector text = { L"word other", @@ -2181,7 +2182,7 @@ void TextBufferTests::MoveByWord() COORD bufferSize{ 80, 9001 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Setup: Write lines of text to the buffer const std::vector text = { L"word other", @@ -2287,7 +2288,7 @@ void TextBufferTests::GetGlyphBoundaries() COORD bufferSize{ 10, 10 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // This is the burrito emoji: 🌯 // It's encoded in UTF-16, as needed by the buffer. @@ -2323,7 +2324,7 @@ void TextBufferTests::GetTextRects() COORD bufferSize{ 20, 50 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Setup: Write lines of text to the buffer const std::vector text = { L"0123456789", @@ -2403,7 +2404,7 @@ void TextBufferTests::GetText() COORD bufferSize{ 10, 20 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Setup: Write lines of text to the buffer const std::vector bufferText = { L"12345", @@ -2495,7 +2496,7 @@ void TextBufferTests::GetText() COORD bufferSize{ 5, 20 }; UINT cursorSize = 12; TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); // Setup: Write lines of text to the buffer const std::vector bufferText = { L"1234567", @@ -2631,7 +2632,7 @@ void TextBufferTests::HyperlinkTrim() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); static constexpr std::wstring_view url{ L"test.url" }; static constexpr std::wstring_view otherUrl{ L"other.url" }; @@ -2677,7 +2678,7 @@ void TextBufferTests::NoHyperlinkTrim() const COORD bufferSize{ 80, 10 }; const UINT cursorSize = 12; const TextAttribute attr{ 0x7f }; - auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); + auto _buffer = std::make_unique(bufferSize, attr, cursorSize, false, _renderer); static constexpr std::wstring_view url{ L"test.url" }; static constexpr std::wstring_view customId{ L"CustomId" }; diff --git a/src/inc/test/CommonState.hpp b/src/inc/test/CommonState.hpp index 6319bb90208..909f8c58131 100644 --- a/src/inc/test/CommonState.hpp +++ b/src/inc/test/CommonState.hpp @@ -64,11 +64,8 @@ class CommonState m_readHandle.reset(nullptr); } - void PrepareGlobalFont() + void PrepareGlobalFont(const COORD coordFontSize = { 8, 12 }) { - COORD coordFontSize; - coordFontSize.X = 8; - coordFontSize.Y = 12; m_pFontInfo = new FontInfo(L"Consolas", 0, 0, coordFontSize, 0); } @@ -80,12 +77,26 @@ class CommonState } } + void PrepareGlobalRenderer() + { + Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = g.getConsoleInformation(); + g.pRender = new Microsoft::Console::Render::Renderer(gci.GetRenderSettings(), &gci.renderData, nullptr, 0, nullptr); + } + + void CleanupGlobalRenderer() + { + const Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + delete g.pRender; + } + void PrepareGlobalScreenBuffer(const short viewWidth = s_csWindowWidth, const short viewHeight = s_csWindowHeight, const short bufferWidth = s_csBufferWidth, const short bufferHeight = s_csBufferHeight) { - CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); + Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = g.getConsoleInformation(); COORD coordWindowSize; coordWindowSize.X = viewWidth; coordWindowSize.Y = viewHeight; @@ -103,6 +114,18 @@ class CommonState TextAttribute{ FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED }, uiCursorSize, &gci.pCurrentScreenBuffer)); + + // If we have a renderer, we need to call EnablePainting to initialize + // the viewport. If not, we mark the text buffer as inactive so that it + // doesn't try to trigger a redraw on a non-existent renderer. + if (g.pRender) + { + g.pRender->EnablePainting(); + } + else + { + gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false); + } } void CleanupGlobalScreenBuffer() @@ -149,7 +172,8 @@ class CommonState const short bufferWidth = s_csBufferWidth, const short bufferHeight = s_csBufferHeight) { - CONSOLE_INFORMATION& gci = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals().getConsoleInformation(); + Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + CONSOLE_INFORMATION& gci = g.getConsoleInformation(); COORD coordScreenBufferSize; coordScreenBufferSize.X = bufferWidth; coordScreenBufferSize.Y = bufferHeight; @@ -165,7 +189,8 @@ class CommonState std::unique_ptr textBuffer = std::make_unique(coordScreenBufferSize, initialAttributes, uiCursorSize, - gci.pCurrentScreenBuffer->GetRenderTarget()); + true, + *g.pRender); if (textBuffer.get() == nullptr) { m_hrTextBufferInfo = E_OUTOFMEMORY; @@ -175,6 +200,18 @@ class CommonState m_hrTextBufferInfo = S_OK; } gci.pCurrentScreenBuffer->_textBuffer.swap(textBuffer); + + // If we have a renderer, we need to call EnablePainting to initialize + // the viewport. If not, we mark the text buffer as inactive so that it + // doesn't try to trigger a redraw on a non-existent renderer. + if (g.pRender) + { + g.pRender->EnablePainting(); + } + else + { + gci.pCurrentScreenBuffer->_textBuffer->SetAsActiveBuffer(false); + } } catch (...) { diff --git a/src/renderer/base/RenderSettings.cpp b/src/renderer/base/RenderSettings.cpp index e3b74513fc6..e889adccbf7 100644 --- a/src/renderer/base/RenderSettings.cpp +++ b/src/renderer/base/RenderSettings.cpp @@ -4,7 +4,7 @@ #include "precomp.h" #include "../inc/RenderSettings.hpp" -#include "../inc/IRenderTarget.hpp" +#include "../base/renderer.hpp" #include "../../types/inc/ColorFix.hpp" #include "../../types/inc/colorTable.hpp" @@ -262,10 +262,10 @@ std::pair RenderSettings::GetAttributeColorsWithAlpha(const // Routine Description: // - Increments the position in the blink cycle, toggling the blink rendition // state on every second call, potentially triggering a redraw of the given -// render target if there are blinking cells currently in view. +// renderer if there are blinking cells currently in view. // Arguments: -// - renderTarget: the render target that will be redrawn. -void RenderSettings::ToggleBlinkRendition(IRenderTarget& renderTarget) noexcept +// - renderer: the renderer that will be redrawn. +void RenderSettings::ToggleBlinkRendition(Renderer& renderer) noexcept try { if (GetRenderMode(Mode::BlinkAllowed)) @@ -283,7 +283,7 @@ try // We reset the _blinkIsInUse flag before redrawing, so we can // get a fresh assessment of the current blink attribute usage. _blinkIsInUse = false; - renderTarget.TriggerRedrawAll(); + renderer.TriggerRedrawAll(); } } } diff --git a/src/renderer/base/lib/base.vcxproj b/src/renderer/base/lib/base.vcxproj index 5d7bc816c41..ceed166fd27 100644 --- a/src/renderer/base/lib/base.vcxproj +++ b/src/renderer/base/lib/base.vcxproj @@ -31,7 +31,6 @@ - diff --git a/src/renderer/base/lib/base.vcxproj.filters b/src/renderer/base/lib/base.vcxproj.filters index 4e8f12c1821..a678fb27cce 100644 --- a/src/renderer/base/lib/base.vcxproj.filters +++ b/src/renderer/base/lib/base.vcxproj.filters @@ -83,9 +83,6 @@ Header Files\inc - - Header Files\inc - Header Files\inc diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 66bbcc6b5ad..e720708b9e2 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -34,9 +34,8 @@ Renderer::Renderer(const RenderSettings& renderSettings, const size_t cEngines, std::unique_ptr thread) : _renderSettings(renderSettings), - _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)), - _pThread{ std::move(thread) }, - _viewport{ pData->GetViewport() } + _pData(pData), + _pThread{ std::move(thread) } { for (size_t i = 0; i < cEngines; i++) { @@ -631,7 +630,15 @@ bool Renderer::IsGlyphWideByFont(const std::wstring_view glyph) // - void Renderer::EnablePainting() { - _pThread->EnablePainting(); + // When the renderer is constructed, the initial viewport won't be available yet, + // but once EnablePainting is called it should be safe to retrieve. + _viewport = _pData->GetViewport(); + + // When running the unit tests, we may be using a render without a render thread. + if (_pThread) + { + _pThread->EnablePainting(); + } } // Routine Description: diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index e53afd2a25f..0a84c6b6e0b 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -16,7 +16,7 @@ Author(s): #pragma once -#include "../inc/IRenderTarget.hpp" +#include "../inc/IRenderEngine.hpp" #include "../inc/RenderSettings.hpp" #include "thread.hpp" @@ -26,7 +26,7 @@ Author(s): namespace Microsoft::Console::Render { - class Renderer : public IRenderTarget + class Renderer { public: Renderer(const RenderSettings& renderSettings, @@ -35,26 +35,26 @@ namespace Microsoft::Console::Render const size_t cEngines, std::unique_ptr thread); - virtual ~Renderer(); + ~Renderer(); [[nodiscard]] HRESULT PaintFrame(); void NotifyPaintFrame() noexcept; void TriggerSystemRedraw(const RECT* const prcDirtyClient); - void TriggerRedraw(const Microsoft::Console::Types::Viewport& region) override; - void TriggerRedraw(const COORD* const pcoord) override; - void TriggerRedrawCursor(const COORD* const pcoord) override; - void TriggerRedrawAll() override; - void TriggerTeardown() noexcept override; + void TriggerRedraw(const Microsoft::Console::Types::Viewport& region); + void TriggerRedraw(const COORD* const pcoord); + void TriggerRedrawCursor(const COORD* const pcoord); + void TriggerRedrawAll(); + void TriggerTeardown() noexcept; - void TriggerSelection() override; - void TriggerScroll() override; - void TriggerScroll(const COORD* const pcoordDelta) override; + void TriggerSelection(); + void TriggerScroll(); + void TriggerScroll(const COORD* const pcoordDelta); - void TriggerCircling() override; - void TriggerTitleChange() override; + void TriggerCircling(); + void TriggerTitleChange(); - void TriggerNewTextNotification(const std::wstring_view newText) override; + void TriggerNewTextNotification(const std::wstring_view newText); void TriggerFontChange(const int iDpi, const FontInfoDesired& FontInfoDesired, diff --git a/src/renderer/inc/DummyRenderTarget.hpp b/src/renderer/inc/DummyRenderTarget.hpp deleted file mode 100644 index fb105e599ba..00000000000 --- a/src/renderer/inc/DummyRenderTarget.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- DummyRenderTarget.hpp - -Abstract: -- Provides an empty implementation of the IRenderTarget interface. - This is needed for some tests, where certain objects need a reference to a - IRenderTarget - -Author(s): -- Mike Griese (migrie) Nov 2018 ---*/ - -#pragma once -#include "IRenderTarget.hpp" - -class DummyRenderTarget final : public Microsoft::Console::Render::IRenderTarget -{ -public: - DummyRenderTarget() {} - void TriggerRedraw(const Microsoft::Console::Types::Viewport& /*region*/) override {} - void TriggerRedraw(const COORD* const /*pcoord*/) override {} - void TriggerRedrawCursor(const COORD* const /*pcoord*/) override {} - void TriggerRedrawAll() override {} - void TriggerTeardown() noexcept override {} - void TriggerSelection() override {} - void TriggerScroll() override {} - void TriggerScroll(const COORD* const /*pcoordDelta*/) override {} - void TriggerCircling() override {} - void TriggerTitleChange() override {} - void TriggerNewTextNotification(const std::wstring_view) override {} -}; diff --git a/src/renderer/inc/DummyRenderer.hpp b/src/renderer/inc/DummyRenderer.hpp new file mode 100644 index 00000000000..0d168311a6e --- /dev/null +++ b/src/renderer/inc/DummyRenderer.hpp @@ -0,0 +1,25 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- DummyRenderer.hpp + +Abstract: +- Provides a minimal instantiation of the Renderer class. + This is needed for some tests, where certain objects need a reference to a + Renderer +--*/ + +#pragma once +#include "../base/renderer.hpp" + +class DummyRenderer final : public Microsoft::Console::Render::Renderer +{ +public: + DummyRenderer(Microsoft::Console::Render::IRenderData* pData = nullptr) : + Microsoft::Console::Render::Renderer(_renderSettings, pData, nullptr, 0, nullptr) {} + +private: + Microsoft::Console::Render::RenderSettings _renderSettings; +}; diff --git a/src/renderer/inc/IRenderTarget.hpp b/src/renderer/inc/IRenderTarget.hpp deleted file mode 100644 index 6f32c40bd84..00000000000 --- a/src/renderer/inc/IRenderTarget.hpp +++ /dev/null @@ -1,54 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- IRenderTarget.hpp - -Abstract: -- This serves as the entry point for console rendering activities. - -Author(s): -- Michael Niksa (MiNiksa) 17-Nov-2015 ---*/ - -#pragma once - -#include "FontInfoDesired.hpp" -#include "IRenderEngine.hpp" -#include "../types/inc/viewport.hpp" - -namespace Microsoft::Console::Render -{ - class IRenderTarget - { - 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; - virtual void TriggerRedrawCursor(const COORD* const pcoord) = 0; - - virtual void TriggerRedrawAll() = 0; - virtual void TriggerTeardown() noexcept = 0; - - virtual void TriggerSelection() = 0; - virtual void TriggerScroll() = 0; - virtual void TriggerScroll(const COORD* const pcoordDelta) = 0; - virtual void TriggerCircling() = 0; - virtual void TriggerTitleChange() = 0; - - virtual void TriggerNewTextNotification(const std::wstring_view newText) = 0; - }; - - inline Microsoft::Console::Render::IRenderTarget::~IRenderTarget() {} - -} diff --git a/src/renderer/inc/RenderSettings.hpp b/src/renderer/inc/RenderSettings.hpp index d90283c9f6f..110075b2969 100644 --- a/src/renderer/inc/RenderSettings.hpp +++ b/src/renderer/inc/RenderSettings.hpp @@ -41,7 +41,7 @@ namespace Microsoft::Console::Render size_t GetColorAliasIndex(const ColorAlias alias) const noexcept; std::pair GetAttributeColors(const TextAttribute& attr) const noexcept; std::pair GetAttributeColorsWithAlpha(const TextAttribute& attr) const noexcept; - void ToggleBlinkRendition(class IRenderTarget& renderTarget) noexcept; + void ToggleBlinkRendition(class Renderer& renderer) noexcept; private: til::enumset _renderMode{ Mode::BlinkAllowed, Mode::IntenseIsBright }; diff --git a/src/types/IBaseData.h b/src/types/IBaseData.h index 58804c38269..d8ba21d8711 100644 --- a/src/types/IBaseData.h +++ b/src/types/IBaseData.h @@ -15,6 +15,7 @@ Author(s): #pragma once #include "inc/viewport.hpp" +#include "../renderer/inc/FontInfo.hpp" class TextBuffer; From d5fb7369cb858161fe159cd2d7a73b5a48740415 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 15 Mar 2022 21:24:29 +0100 Subject: [PATCH 47/75] DxEngine: Fix shader compilation on pre-D3D11 hardware (#12677) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Drop engine support for DirectX 9.1 Practically no one has such old hardware anymore and AtlasEngine additionally drops support for 10.0. The fallback also didn't work properly, because the `FeatureLevels` array failed to include 9.2 and 9.3. We'll simply fall back to WARP on all such devices. * Optimize shaders during compilation The two new flags increase shader performance sometimes significantly. * Fix shader feature level flags D3D feature level 10.0 only support 4.0 and 10.1 only 4.1 shaders. ## PR Checklist * [x] Closes #12655 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Add `WindowsTerminal.exe` in `dxcpl.exe` * Add a basic `experimental.pixelShaderPath` * All forced feature levels between `9_1` and `11_1` render as expected ✅ --- src/renderer/dx/DxRenderer.cpp | 51 ++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 18b607fb70c..265160cc983 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -187,11 +187,7 @@ DxEngine::~DxEngine() // - entry - Entry function of shader // Return Value: // - Compiled binary. Errors are thrown and logged. -inline Microsoft::WRL::ComPtr -_CompileShader( - std::string source, - std::string target, - std::string entry = "main") +static Microsoft::WRL::ComPtr _CompileShader(const std::string_view& source, const char* target) { #if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED THROW_HR(E_UNEXPECTED); @@ -201,24 +197,24 @@ _CompileShader( Microsoft::WRL::ComPtr error{}; const HRESULT hr = D3DCompile( - source.c_str(), + source.data(), source.size(), nullptr, nullptr, nullptr, - entry.c_str(), - target.c_str(), - 0, + "main", + target, + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &code, &error); if (FAILED(hr)) { - LOG_HR_MSG(hr, "D3DCompile failed with %x.", static_cast(hr)); + LOG_HR_MSG(hr, "D3DCompile failed with %08x", hr); if (error) { - LOG_HR_MSG(hr, "D3DCompile error\n%*S", static_cast(error->GetBufferSize()), static_cast(error->GetBufferPointer())); + LOG_HR_MSG(hr, "D3DCompile error\n%S", static_cast(error->GetBufferPointer())); } THROW_HR(hr); @@ -351,15 +347,33 @@ HRESULT DxEngine::_SetupTerminalEffects() vp.TopLeftY = 0; _d3dDeviceContext->RSSetViewports(1, &vp); + const char* shaderTargetVS = nullptr; + const char* shaderTargetPS = nullptr; + switch (_d3dDevice->GetFeatureLevel()) + { + case D3D_FEATURE_LEVEL_10_0: + shaderTargetVS = "vs_4_0"; + shaderTargetPS = "ps_4_0"; + break; + case D3D_FEATURE_LEVEL_10_1: + shaderTargetVS = "vs_4_1"; + shaderTargetPS = "ps_4_1"; + break; + default: + shaderTargetVS = "vs_5_0"; + shaderTargetPS = "ps_5_0"; + break; + } + // Prepare shaders. - auto vertexBlob = _CompileShader(screenVertexShaderString, "vs_5_0"); + auto vertexBlob = _CompileShader(&screenVertexShaderString[0], shaderTargetVS); Microsoft::WRL::ComPtr pixelBlob; // As the pixel shader source is user provided it's possible there's a problem with it // so load it inside a try catch, on any error log and fallback on the error pixel shader // If even the error pixel shader fails to load rely on standard exception handling try { - pixelBlob = _CompileShader(pixelShaderSource, "ps_5_0"); + pixelBlob = _CompileShader(pixelShaderSource, shaderTargetPS); } catch (...) { @@ -542,11 +556,12 @@ try // D3D11_CREATE_DEVICE_DEBUG | D3D11_CREATE_DEVICE_SINGLETHREADED; - 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 }; + static constexpr std::array FeatureLevels{ + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; // 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 From ee83081b642c65ba144a635c46cc34cc3bcc02e8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 16 Mar 2022 00:06:16 +0100 Subject: [PATCH 48/75] Allow fragments to override the name of new profiles (#12627) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After this commit we only set the default fields of a profile - primarily the name field - as late as possible, after layering has already completed. This ensures that we pick up any modifications from fragments. ## PR Checklist * [x] Closes #12520 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Add a fragment at `%LocalAppData%\Microsoft\Windows Terminal\Fragments\Fragment\fragment.json` with `{"profiles":[{"updates":"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}","name":"NewName"}]}` * Windows PowerShell profile is created with the name "NewName" in settings.json ✅ --- .../DeserializationTests.cpp | 6 ++- .../CascadiaSettings.cpp | 2 + .../TerminalSettingsModel/CascadiaSettings.h | 2 +- .../CascadiaSettingsSerialization.cpp | 52 +++++++++++++++---- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp index c20a9a853e4..a623a204204 100644 --- a/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/DeserializationTests.cpp @@ -1983,7 +1983,7 @@ namespace SettingsModelLocalTests } } - // This test ensures GH#11597 doesn't regress. + // This test ensures GH#11597, GH#12520 don't regress. void DeserializationTests::LoadFragmentsWithMultipleUpdates() { static constexpr std::wstring_view fragmentSource{ L"fragment" }; @@ -1991,7 +1991,7 @@ namespace SettingsModelLocalTests "profiles": [ { "updates": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", - "cursorShape": "filledBox" + "name": "NewName" }, { "updates": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}", @@ -2011,5 +2011,7 @@ namespace SettingsModelLocalTests VERIFY_IS_FALSE(loader.duplicateProfile); VERIFY_ARE_EQUAL(3u, loader.userSettings.profiles.size()); + // GH#12520: Fragments should be able to override the name of builtin profiles. + VERIFY_ARE_EQUAL(L"NewName", loader.userSettings.profiles[0]->Name()); } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index b3f49688a26..55456867f91 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -31,6 +31,8 @@ using namespace Microsoft::Console; // which is why this unsafety wasn't further abstracted away. winrt::com_ptr Model::implementation::CreateChild(const winrt::com_ptr& parent) { + // If you add more fields here, make sure to do the same in + // SettingsLoader::_addUserProfileParent(). auto profile = winrt::make_self(); profile->Origin(OriginTag::User); profile->Name(parent->Name()); diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index 772f7625beb..381a5148ce6 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -84,7 +84,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation static JsonSettings _parseJson(const std::string_view& content); static winrt::com_ptr _parseProfile(const OriginTag origin, const winrt::hstring& source, const Json::Value& profileJson); void _appendProfile(winrt::com_ptr&& profile, const winrt::guid& guid, ParsedSettings& settings); - static void _addParentProfile(const winrt::com_ptr& profile, ParsedSettings& settings); + void _addUserProfileParent(const winrt::com_ptr& profile); void _executeGenerator(const IDynamicProfileGenerator& generator); std::unordered_set _ignoredNamespaces; diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp index e008c5e6d61..6d50e3990b9 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettingsSerialization.cpp @@ -185,7 +185,7 @@ void SettingsLoader::MergeInboxIntoUserSettings() { for (const auto& profile : inboxSettings.profiles) { - _addParentProfile(profile, userSettings); + _addUserProfileParent(profile); } } @@ -314,7 +314,17 @@ void SettingsLoader::FinalizeLayering() for (const auto& profile : userSettings.profiles) { profile->AddMostImportantParent(userSettings.baseLayerProfile); + + // This completes the parenting process that was started in _addUserProfileParent(). profile->_FinalizeInheritance(); + if (profile->Origin() == OriginTag::None) + { + // If you add more fields here, make sure to do the same in + // implementation::CreateChild(). + profile->Origin(OriginTag::User); + profile->Name(profile->Name()); + profile->Hidden(profile->Hidden()); + } } } @@ -550,7 +560,7 @@ void SettingsLoader::_parseFragment(const winrt::hstring& source, const std::str } else { - _addParentProfile(fragmentProfile, userSettings); + _addUserProfileParent(fragmentProfile); } } @@ -613,9 +623,9 @@ void SettingsLoader::_appendProfile(winrt::com_ptr&& profile, const win // If the given ParsedSettings instance contains a profile with the given profile's GUID, // the profile is added as a parent. Otherwise a new child profile is created. -void SettingsLoader::_addParentProfile(const winrt::com_ptr& profile, ParsedSettings& settings) +void SettingsLoader::_addUserProfileParent(const winrt::com_ptr& profile) { - if (const auto [it, inserted] = settings.profilesByGuid.emplace(profile->Guid(), profile); !inserted) + if (const auto [it, inserted] = userSettings.profilesByGuid.emplace(profile->Guid(), nullptr); !inserted) { // If inserted is false, we got a matching user profile with identical GUID. // --> The generated profile is a parent of the existing user profile. @@ -623,14 +633,36 @@ void SettingsLoader::_addParentProfile(const winrt::com_ptrsecond with the (new) child profile. + // + // These additional things are required to complete a (user) profile: + // * A call to _FinalizeInheritance() + // * Every profile should at least have Origin(), Name() and Hidden() set + // They're handled by SettingsLoader::FinalizeLayering() and detected by + // the missing Origin(). Setting these fields as late as possible ensures + // that we pick up the correct, inherited values of all of the child's parents. + // + // If you add more fields here, make sure to do the same in + // implementation::CreateChild(). + auto child = winrt::make_self(); + child->AddLeastImportantParent(profile); + child->Guid(profile->Guid()); + + // If profile is a dynamic/generated profile, a fragment's + // Source() should have no effect on this user profile. + if (profile->HasSource()) + { + child->Source(profile->Source()); + } + + it->second = child; + userSettings.profiles.emplace_back(std::move(child)); } } From e5b5af5186832bcde80396479fb6e6d5e91774a8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 16 Mar 2022 21:38:40 +0100 Subject: [PATCH 49/75] Fix exception spam if UIA is running (#12698) `std::basic_string_view::substr` throws an exception if the first argument (offset) is out of range. If UIA is running, this creates _a lot_ of exceptions and associated log output. This trivial change takes care of that. --- src/renderer/uia/UiaRenderer.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/renderer/uia/UiaRenderer.cpp b/src/renderer/uia/UiaRenderer.cpp index 0a87ce0e619..1b31b6f4916 100644 --- a/src/renderer/uia/UiaRenderer.cpp +++ b/src/renderer/uia/UiaRenderer.cpp @@ -299,14 +299,9 @@ void UiaEngine::WaitUntilCanRender() noexcept // the output isn't cut off. static constexpr size_t sapiLimit{ 1000 }; const std::wstring_view output{ _queuedOutput }; - for (size_t offset = 0;; offset += sapiLimit) + for (size_t offset = 0; offset < output.size(); offset += sapiLimit) { - const auto croppedText{ output.substr(offset, sapiLimit) }; - if (croppedText.empty()) - { - break; - } - _dispatcher->NotifyNewOutput(croppedText); + _dispatcher->NotifyNewOutput(output.substr(offset, sapiLimit)); } } CATCH_LOG(); From 5072ee640fd6f50b7681e37e8e8be76ef32d4767 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 17 Mar 2022 20:13:33 +0100 Subject: [PATCH 50/75] Fix uninitialized memory access in GetConsoleTitleA test (#12699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `WideCharToMultiByte` doesn't write a final null-byte by default. `til::u16u8` avoids the problem. ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Test passes in Debug builds ✅ --- src/host/ut_host/ApiRoutinesTests.cpp | 28 ++++----------------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/host/ut_host/ApiRoutinesTests.cpp b/src/host/ut_host/ApiRoutinesTests.cpp index a8dfcf4c143..36e8a156c01 100644 --- a/src/host/ut_host/ApiRoutinesTests.cpp +++ b/src/host/ut_host/ApiRoutinesTests.cpp @@ -215,30 +215,10 @@ class ApiRoutinesTests TEST_METHOD(ApiGetConsoleTitleA) { CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.SetTitle(L"Test window title."); - - const auto title = gci.GetTitle(); - - int const iBytesNeeded = WideCharToMultiByte(gci.OutputCP, - 0, - title.data(), - gsl::narrow_cast(title.size()), - nullptr, - 0, - nullptr, - nullptr); - wistd::unique_ptr pszExpected = wil::make_unique_nothrow(iBytesNeeded); - VERIFY_IS_NOT_NULL(pszExpected); - - VERIFY_WIN32_BOOL_SUCCEEDED(WideCharToMultiByte(gci.OutputCP, - 0, - title.data(), - gsl::narrow_cast(title.size()), - pszExpected.get(), - iBytesNeeded, - nullptr, - nullptr)); + // SetTitle() runs some extra code. Let's not skip it since this is a test. + gci.SetTitle(L"Test window title."); + const auto pszExpected = til::u16u8(gci.GetTitle()); char pszTitle[MAX_PATH]; // most applications use MAX_PATH size_t cchWritten = 0; @@ -249,7 +229,7 @@ class ApiRoutinesTests // NOTE: W version of API returns string length. A version of API returns buffer length (string + null). VERIFY_ARE_EQUAL(gci.GetTitle().length() + 1, cchWritten); VERIFY_ARE_EQUAL(gci.GetTitle().length(), cchNeeded); - VERIFY_ARE_EQUAL(WEX::Common::String(pszExpected.get()), WEX::Common::String(pszTitle)); + VERIFY_ARE_EQUAL(std::string_view{ pszExpected }, std::string_view{ pszTitle }); } TEST_METHOD(ApiGetConsoleTitleW) From 830a422c015a64cb37196c0b55c2d35d79692fc3 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 17 Mar 2022 20:45:48 +0100 Subject: [PATCH 51/75] AtlasEngine: Fix ClearType being always enabled (#12705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HLSL uses 32-bit booleans, while C++ uses 8-bit ones aligned by 32-bit. This meant that the shader was accessing uninitialized memory forcing ClearType blending to be randomly enabled. This regressed in commit 5964060. ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * ClearType blending works ✅ * Enabling transparent backgrounds forces grayscale blending ✅ --- src/renderer/atlas/AtlasEngine.h | 4 +++- src/renderer/atlas/shader_ps.hlsl | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/atlas/AtlasEngine.h b/src/renderer/atlas/AtlasEngine.h index 11ad2026a43..1645544daba 100644 --- a/src/renderer/atlas/AtlasEngine.h +++ b/src/renderer/atlas/AtlasEngine.h @@ -555,6 +555,8 @@ namespace Microsoft::Console::Render // * Members cannot straddle 16 byte boundaries // This means a structure like {u32; u32; u32; u32x2} would require // padding so that it is {u32; u32; u32; <4 byte padding>; u32x2}. + // * bool will probably not work the way you want it to, + // because HLSL uses 32-bit bools and C++ doesn't. alignas(sizeof(f32x4)) f32x4 viewport; alignas(sizeof(f32x4)) f32 gammaRatios[4]{}; alignas(sizeof(f32)) f32 enhancedContrast = 0; @@ -565,7 +567,7 @@ namespace Microsoft::Console::Render alignas(sizeof(u32)) u32 backgroundColor = 0; alignas(sizeof(u32)) u32 cursorColor = 0; alignas(sizeof(u32)) u32 selectionColor = 0; - alignas(sizeof(u32)) bool useClearType = 0; + alignas(sizeof(u32)) u32 useClearType = 0; #pragma warning(suppress : 4324) // 'ConstBuffer': structure was padded due to alignment specifier }; diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index 787959ad736..fadbf3469d5 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -48,7 +48,7 @@ cbuffer ConstBuffer : register(b0) uint backgroundColor; uint cursorColor; uint selectionColor; - bool useClearType; + uint useClearType; }; StructuredBuffer cells : register(t0); Texture2D glyphs : register(t1); From 2de2ae23629db438edf916b3e62368e60074c85b Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Fri, 18 Mar 2022 16:54:48 -0500 Subject: [PATCH 52/75] Hush a garrulous event from TerminalControl (#12723) Fixes MSFT-36708760 --- src/inc/WilErrorReporting.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/inc/WilErrorReporting.h b/src/inc/WilErrorReporting.h index 8f7a3dfbc79..41efd625e4d 100644 --- a/src/inc/WilErrorReporting.h +++ b/src/inc/WilErrorReporting.h @@ -16,6 +16,16 @@ namespace Microsoft::Console::ErrorReporting __declspec(noinline) inline void WINAPI ReportFailureToFallbackProvider(bool alreadyReported, const wil::FailureInfo& failure) noexcept try { + if (failure.hr == 0x80131515L) + { + // XAML requires that we reply with this HR for the accessibility code in XamlUiaTextRange to work. + // Unfortunately, due to C++/WinRT, we have to _throw_ it. That results in us ending up here, + // trying to report the error to telemetry. It's not an actual error, per se, so we don't + // want to log it. It's also incredibly noisy, which results in bugs getting filed on us. + // See https://github.com/microsoft/cppwinrt/issues/798 for more discussion about throwing HRESULTs. + return; + } + if (!alreadyReported && FallbackProvider) { #pragma warning(suppress : 26477) // Use 'nullptr' rather than 0 or NULL From 6bc2b4af09cf3032e2fccd405302ffac70fd348c Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 19 Mar 2022 00:55:25 +0100 Subject: [PATCH 53/75] Make conhost history deduplication case-sensitive (#12700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The legacy console used to use case-sensitive history deduplication and this change reverts the logic to restore ye olde history functionality. This commit additionally changes the other remaining `std::equal` plus `std::towlower` check into a `CompareStringOrdinal` call, just because that's what MSDN suggests to use in such situations. ## PR Checklist * [x] Closes #4186 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Enter `test /v` * Enter `test /V` * Browsing through the history yields both items ✅ --- src/host/history.cpp | 13 ++----------- src/host/ut_host/CommandLineTests.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/host/history.cpp b/src/host/history.cpp index 2b5adf86290..5c655d647e4 100644 --- a/src/host/history.cpp +++ b/src/host/history.cpp @@ -72,14 +72,9 @@ void CommandHistory::s_ResizeAll(const size_t commands) } } -static bool CaseInsensitiveEquality(wchar_t a, wchar_t b) -{ - return ::towlower(a) == ::towlower(b); -} - bool CommandHistory::IsAppNameMatch(const std::wstring_view other) const { - return std::equal(_appName.cbegin(), _appName.cend(), other.cbegin(), other.cend(), CaseInsensitiveEquality); + return CompareStringOrdinal(_appName.data(), gsl::narrow(_appName.size()), other.data(), gsl::narrow(other.size()), TRUE) == CSTR_EQUAL; } // Routine Description: @@ -534,11 +529,7 @@ std::wstring CommandHistory::Remove(const SHORT iDel) const auto& storedCommand = _commands.at(indexFound); if ((WI_IsFlagClear(options, MatchOptions::ExactMatch) && (givenCommand.size() <= storedCommand.size())) || (givenCommand.size() == storedCommand.size())) { - if (std::equal(storedCommand.begin(), - storedCommand.begin() + givenCommand.size(), - givenCommand.begin(), - givenCommand.end(), - CaseInsensitiveEquality)) + if (til::starts_with(storedCommand, givenCommand)) { return true; } diff --git a/src/host/ut_host/CommandLineTests.cpp b/src/host/ut_host/CommandLineTests.cpp index 78f253b1b13..6062690ea2a 100644 --- a/src/host/ut_host/CommandLineTests.cpp +++ b/src/host/ut_host/CommandLineTests.cpp @@ -450,20 +450,21 @@ class CommandLineTests VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false)); VERIFY_SUCCEEDED(m_pHistory->Add(L"short and stout", false)); VERIFY_SUCCEEDED(m_pHistory->Add(L"inflammable", false)); + VERIFY_SUCCEEDED(m_pHistory->Add(L"Indestructible", false)); - SetPrompt(cookedReadData, L"i"); + SetPrompt(cookedReadData, L"I"); auto& commandLine = CommandLine::Instance(); commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); - VerifyPromptText(cookedReadData, L"inflammable"); + VerifyPromptText(cookedReadData, L"Indestructible"); - // make sure we skip to the next "i" history item + // make sure we skip to the next "I" history item commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); VerifyPromptText(cookedReadData, L"I'm a little teapot"); // should cycle back to the start of the command history commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); - VerifyPromptText(cookedReadData, L"inflammable"); + VerifyPromptText(cookedReadData, L"Indestructible"); } TEST_METHOD(CmdlineCtrlHomeFullwidthChars) From 8a34a0e59ac41bc805c308fb82bc7683e583d45d Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Sat, 19 Mar 2022 01:35:26 -0500 Subject: [PATCH 54/75] Clear out g.pRender on cleanup so further tests don't explode (#12724) The x86 Conhost UTs were failing because--when run in a specific order (SearchTests, then SelectionInputTests)--PrepareGlobalScreenBuffer would attempt to enable painting on a renderer that was dead and gone and pushing up daisies. --- src/inc/test/CommonState.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/inc/test/CommonState.hpp b/src/inc/test/CommonState.hpp index 909f8c58131..791782cce98 100644 --- a/src/inc/test/CommonState.hpp +++ b/src/inc/test/CommonState.hpp @@ -86,8 +86,9 @@ class CommonState void CleanupGlobalRenderer() { - const Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); + Globals& g = Microsoft::Console::Interactivity::ServiceLocator::LocateGlobals(); delete g.pRender; + g.pRender = nullptr; } void PrepareGlobalScreenBuffer(const short viewWidth = s_csWindowWidth, From 855e1360c0ff810decf862f1d90e15b5f49e7bbd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 21 Mar 2022 12:02:36 -0500 Subject: [PATCH 55/75] Manually copy trailing attributes on a resize (#12637) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## THE WHITE WHALE This is a fairly naive fix for this bug. It's not terribly performant, but neither is resize in the first place. When the buffer gets resized, typically we only copy the text up to the `MeasureRight` point, the last printable char in the row. Then we'd just use the last char's attributes to fill the remainder of the row. Instead, this PR changes how reflow behaves when it gets to the end of the row. After we finish copying text, then manually walk through the attributes at the end of the row, and copy them over. This ensures that cells that just have a colored space in them get copied into the new buffer as well, and we don't just blat the last character's attributes into the rest of the row. We'll do a similar thing once we get to the last printable char in the buffer, copying the remaining attributes. This could DEFINITELY be more performant. I think this current implementation walks the attrs _on every cell_, then appends the new attrs to the new ATTR_ROW. That could be optimized by just using the actual iterator. The copy after the last printable char bit is also especially bad in this regard. That could likely be a blind copy - I just wanted to get this into the world. Finally, we now copy the final attributes to the correct buffer: the new one. We used to copy them to the _old_ buffer, which we were about to destroy. ## Validation I'll add more gifs in the morning, not enough time to finish spinning a release Terminal build with this tonight. Closes #32 🎉🎉🎉🎉🎉🎉🎉🎉🎉 Closes #12567 --- .../templates/build-console-steps.yml | 2 + src/buffer/out/textBuffer.cpp | 93 ++++++- .../ConptyRoundtripTests.cpp | 2 +- src/host/screenInfo.cpp | 2 +- src/host/ut_host/ScreenBufferTests.cpp | 243 ++++++++++++++++++ 5 files changed, 334 insertions(+), 8 deletions(-) diff --git a/build/pipelines/templates/build-console-steps.yml b/build/pipelines/templates/build-console-steps.yml index 89e091ff736..dd609988604 100644 --- a/build/pipelines/templates/build-console-steps.yml +++ b/build/pipelines/templates/build-console-steps.yml @@ -110,6 +110,8 @@ steps: $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.dll $(Build.SourcesDirectory)/bin/$(RationalizedBuildPlatform)/$(BuildConfiguration)/*.xml **/Microsoft.VCLibs.*.appx + **/*unit.test*.dll + **/*unit.test*.manifest **/TestHostApp/*.exe **/TestHostApp/*.dll **/TestHostApp/*.xml diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 66b9980d26c..c59a7308ead 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2251,7 +2251,8 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, bool foundOldVisible = false; HRESULT hr = S_OK; // Loop through all the rows of the old buffer and reprint them into the new buffer - for (short iOldRow = 0; iOldRow < cOldRowsTotal; iOldRow++) + short iOldRow = 0; + for (; iOldRow < cOldRowsTotal; iOldRow++) { // Fetch the row and its "right" which is the last printable character. const ROW& row = oldBuffer.GetRowByOffset(iOldRow); @@ -2295,7 +2296,11 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, // Loop through every character in the current row (up to // the "right" boundary, which is one past the final valid // character) - for (short iOldCol = 0; iOldCol < iRight; iOldCol++) + short iOldCol = 0; + auto chars{ row.GetCharRow().cbegin() }; + auto attrs{ row.GetAttrRow().begin() }; + const auto copyRight = iRight; + for (; iOldCol < copyRight; iOldCol++) { if (iOldCol == cOldCursorPos.X && iOldRow == cOldCursorPos.Y) { @@ -2306,9 +2311,9 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, try { // TODO: MSFT: 19446208 - this should just use an iterator and the inserter... - const auto glyph = row.GetCharRow().GlyphAt(iOldCol); - const auto dbcsAttr = row.GetCharRow().DbcsAttrAt(iOldCol); - const auto textAttr = row.GetAttrRow().GetAttrByColumn(iOldCol); + const auto glyph = chars->Char(); + const auto dbcsAttr = chars->DbcsAttr(); + const auto textAttr = *attrs; if (!newBuffer.InsertCharacter(glyph, dbcsAttr, textAttr)) { @@ -2317,6 +2322,54 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, } } CATCH_RETURN(); + + ++chars; + ++attrs; + } + + // GH#32: Copy the attributes from the rest of the row into this new buffer. + // From where we are in the old buffer, to the end of the row, copy the + // remaining attributes. + // - if the old buffer is smaller than the new buffer, then just copy + // what we have, as it was. We already copied all _text_ with colors, + // but it's possible for someone to just put some color into the + // buffer to the right of that without any text (as just spaces). The + // buffer looks weird to the user when we resize and it starts losing + // those colors, so we need to copy them over too... as long as there + // is space. The last attr in the row will be extended to the end of + // the row in the new buffer. + // - if the old buffer is WIDER, than we might have wrapped onto a new + // line. Use the cursor's position's Y so that we know where the new + // row is, and start writing at the cursor position. Again, the attr + // in the last column of the old row will be extended to the end of the + // row that the text was flowed onto. + // - if the text in the old buffer didn't actually fill the whole + // line in the new buffer, then we didn't wrap. That's fine. just + // copy attributes from the old row till the end of the new row, and + // move on. + const auto newRowY = newCursor.GetPosition().Y; + auto& newRow = newBuffer.GetRowByOffset(newRowY); + auto newAttrColumn = newCursor.GetPosition().X; + const auto newWidth = newBuffer.GetLineWidth(newRowY); + // Stop when we get to the end of the buffer width, or the new position + // for inserting an attr would be past the right of the new buffer. + for (short copyAttrCol = iOldCol; + copyAttrCol < cOldColsTotal && newAttrColumn < newWidth; + copyAttrCol++) + { + try + { + // TODO: MSFT: 19446208 - this should just use an iterator and the inserter... + const auto textAttr = *attrs; + if (!newRow.GetAttrRow().SetAttrToEnd(newAttrColumn, textAttr)) + { + break; + } + } + CATCH_LOG(); // Not worth dying over. + + ++newAttrColumn; + ++attrs; } // If we found the old row that the caller was interested in, set the @@ -2352,7 +2405,7 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, // only because we ran out of space. if (iRight < cOldColsTotal && !row.WasWrapForced()) { - if (iRight == cOldCursorPos.X && iOldRow == cOldCursorPos.Y) + if (!fFoundCursorPos && (iRight == cOldCursorPos.X && iOldRow == cOldCursorPos.Y)) { cNewCursorPos = newCursor.GetPosition(); fFoundCursorPos = true; @@ -2403,6 +2456,34 @@ HRESULT TextBuffer::Reflow(TextBuffer& oldBuffer, } } } + + // Finish copying buffer attributes to remaining rows below the last + // printable character. This is to fix the `color 2f` scenario, where you + // change the buffer colors then resize and everything below the last + // printable char gets reset. See GH #12567 + auto newRowY = newCursor.GetPosition().Y + 1; + const auto newHeight = newBuffer.GetSize().Height(); + const auto oldHeight = oldBuffer.GetSize().Height(); + for (; + iOldRow < oldHeight && newRowY < newHeight; + iOldRow++) + { + const ROW& row = oldBuffer.GetRowByOffset(iOldRow); + + // Optimization: Since all these rows are below the last printable char, + // we can reasonably assume that they are filled with just spaces. + // That's convenient, we can just copy the attr row from the old buffer + // into the new one, and resize the row to match. We'll rely on the + // behavior of ATTR_ROW::Resize to trim down when narrower, or extend + // the last attr when wider. + auto& newRow = newBuffer.GetRowByOffset(newRowY); + const auto newWidth = newBuffer.GetLineWidth(newRowY); + newRow.GetAttrRow() = row.GetAttrRow(); + newRow.GetAttrRow().Resize(newWidth); + + newRowY++; + } + if (SUCCEEDED(hr)) { // Finish copying remaining parameters from the old text buffer to the new one diff --git a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp index f8b537bc731..35a6f8a6665 100644 --- a/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp +++ b/src/cascadia/UnitTests_TerminalCore/ConptyRoundtripTests.cpp @@ -2964,7 +2964,7 @@ void ConptyRoundtripTests::ResizeInitializeBufferWithDefaultAttrs() else if (leaveTrailingChar && row == 3) { auto iter = TestUtils::VerifyLineContains(tb, { 0, row }, L'#', greenAttrs, 1u); - TestUtils::VerifyLineContains(iter, L' ', (afterResize ? greenAttrs : actualDefaultAttrs), static_cast(width - 1)); + TestUtils::VerifyLineContains(iter, L' ', (actualDefaultAttrs), static_cast(width - 1)); } else { diff --git a/src/host/screenInfo.cpp b/src/host/screenInfo.cpp index 75b1585b5ac..49dea7ba74b 100644 --- a/src/host/screenInfo.cpp +++ b/src/host/screenInfo.cpp @@ -1472,7 +1472,7 @@ bool SCREEN_INFORMATION::IsMaximizedY() const coordCursorHeightDiff.Y = sCursorHeightInViewportAfter - sCursorHeightInViewportBefore; LOG_IF_FAILED(SetViewportOrigin(false, coordCursorHeightDiff, true)); - _textBuffer->SetCurrentAttributes(oldPrimaryAttributes); + newTextBuffer->SetCurrentAttributes(oldPrimaryAttributes); _textBuffer.swap(newTextBuffer); } diff --git a/src/host/ut_host/ScreenBufferTests.cpp b/src/host/ut_host/ScreenBufferTests.cpp index a75636278f8..5315687810c 100644 --- a/src/host/ut_host/ScreenBufferTests.cpp +++ b/src/host/ut_host/ScreenBufferTests.cpp @@ -17,6 +17,8 @@ #include "../../inc/conattrs.hpp" #include "../../types/inc/Viewport.hpp" +#include "../../cascadia/UnitTests_TerminalCore/TestUtils.h" + #include using namespace WEX::Common; @@ -26,6 +28,7 @@ using namespace Microsoft::Console::Types; using namespace Microsoft::Console::Interactivity; using namespace Microsoft::Console::Render; using namespace Microsoft::Console::VirtualTerminal; +using namespace TerminalCoreUnitTests; class ScreenBufferTests { @@ -224,6 +227,10 @@ class ScreenBufferTests TEST_METHOD(RetainHorizontalOffsetWhenMovingToBottom); TEST_METHOD(TestWriteConsoleVTQuirkMode); + + TEST_METHOD(TestReflowEndOfLineColor); + TEST_METHOD(TestReflowSmallerLongLineWithColor); + TEST_METHOD(TestReflowBiggerLongLineWithColor); }; void ScreenBufferTests::SingleAlternateBufferCreationTest() @@ -6269,3 +6276,239 @@ void ScreenBufferTests::TestWriteConsoleVTQuirkMode() verifyLastAttribute(vtWhiteOnBlack256Attribute); } } + +void ScreenBufferTests::TestReflowEndOfLineColor() +{ + BEGIN_TEST_METHOD_PROPERTIES() + TEST_METHOD_PROPERTY(L"Data:dx", L"{-1, 0, 1}") + TEST_METHOD_PROPERTY(L"Data:dy", L"{-1, 0, 1}") + END_TEST_METHOD_PROPERTIES(); + + INIT_TEST_PROPERTY(int, dx, L"The change in width of the buffer"); + INIT_TEST_PROPERTY(int, dy, L"The change in height of the buffer"); + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& stateMachine = si.GetStateMachine(); + + gci.SetWrapText(true); + + auto defaultAttrs = si.GetAttributes(); + auto red = defaultAttrs; + red.SetIndexedBackground(TextColor::DARK_RED); + auto green = defaultAttrs; + green.SetIndexedBackground(TextColor::DARK_GREEN); + auto blue = defaultAttrs; + blue.SetIndexedBackground(TextColor::DARK_BLUE); + auto yellow = defaultAttrs; + yellow.SetIndexedBackground(TextColor::DARK_YELLOW); + + Log::Comment(L"Make sure the viewport is at 0,0"); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, COORD({ 0, 0 }), true)); + + Log::Comment(L"Fill buffer with some data"); + stateMachine.ProcessString(L"\x1b[H"); + stateMachine.ProcessString(L"\x1b[41m"); // Red BG + stateMachine.ProcessString(L"AAAAA"); // AAAAA + stateMachine.ProcessString(L"\x1b[42m"); // Green BG + stateMachine.ProcessString(L"\nBBBBB\n"); // BBBBB + stateMachine.ProcessString(L"\x1b[44m"); // Blue BG + stateMachine.ProcessString(L" CCC \n"); // " abc " (with spaces on either side) + stateMachine.ProcessString(L"\x1b[43m"); // yellow BG + stateMachine.ProcessString(L"\x1b[K"); // Erase line + stateMachine.ProcessString(L"\x1b[2;6H"); // move the cursor to the end of the BBBBB's + + auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& /*viewport*/, const bool /*before*/) { + const short width = tb.GetSize().Width(); + Log::Comment(NoThrowString().Format(L"Buffer width: %d", width)); + + auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L'A', red, 5u); + TestUtils::VerifyLineContains(iter0, L' ', defaultAttrs, static_cast(width - 5)); + + auto iter1 = tb.GetCellLineDataAt({ 0, 1 }); + TestUtils::VerifyLineContains(iter1, L'B', green, 5u); + TestUtils::VerifyLineContains(iter1, L' ', defaultAttrs, static_cast(width - 5)); + + auto iter2 = tb.GetCellLineDataAt({ 0, 2 }); + TestUtils::VerifyLineContains(iter2, L' ', blue, 1u); + TestUtils::VerifyLineContains(iter2, L'C', blue, 3u); + TestUtils::VerifyLineContains(iter2, L' ', blue, 1u); + TestUtils::VerifyLineContains(iter2, L' ', defaultAttrs, static_cast(width - 5)); + + auto iter3 = tb.GetCellLineDataAt({ 0, 3 }); + TestUtils::VerifyLineContains(iter3, L' ', yellow, static_cast(width)); + }; + + Log::Comment(L"========== Checking the buffer state (before) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, true); + + Log::Comment(L"========== resize buffer =========="); + const til::point delta{ dx, dy }; + const til::point oldSize{ si.GetBufferSize().Dimensions() }; + const til::point newSize{ oldSize + delta }; + VERIFY_SUCCEEDED(si.ResizeWithReflow(newSize.to_win32_coord())); + + Log::Comment(L"========== Checking the buffer state (after) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, false); +} + +void ScreenBufferTests::TestReflowSmallerLongLineWithColor() +{ + Log::Comment(L"Reflow the buffer such that a long, line of text flows onto " + L"the next line. Check that the trailing attributes were copied" + L" to the new row."); + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& stateMachine = si.GetStateMachine(); + + gci.SetWrapText(true); + + auto defaultAttrs = si.GetAttributes(); + auto red = defaultAttrs; + red.SetIndexedBackground(TextColor::DARK_RED); + auto green = defaultAttrs; + green.SetIndexedBackground(TextColor::DARK_GREEN); + auto blue = defaultAttrs; + blue.SetIndexedBackground(TextColor::DARK_BLUE); + auto yellow = defaultAttrs; + yellow.SetIndexedBackground(TextColor::DARK_YELLOW); + + Log::Comment(L"Make sure the viewport is at 0,0"); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, COORD({ 0, 0 }), true)); + + Log::Comment(L"Fill buffer with some data"); + stateMachine.ProcessString(L"\x1b[H"); + stateMachine.ProcessString(L"\x1b[41m"); // Red BG + stateMachine.ProcessString(L"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); // 35 A's + stateMachine.ProcessString(L"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); // 35 more, 70 total + stateMachine.ProcessString(L"\x1b[42m"); // Green BG + stateMachine.ProcessString(L" BBB \n"); // " BBB " with spaces on either side). + // Attributes fill 70 red, 5 green, the last 5 are {whatever it was before} + + auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& /*viewport*/, const bool before) { + const short width = tb.GetSize().Width(); + Log::Comment(NoThrowString().Format(L"Buffer width: %d", width)); + + if (before) + { + auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L'A', red, 70u); + TestUtils::VerifyLineContains(iter0, L' ', green, 1u); + TestUtils::VerifyLineContains(iter0, L'B', green, 3u); + TestUtils::VerifyLineContains(iter0, L' ', green, 1u); + TestUtils::VerifyLineContains(iter0, L' ', defaultAttrs, 5u); + } + else + { + auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L'A', red, 65u); + + auto iter1 = tb.GetCellLineDataAt({ 0, 1 }); + TestUtils::VerifyLineContains(iter1, L'A', red, 5u); + TestUtils::VerifyLineContains(iter1, L' ', green, 1u); + TestUtils::VerifyLineContains(iter1, L'B', green, 3u); + TestUtils::VerifyLineContains(iter1, L' ', green, 1u); + + // We don't want to necessarily verify the contents of the rest of the + // line, but we will anyways. Right now, we expect the last attrs on the + // original row to fill the remainder of the row it flowed onto. We may + // want to change that in the future. If we do, this check can always be + // changed. + TestUtils::VerifyLineContains(iter1, L' ', defaultAttrs, static_cast(width - 10)); + } + }; + + Log::Comment(L"========== Checking the buffer state (before) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, true); + + Log::Comment(L"========== resize buffer =========="); + const til::point delta{ -15, 0 }; + const til::point oldSize{ si.GetBufferSize().Dimensions() }; + const til::point newSize{ oldSize + delta }; + VERIFY_SUCCEEDED(si.ResizeWithReflow(newSize.to_win32_coord())); + + // Buffer is now 65 wide. 65 A's that wrapped onto the next row, where there + // are also 3 B's wrapped in spaces. + Log::Comment(L"========== Checking the buffer state (after) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, false); +} + +void ScreenBufferTests::TestReflowBiggerLongLineWithColor() +{ + Log::Comment(L"Reflow the buffer such that a wrapped line of text 'de-flows' onto the previous line."); + + auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); + auto& si = gci.GetActiveOutputBuffer(); + auto& stateMachine = si.GetStateMachine(); + + gci.SetWrapText(true); + + auto defaultAttrs = si.GetAttributes(); + auto red = defaultAttrs; + red.SetIndexedBackground(TextColor::DARK_RED); + auto green = defaultAttrs; + green.SetIndexedBackground(TextColor::DARK_GREEN); + auto blue = defaultAttrs; + blue.SetIndexedBackground(TextColor::DARK_BLUE); + auto yellow = defaultAttrs; + yellow.SetIndexedBackground(TextColor::DARK_YELLOW); + + Log::Comment(L"Make sure the viewport is at 0,0"); + VERIFY_SUCCEEDED(si.SetViewportOrigin(true, COORD({ 0, 0 }), true)); + + Log::Comment(L"Fill buffer with some data"); + stateMachine.ProcessString(L"\x1b[H"); + stateMachine.ProcessString(L"\x1b[41m"); // Red BG + stateMachine.ProcessString(L"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); // 40 A's + stateMachine.ProcessString(L"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); // 40 more, to wrap + stateMachine.ProcessString(L"AAAAA"); // 5 more. 85 total. + stateMachine.ProcessString(L"\x1b[42m"); // Green BG + stateMachine.ProcessString(L" BBB \n"); // " BBB " with spaces on either side). + // Attributes fill 85 red, 5 green, the rest are {whatever it was before} + + auto verifyBuffer = [&](const TextBuffer& tb, const til::rect& /*viewport*/, const bool before) { + const short width = tb.GetSize().Width(); + Log::Comment(NoThrowString().Format(L"Buffer width: %d", width)); + + if (before) + { + auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L'A', red, 80u); + + auto iter1 = tb.GetCellLineDataAt({ 0, 1 }); + TestUtils::VerifyLineContains(iter1, L'A', red, 5u); + TestUtils::VerifyLineContains(iter1, L' ', green, 1u); + TestUtils::VerifyLineContains(iter1, L'B', green, 3u); + TestUtils::VerifyLineContains(iter1, L' ', green, 1u); + TestUtils::VerifyLineContains(iter1, L' ', defaultAttrs, static_cast(width - 10)); + } + else + { + auto iter0 = TestUtils::VerifyLineContains(tb, { 0, 0 }, L'A', red, 85u); + TestUtils::VerifyLineContains(iter0, L' ', green, 1u); + TestUtils::VerifyLineContains(iter0, L'B', green, 3u); + TestUtils::VerifyLineContains(iter0, L' ', green, 1u); + + // We don't want to necessarily verify the contents of the rest of + // the line, but we will anyways. Differently than + // TestReflowSmallerLongLineWithColor: In this test, the since we're + // de-flowing row 1 onto row 0, and the trailing spaces in row 1 are + // default attrs, then that's the attrs we'll use to finish filling + // up the 0th row in the new buffer. Again, we may want to change + // that in the future. If we do, this check can always be changed. + TestUtils::VerifyLineContains(iter0, L' ', defaultAttrs, static_cast(width - 90)); + } + }; + + Log::Comment(L"========== Checking the buffer state (before) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, true); + + Log::Comment(L"========== resize buffer =========="); + const til::point delta{ 15, 0 }; + const til::point oldSize{ si.GetBufferSize().Dimensions() }; + const til::point newSize{ oldSize + delta }; + VERIFY_SUCCEEDED(si.ResizeWithReflow(newSize.to_win32_coord())); + + // Buffer is now 95 wide. 85 A's that de-flowed onto the first row, where + // there are also 3 B's wrapped in spaces, and finally 5 trailing spaces. + Log::Comment(L"========== Checking the buffer state (after) =========="); + verifyBuffer(si.GetTextBuffer(), til::rect{ si.GetViewport().ToInclusive() }, false); +} From 8f180c2b2db6d7781a71002e9272a5a430e374ac Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Mon, 21 Mar 2022 23:07:34 +0000 Subject: [PATCH 56/75] Merged PR 7088613: [Git2Git] Merged PR 7088552: Silence TVS by using %d (int) instead of %td (ptrdiff_t) [Git2Git] Merged PR 7088552: Silence TVS by using %d (int) instead of %td (ptrdiff_t) Silence TVS by using %d (int) instead of %td (ptrdiff_t) This commit also includes an automatic sources.dep fix. Fixes MSFT-38106841 Fixes MSFT-38106866 Related work items: #38106841, #38106866 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 5983633fe90cccdb1165ab28935bd704414bb6f2 Related work items: #38106841, #38106866 --- src/host/proxy/sources.dep | 3 +++ src/inc/til/point.h | 2 +- src/inc/til/size.h | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/host/proxy/sources.dep b/src/host/proxy/sources.dep index 6b254b27a36..47d8c3dbb10 100644 --- a/src/host/proxy/sources.dep +++ b/src/host/proxy/sources.dep @@ -1,3 +1,6 @@ +BUILD_PASS0_CONSUMES= \ + onecore\inetcore\published\sdk\inc|PASS0 \ + PUBLIC_PASS0_CONSUMES= \ minkernel\published\base|PASS0 \ onecore\com\published\idlole\publish|PASS0 \ diff --git a/src/inc/til/point.h b/src/inc/til/point.h index 4741a67c8d8..98bd50c6d54 100644 --- a/src/inc/til/point.h +++ b/src/inc/til/point.h @@ -226,7 +226,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" std::wstring to_string() const { - return wil::str_printf(L"(X:%td, Y:%td)", x, y); + return wil::str_printf(L"(X:%d, Y:%d)", x, y); } }; } diff --git a/src/inc/til/size.h b/src/inc/til/size.h index 22422bccf7d..199f836a3a0 100644 --- a/src/inc/til/size.h +++ b/src/inc/til/size.h @@ -206,7 +206,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" std::wstring to_string() const { - return wil::str_printf(L"[W:%td, H:%td]", width, height); + return wil::str_printf(L"[W:%d, H:%d]", width, height); } }; }; From d97d9f0fcf89c3c772fcf8c776321e5cb01e26e6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 22 Mar 2022 00:32:45 +0100 Subject: [PATCH 57/75] Force LTR / logical order for text in GdiEngine (#12722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some applications like `vim -H` implement their own BiDi reordering. Previously we used `PolyTextOutW` which supported such arrangements, but with a0527a1 and the switch to `ExtTextOutW` we broke such applications. This commit restores the old behavior by reimplementing the basics of `ExtTextOutW`'s internal workings while enforcing LTR ordering. ## Validation Steps Performed * Create a text file with "ץחסק פחופפסנ חס קוח ז׳חסש ץקקטק פחטסץ" Viewing the text file with `vim -H` presents the contents as expected ✅ * Printing enwik8 is as fast as before ✅ * Font fallback for various eastern scripts in enwik8 works as expected ✅ * `DECDWL` double-width sequences ✅ * Horizontal scrolling (apart from producing expected artifacts) ✅ Closes #12294 --- .github/actions/spelling/expect/expect.txt | 8 +++- src/renderer/gdi/gdirenderer.hpp | 6 ++- src/renderer/gdi/lib/gdi.vcxproj | 7 +++- src/renderer/gdi/paint.cpp | 49 ++++++++++++++++++++-- src/renderer/gdi/precomp.h | 3 +- src/renderer/gdi/state.cpp | 19 +++------ 6 files changed, 70 insertions(+), 22 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 0ae5cecb6a7..6b8bd612920 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -237,9 +237,9 @@ charlespetzold charset CHARSETINFO chcp -Checkin checkbox checkboxes +Checkin chh Childitem chk @@ -917,6 +917,7 @@ getwriter GFEh Gfun gfx +GGI GHIJK GHIJKL GHIJKLM @@ -1094,6 +1095,7 @@ ifndef IFont ifstream IGNOREEND +IGNORELANGUAGE IHigh IHosted iid @@ -1463,9 +1465,9 @@ mscorlib msctf msctls msdata +MSDL msdn msft -MSDL MSGCMDLINEF MSGF MSGFILTER @@ -2239,6 +2241,7 @@ srect srv srvinit srvpipe +ssa ssh sstream stackoverflow @@ -2565,6 +2568,7 @@ USESHOWWINDOW USESIZE USESTDHANDLES ushort +usp USRDLL utf utils diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 6ae3b0e20ea..8b0cab05b4d 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -91,6 +91,8 @@ namespace Microsoft::Console::Render const int nIndex, const LONG dwNewLong) noexcept; + static bool FontHasWesternScript(HDC hdc); + bool _fPaintStarted; til::rect _invalidCharacters; @@ -138,13 +140,15 @@ namespace Microsoft::Console::Render COLORREF _lastFg; COLORREF _lastBg; - enum class FontType : size_t + enum class FontType : uint8_t { + Undefined, Default, Italic, Soft }; FontType _lastFontType; + bool _fontHasWesternScript = false; XFORM _currentLineTransform; LineRendition _currentLineRendition; diff --git a/src/renderer/gdi/lib/gdi.vcxproj b/src/renderer/gdi/lib/gdi.vcxproj index 1a1b96b3b62..bf1fe0758f9 100644 --- a/src/renderer/gdi/lib/gdi.vcxproj +++ b/src/renderer/gdi/lib/gdi.vcxproj @@ -6,7 +6,7 @@ gdi RendererGdi ConRenderGdi - StaticLibrary + StaticLibrary @@ -22,6 +22,11 @@ + + + usp10.lib;%(AdditionalDependencies) + + diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 598ce348987..2a7c38ff443 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -11,6 +11,15 @@ using namespace Microsoft::Console::Render; +// This is an excerpt of GDI's FontHasWesternScript() as +// used by InternalTextOut() which is part of ExtTextOutW(). +bool GdiEngine::FontHasWesternScript(HDC hdc) +{ + WORD glyphs[4]; + return (GetGlyphIndicesW(hdc, L"dMr\"", 4, glyphs, GGI_MARK_NONEXISTING_GLYPHS) == 4) && + (glyphs[0] != 0xFFFF && glyphs[1] != 0xFFFF && glyphs[2] != 0xFFFF && glyphs[3] != 0xFFFF); +} + // Routine Description: // - Prepares internal structures for a painting operation. // Arguments: @@ -54,6 +63,8 @@ using namespace Microsoft::Console::Render; _debugContext = GetDC(_debugWindow); #endif + _lastFontType = FontType::Undefined; + return S_OK; } @@ -445,10 +456,42 @@ using namespace Microsoft::Console::Render; for (size_t i = 0; i != _cPolyText; ++i) { const auto& t = _pPolyText[i]; - if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags, &t.rcl, t.lpstr, t.n, t.pdx)) + + // The following if/else replicates the essentials of how ExtTextOutW() without ETO_IGNORELANGUAGE works. + // See InternalTextOut(). + // + // Unlike the original, we don't check for `GetTextCharacterExtra(hdc) != 0`, + // because we don't ever call SetTextCharacterExtra() anyways. + // + // GH#12294: + // Additionally we set ss.fOverrideDirection to TRUE, because we need to present RTL + // text in logical order in order to be compatible with applications like `vim -H`. + if (_fontHasWesternScript && ScriptIsComplex(t.lpstr, t.n, SIC_COMPLEX) == S_FALSE) { - hr = E_FAIL; - break; + if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags | ETO_IGNORELANGUAGE, &t.rcl, t.lpstr, t.n, t.pdx)) + { + hr = E_FAIL; + break; + } + } + else + { + SCRIPT_STATE ss{}; + ss.fOverrideDirection = TRUE; + + SCRIPT_STRING_ANALYSIS ssa; + hr = ScriptStringAnalyse(_hdcMemoryContext, t.lpstr, t.n, 0, -1, SSA_GLYPHS | SSA_FALLBACK, 0, nullptr, &ss, t.pdx, nullptr, nullptr, &ssa); + if (FAILED(hr)) + { + break; + } + + hr = ScriptStringOut(ssa, t.x, t.y, t.uiFlags, &t.rcl, 0, 0, FALSE); + std::ignore = ScriptStringFree(&ssa); + if (FAILED(hr)) + { + break; + } } } diff --git a/src/renderer/gdi/precomp.h b/src/renderer/gdi/precomp.h index 9043621d11e..4b402939d5e 100644 --- a/src/renderer/gdi/precomp.h +++ b/src/renderer/gdi/precomp.h @@ -16,8 +16,9 @@ Module Name: // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" -#include +#include #include +#include #ifndef _NTSTATUS_DEFINED #define _NTSTATUS_DEFINED diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 17b939591c2..0d36d39c6cd 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -29,7 +29,7 @@ GdiEngine::GdiEngine() : _fInvalidRectUsed(false), _lastFg(INVALID_COLOR), _lastBg(INVALID_COLOR), - _lastFontType(FontType::Default), + _lastFontType(FontType::Undefined), _currentLineTransform(IDENTITY_XFORM), _currentLineRendition(LineRendition::SingleWidth), _fPaintStarted(false), @@ -142,15 +142,6 @@ GdiEngine::~GdiEngine() _hwndTargetWindow = hwnd; _hdcMemoryContext = hdcNewMemoryContext; - // If we have a font, apply it to the context. - if (nullptr != _hfont) - { - LOG_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont)); - } - - // Record the fact that the selected font is the default. - _lastFontType = FontType::Default; - if (nullptr != hdcRealWindow) { LOG_HR_IF(E_FAIL, !(ReleaseDC(_hwndTargetWindow, hdcRealWindow))); @@ -327,6 +318,7 @@ GdiEngine::~GdiEngine() break; } _lastFontType = fontType; + _fontHasWesternScript = FontHasWesternScript(_hdcMemoryContext); } return S_OK; @@ -348,9 +340,6 @@ GdiEngine::~GdiEngine() // Select into DC RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get())); - // Record the fact that the selected font is the default. - _lastFontType = FontType::Default; - // Save off the font metrics for various other calculations RETURN_HR_IF(E_FAIL, !(GetTextMetricsW(_hdcMemoryContext, &_tmFontMetrics))); @@ -456,7 +445,9 @@ GdiEngine::~GdiEngine() const SIZE cellSize, const size_t centeringHint) noexcept { - // If the soft font is currently selected, replace it with the default font. + // If we previously called SelectFont(_hdcMemoryContext, _softFont), it will + // still hold a reference to the _softFont object we're planning to overwrite. + // --> First revert back to the standard _hfont, lest we have dangling pointers. if (_lastFontType == FontType::Soft) { RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont)); From fd4fa664602d25fc2d3b52d82346e048b7328d66 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Wed, 23 Mar 2022 20:01:52 +0000 Subject: [PATCH 58/75] PR 7101178: Remove the OneCore redirects for MapVkey/VkeyScan/GetKeyState In !1806141, MapVirtualKey and VkKeyScan became part of OneCore (yeah, in 2018!). GetKeyState I can't quite figure out... but it looks like the places we would use it (Win32 Window and Selection) already either don't exist (window) or don't work (selection) in OneCore conhost. Removing these redirects reduces our maintenance burden quite a bit. Because we had to run all keyboard code through the service locator, anything that had a dependency on key translation needed to link the entire console host. Therefore, for anything that conhost depended upon, so did the unit test binaries. Ugh. I chose to keep TranslateCharsetInfo, even though it looks like gdi32 is hosted in OneCore and the code appears as though it would work. It was not in my critical path, and is a very basic local lookup that only powers whether gridlines are available and the console's "Lang ID". TEST RESULTS FROM ONECORE Microsoft.Console.Host.FeatureTests.dll Summary: Total=408, Passed=140, Failed=266, Blocked=0, Not Run=0, Skipped=2 I do not know how to make sure that the feature tests run properly on OneCore. It looks like ModuleSetup is failing, which is unlikely to be related to this change (especially given that conhost *does* launch.) If I re-run the categories individually, they either pass or get marked as skipped intentionally. Microsoft.Console.Host.IntegrityTests.dll Summary: Total=5, Passed=0, Failed=5, Blocked=0, Not Run=0, Skipped=0 Same. Microsoft.Console.Interactivity.Win32.UnitTests.dll Summary: Total=392, Passed=392, Failed=0, Blocked=0, Not Run=0, Skipped=0 Microsoft.Console.TextBuffer.UnitTests.dll Summary: Total=27, Passed=27, Failed=0, Blocked=0, Not Run=0, Skipped=0 Microsoft.Console.Host.UIAutomationTests.dll Summary: Total=6, Passed=0, Failed=0, Blocked=6, Not Run=0, Skipped=0 Microsoft.Console.Types.UnitTests.dll Summary: Total=8, Passed=8, Failed=0, Blocked=0, Not Run=0, Skipped=0 Microsoft.Terminal.Til.UnitTests.dll Summary: Total=290, Passed=290, Failed=0, Blocked=0, Not Run=0, Skipped=0 Microsoft.Console.VirtualTerminal.Parser.UnitTests.dll Summary: Total=748, Passed=747, Failed=0, Blocked=0, Not Run=0, Skipped=1 Microsoft.Console.VirtualTerminal.Adapter.UnitTests.dll Summary: Total=222, Passed=222, Failed=0, Blocked=0, Not Run=0, Skipped=0 Microsoft.Console.Host.UnitTests.dll Summary: Total=5235, Passed=5222, Failed=0, Blocked=12, Not Run=0, Skipped=1 Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev c4c0267e1139d8d205bc9995da7906e5b09f03db Related work items: MSFT-38632962 --- src/host/input.cpp | 16 +-- src/host/selectionInput.cpp | 7 +- src/host/stream.cpp | 2 +- src/host/ut_host/ClipboardTests.cpp | 19 +-- src/interactivity/base/EventSynthesis.cpp | 4 - src/interactivity/base/VtApiRedirection.cpp | 27 ----- .../base/lib/InteractivityBase.vcxproj | 3 +- .../lib/InteractivityBase.vcxproj.filters | 5 +- src/interactivity/base/sources.inc | 1 - src/interactivity/inc/IInputServices.hpp | 3 - src/interactivity/inc/VtApiRedirection.hpp | 29 ----- src/interactivity/onecore/ConIoSrvComm.cpp | 112 ------------------ src/interactivity/onecore/ConIoSrvComm.hpp | 7 -- src/interactivity/win32/InputServices.cpp | 15 --- src/interactivity/win32/InputServices.hpp | 3 - .../ut_adapter/Adapter.UnitTests.vcxproj | 24 ---- src/terminal/adapter/ut_adapter/inputTest.cpp | 4 - src/terminal/adapter/ut_adapter/sources | 87 -------------- src/terminal/input/mouseInput.cpp | 3 - src/terminal/input/terminalInput.cpp | 4 - .../parser/InputStateMachineEngine.cpp | 4 - .../parser/ut_parser/InputEngineTest.cpp | 4 - src/terminal/parser/ut_parser/sources | 98 +-------------- 23 files changed, 23 insertions(+), 458 deletions(-) delete mode 100644 src/interactivity/base/VtApiRedirection.cpp delete mode 100644 src/interactivity/inc/VtApiRedirection.hpp diff --git a/src/host/input.cpp b/src/host/input.cpp index 58c876a328e..9f9582ebd47 100644 --- a/src/host/input.cpp +++ b/src/host/input.cpp @@ -52,35 +52,35 @@ ULONG GetControlKeyState(const LPARAM lParam) { ULONG ControlKeyState = 0; - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_LMENU) & KEY_PRESSED) + if (GetKeyState(VK_LMENU) & KEY_PRESSED) { ControlKeyState |= LEFT_ALT_PRESSED; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_RMENU) & KEY_PRESSED) + if (GetKeyState(VK_RMENU) & KEY_PRESSED) { ControlKeyState |= RIGHT_ALT_PRESSED; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_LCONTROL) & KEY_PRESSED) + if (GetKeyState(VK_LCONTROL) & KEY_PRESSED) { ControlKeyState |= LEFT_CTRL_PRESSED; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_RCONTROL) & KEY_PRESSED) + if (GetKeyState(VK_RCONTROL) & KEY_PRESSED) { ControlKeyState |= RIGHT_CTRL_PRESSED; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_SHIFT) & KEY_PRESSED) + if (GetKeyState(VK_SHIFT) & KEY_PRESSED) { ControlKeyState |= SHIFT_PRESSED; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_NUMLOCK) & KEY_TOGGLED) + if (GetKeyState(VK_NUMLOCK) & KEY_TOGGLED) { ControlKeyState |= NUMLOCK_ON; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_SCROLL) & KEY_TOGGLED) + if (GetKeyState(VK_SCROLL) & KEY_TOGGLED) { ControlKeyState |= SCROLLLOCK_ON; } - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_CAPITAL) & KEY_TOGGLED) + if (GetKeyState(VK_CAPITAL) & KEY_TOGGLED) { ControlKeyState |= CAPSLOCK_ON; } diff --git a/src/host/selectionInput.cpp b/src/host/selectionInput.cpp index cab5aa88b6f..e36db3a5848 100644 --- a/src/host/selectionInput.cpp +++ b/src/host/selectionInput.cpp @@ -22,11 +22,10 @@ using Microsoft::Console::Interactivity::ServiceLocator; Selection::KeySelectionEventResult Selection::HandleKeySelectionEvent(const INPUT_KEY_INFO* const pInputKeyInfo) { const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - const auto inputServices = ServiceLocator::LocateInputServices(); FAIL_FAST_IF(!IsInSelectingState()); const WORD wVirtualKeyCode = pInputKeyInfo->GetVirtualKey(); - const bool ctrlPressed = WI_IsFlagSet(inputServices->GetKeyState(VK_CONTROL), KEY_PRESSED); + const bool ctrlPressed = WI_IsFlagSet(GetKeyState(VK_CONTROL), KEY_PRESSED); // if escape or ctrl-c, cancel selection if (!IsMouseButtonDown()) @@ -612,7 +611,7 @@ bool Selection::HandleKeyboardLineSelectionEvent(const INPUT_KEY_INFO* const pIn // - void Selection::CheckAndSetAlternateSelection() { - _fUseAlternateSelection = !!(ServiceLocator::LocateInputServices()->GetKeyState(VK_MENU) & KEY_PRESSED); + _fUseAlternateSelection = !!(GetKeyState(VK_MENU) & KEY_PRESSED); } // Routine Description: @@ -905,7 +904,7 @@ bool Selection::_HandleMarkModeSelectionNav(const INPUT_KEY_INFO* const pInputKe } // see if shift is down. if so, we're extending the selection. otherwise, we're resetting the anchor - if (ServiceLocator::LocateInputServices()->GetKeyState(VK_SHIFT) & KEY_PRESSED) + if (GetKeyState(VK_SHIFT) & KEY_PRESSED) { // if we're just starting to "extend" our selection from moving around as a cursor // then attempt to set the alternate selection state based on the ALT key right now diff --git a/src/host/stream.cpp b/src/host/stream.cpp index 6f642ddd992..692761e341a 100644 --- a/src/host/stream.cpp +++ b/src/host/stream.cpp @@ -153,7 +153,7 @@ using Microsoft::Console::Interactivity::ServiceLocator; } else { - const short zeroVkeyData = ServiceLocator::LocateInputServices()->VkKeyScanW(0); + const short zeroVkeyData = VkKeyScanW(0); const byte zeroVKey = LOBYTE(zeroVkeyData); const byte zeroControlKeyState = HIBYTE(zeroVkeyData); diff --git a/src/host/ut_host/ClipboardTests.cpp b/src/host/ut_host/ClipboardTests.cpp index fb389757ec4..3cc38e2c2f6 100644 --- a/src/host/ut_host/ClipboardTests.cpp +++ b/src/host/ut_host/ClipboardTests.cpp @@ -15,10 +15,6 @@ #include -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../interactivity/inc/VtApiRedirection.hpp" -#endif - #include "../../inc/consoletaeftemplates.hpp" using namespace WEX::Common; @@ -161,7 +157,6 @@ class ClipboardTests std::deque> events = Clipboard::Instance().TextToKeyEvents(wstr.c_str(), wstr.size()); VERIFY_ARE_EQUAL(wstr.size() * 2, events.size()); - IInputServices* pInputServices = ServiceLocator::LocateInputServices(); for (wchar_t wch : wstr) { std::deque keydownPattern{ true, false }; @@ -172,9 +167,9 @@ class ClipboardTests keyEvent.reset(static_cast(events.front().release())); events.pop_front(); - const short keyState = pInputServices->VkKeyScanW(wch); + const short keyState = VkKeyScanW(wch); VERIFY_ARE_NOT_EQUAL(-1, keyState); - const WORD virtualScanCode = static_cast(pInputServices->MapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); + const WORD virtualScanCode = static_cast(MapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); VERIFY_ARE_EQUAL(wch, keyEvent->GetCharData()); VERIFY_ARE_EQUAL(isKeyDown, keyEvent->IsKeyDown()); @@ -198,8 +193,6 @@ class ClipboardTests wstr.size()); VERIFY_ARE_EQUAL((wstr.size() + uppercaseCount) * 2, events.size()); - IInputServices* pInputServices = ServiceLocator::LocateInputServices(); - VERIFY_IS_NOT_NULL(pInputServices); for (wchar_t wch : wstr) { std::deque keydownPattern{ true, false }; @@ -213,9 +206,9 @@ class ClipboardTests events.pop_front(); const short keyScanError = -1; - const short keyState = pInputServices->VkKeyScanW(wch); + const short keyState = VkKeyScanW(wch); VERIFY_ARE_NOT_EQUAL(keyScanError, keyState); - const WORD virtualScanCode = static_cast(pInputServices->MapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); + const WORD virtualScanCode = static_cast(MapVirtualKeyW(LOBYTE(keyState), MAPVK_VK_TO_VSC)); if (std::isupper(wch)) { @@ -229,9 +222,9 @@ class ClipboardTests keyEvent2.reset(static_cast(events.front().release())); events.pop_front(); - const short keyState2 = pInputServices->VkKeyScanW(wch); + const short keyState2 = VkKeyScanW(wch); VERIFY_ARE_NOT_EQUAL(keyScanError, keyState2); - const WORD virtualScanCode2 = static_cast(pInputServices->MapVirtualKeyW(LOBYTE(keyState2), MAPVK_VK_TO_VSC)); + const WORD virtualScanCode2 = static_cast(MapVirtualKeyW(LOBYTE(keyState2), MAPVK_VK_TO_VSC)); if (isKeyDown) { diff --git a/src/interactivity/base/EventSynthesis.cpp b/src/interactivity/base/EventSynthesis.cpp index e73c0672739..0b26b7ca5b4 100644 --- a/src/interactivity/base/EventSynthesis.cpp +++ b/src/interactivity/base/EventSynthesis.cpp @@ -5,10 +5,6 @@ #include "../inc/EventSynthesis.hpp" #include "../../types/inc/convert.hpp" -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../inc/VtApiRedirection.hpp" -#endif - #pragma hdrstop // TODO: MSFT 14150722 - can these const values be generated at diff --git a/src/interactivity/base/VtApiRedirection.cpp b/src/interactivity/base/VtApiRedirection.cpp deleted file mode 100644 index d7bfef8d496..00000000000 --- a/src/interactivity/base/VtApiRedirection.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../inc/VtApiRedirection.hpp" - -#include "../inc/ServiceLocator.hpp" - -#pragma hdrstop - -using namespace Microsoft::Console::Interactivity; - -UINT VTRedirMapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType) -{ - return ServiceLocator::LocateInputServices()->MapVirtualKeyW(uCode, uMapType); -} - -SHORT VTRedirVkKeyScanW(_In_ WCHAR ch) -{ - return ServiceLocator::LocateInputServices()->VkKeyScanW(ch); -} - -SHORT VTRedirGetKeyState(_In_ int nVirtKey) -{ - return ServiceLocator::LocateInputServices()->GetKeyState(nVirtKey); -} diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj b/src/interactivity/base/lib/InteractivityBase.vcxproj index 33913e1377b..5f3f7c4e8e5 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj @@ -41,7 +41,6 @@ - @@ -52,4 +51,4 @@ - \ No newline at end of file + diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters index 23e8c2130e5..4c7b8495037 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters @@ -77,9 +77,6 @@ Header Files - - Header Files - Header Files @@ -96,4 +93,4 @@ - \ No newline at end of file + diff --git a/src/interactivity/base/sources.inc b/src/interactivity/base/sources.inc index 00bfdaf13e3..69e423131c2 100644 --- a/src/interactivity/base/sources.inc +++ b/src/interactivity/base/sources.inc @@ -39,7 +39,6 @@ SOURCES = \ ..\ApiDetector.cpp \ ..\InteractivityFactory.cpp \ ..\ServiceLocator.cpp \ - ..\VtApiRedirection.cpp \ ..\EventSynthesis.cpp \ ..\RemoteConsoleControl.cpp \ ..\HostSignalInputThread.cpp \ diff --git a/src/interactivity/inc/IInputServices.hpp b/src/interactivity/inc/IInputServices.hpp index 30166108291..390a76a5e40 100644 --- a/src/interactivity/inc/IInputServices.hpp +++ b/src/interactivity/inc/IInputServices.hpp @@ -19,9 +19,6 @@ namespace Microsoft::Console::Interactivity class IInputServices { public: - virtual UINT MapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType) = 0; - virtual SHORT VkKeyScanW(_In_ WCHAR ch) = 0; - virtual SHORT GetKeyState(_In_ int nVirtKey) = 0; virtual BOOL TranslateCharsetInfo(_Inout_ DWORD FAR* lpSrc, _Out_ LPCHARSETINFO lpCs, _In_ DWORD dwFlags) = 0; virtual ~IInputServices() = 0; diff --git a/src/interactivity/inc/VtApiRedirection.hpp b/src/interactivity/inc/VtApiRedirection.hpp deleted file mode 100644 index 14dcc1cc8da..00000000000 --- a/src/interactivity/inc/VtApiRedirection.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- VtApiRedirection.h - -Abstract: -- Redefines several input-related API's that are not available on OneCore such - that they be redirected through the ServiceLocator via the IInputServices - interface. -- This ensures that all calls to these API's are executed as normal when the - console is running on big Windows, but that they are also redirected to the - Console IO Server when it is running on a OneCore system, where the OneCore - implementations live. - -Author: -- HeGatta Apr.25.2017 ---*/ - -#pragma once - -#define MapVirtualKeyW(x, y) VTRedirMapVirtualKeyW(x, y) -#define VkKeyScanW(x) VTRedirVkKeyScanW(x) -#define GetKeyState(x) VTRedirGetKeyState(x) - -UINT VTRedirMapVirtualKeyW(_In_ UINT uCode, _In_ UINT uMapType); -SHORT VTRedirVkKeyScanW(_In_ WCHAR ch); -SHORT VTRedirGetKeyState(_In_ int nVirtKey); diff --git a/src/interactivity/onecore/ConIoSrvComm.cpp b/src/interactivity/onecore/ConIoSrvComm.cpp index cdeebed38c3..e781f6b786a 100644 --- a/src/interactivity/onecore/ConIoSrvComm.cpp +++ b/src/interactivity/onecore/ConIoSrvComm.cpp @@ -532,70 +532,6 @@ VOID ConIoSrvComm::CleanupForHeadless(const NTSTATUS status) return Status; } -[[nodiscard]] NTSTATUS ConIoSrvComm::RequestMapVirtualKey(_In_ UINT uCode, _In_ UINT uMapType, _Out_ UINT* puReturnValue) -{ - NTSTATUS Status; - - Status = EnsureConnection(); - if (NT_SUCCESS(Status)) - { - CIS_MSG Message = { 0 }; - Message.Type = CIS_MSG_TYPE_MAPVIRTUALKEY; - Message.MapVirtualKeyParams.Code = uCode; - Message.MapVirtualKeyParams.MapType = uMapType; - - Status = SendRequestReceiveReply(&Message); - if (NT_SUCCESS(Status)) - { - *puReturnValue = Message.MapVirtualKeyParams.ReturnValue; - } - } - - return Status; -} - -[[nodiscard]] NTSTATUS ConIoSrvComm::RequestVkKeyScan(_In_ WCHAR wCharacter, _Out_ SHORT* psReturnValue) -{ - NTSTATUS Status; - - Status = EnsureConnection(); - if (NT_SUCCESS(Status)) - { - CIS_MSG Message = { 0 }; - Message.Type = CIS_MSG_TYPE_VKKEYSCAN; - Message.VkKeyScanParams.Character = wCharacter; - - Status = SendRequestReceiveReply(&Message); - if (NT_SUCCESS(Status)) - { - *psReturnValue = Message.VkKeyScanParams.ReturnValue; - } - } - - return Status; -} - -[[nodiscard]] NTSTATUS ConIoSrvComm::RequestGetKeyState(_In_ int iVirtualKey, _Out_ SHORT* psReturnValue) -{ - NTSTATUS Status; - - Status = EnsureConnection(); - if (NT_SUCCESS(Status)) - { - CIS_MSG Message = { 0 }; - Message.Type = CIS_MSG_TYPE_GETKEYSTATE; - Message.GetKeyStateParams.VirtualKey = iVirtualKey; - - Status = SendRequestReceiveReply(&Message); - if (NT_SUCCESS(Status)) - { - *psReturnValue = Message.GetKeyStateParams.ReturnValue; - } - } - - return Status; -} - [[nodiscard]] USHORT ConIoSrvComm::GetDisplayMode() const { return _displayMode; @@ -610,54 +546,6 @@ PVOID ConIoSrvComm::GetSharedViewBase() const #pragma region IInputServices Members -UINT ConIoSrvComm::MapVirtualKeyW(UINT uCode, UINT uMapType) -{ - NTSTATUS Status = STATUS_SUCCESS; - - UINT ReturnValue; - Status = RequestMapVirtualKey(uCode, uMapType, &ReturnValue); - - if (!NT_SUCCESS(Status)) - { - ReturnValue = 0; - SetLastError(ERROR_PROC_NOT_FOUND); - } - - return ReturnValue; -} - -SHORT ConIoSrvComm::VkKeyScanW(WCHAR ch) -{ - NTSTATUS Status = STATUS_SUCCESS; - - SHORT ReturnValue; - Status = RequestVkKeyScan(ch, &ReturnValue); - - if (!NT_SUCCESS(Status)) - { - ReturnValue = 0; - SetLastError(ERROR_PROC_NOT_FOUND); - } - - return ReturnValue; -} - -SHORT ConIoSrvComm::GetKeyState(int nVirtKey) -{ - NTSTATUS Status = STATUS_SUCCESS; - - SHORT ReturnValue; - Status = RequestGetKeyState(nVirtKey, &ReturnValue); - - if (!NT_SUCCESS(Status)) - { - ReturnValue = 0; - SetLastError(ERROR_PROC_NOT_FOUND); - } - - return ReturnValue; -} - BOOL ConIoSrvComm::TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags) { SetLastError(ERROR_SUCCESS); diff --git a/src/interactivity/onecore/ConIoSrvComm.hpp b/src/interactivity/onecore/ConIoSrvComm.hpp index 1a0a827e55d..4a52ed53617 100644 --- a/src/interactivity/onecore/ConIoSrvComm.hpp +++ b/src/interactivity/onecore/ConIoSrvComm.hpp @@ -40,10 +40,6 @@ namespace Microsoft::Console::Interactivity::OneCore [[nodiscard]] NTSTATUS RequestSetCursor(_In_ CD_IO_CURSOR_INFORMATION* const pCdCursorInformation) const; [[nodiscard]] NTSTATUS RequestUpdateDisplay(_In_ SHORT RowIndex) const; - [[nodiscard]] NTSTATUS RequestMapVirtualKey(_In_ UINT uCode, _In_ UINT uMapType, _Out_ UINT* puReturnValue); - [[nodiscard]] NTSTATUS RequestVkKeyScan(_In_ WCHAR wCharacter, _Out_ SHORT* psReturnValue); - [[nodiscard]] NTSTATUS RequestGetKeyState(_In_ int iVirtualKey, _Out_ SHORT* psReturnValue); - [[nodiscard]] USHORT GetDisplayMode() const; PVOID GetSharedViewBase() const; @@ -51,9 +47,6 @@ namespace Microsoft::Console::Interactivity::OneCore VOID CleanupForHeadless(const NTSTATUS status); // IInputServices Members - UINT MapVirtualKeyW(UINT uCode, UINT uMapType); - SHORT VkKeyScanW(WCHAR ch); - SHORT GetKeyState(int nVirtKey); BOOL TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags); [[nodiscard]] NTSTATUS InitializeBgfx(); diff --git a/src/interactivity/win32/InputServices.cpp b/src/interactivity/win32/InputServices.cpp index 3d798e25899..44014fd9284 100644 --- a/src/interactivity/win32/InputServices.cpp +++ b/src/interactivity/win32/InputServices.cpp @@ -9,21 +9,6 @@ using namespace Microsoft::Console::Interactivity::Win32; -UINT InputServices::MapVirtualKeyW(UINT uCode, UINT uMapType) -{ - return ::MapVirtualKeyW(uCode, uMapType); -} - -SHORT InputServices::VkKeyScanW(WCHAR ch) -{ - return ::VkKeyScanW(ch); -} - -SHORT InputServices::GetKeyState(int nVirtKey) -{ - return ::GetKeyState(nVirtKey); -} - BOOL InputServices::TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags) { return ::TranslateCharsetInfo(lpSrc, lpCs, dwFlags); diff --git a/src/interactivity/win32/InputServices.hpp b/src/interactivity/win32/InputServices.hpp index 1df8da3c8ef..00d74b1cabb 100644 --- a/src/interactivity/win32/InputServices.hpp +++ b/src/interactivity/win32/InputServices.hpp @@ -21,9 +21,6 @@ namespace Microsoft::Console::Interactivity::Win32 public: // Inherited via IInputServices ~InputServices() = default; - UINT MapVirtualKeyW(UINT uCode, UINT uMapType); - SHORT VkKeyScanW(WCHAR ch); - SHORT GetKeyState(int nVirtKey); BOOL TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags); }; } diff --git a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj index b4e5ba6a2c1..b1ce6a9d73e 100644 --- a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj +++ b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj @@ -24,30 +24,9 @@ {0cf235bd-2da0-407e-90ee-c467e8bbc714} - - {06ec74cb-9a12-429c-b551-8562ec954746} - {06ec74cb-9a12-429c-b551-8562ec964846} - - {06ec74cb-9a12-429c-b551-8532ec964726} - - - {345fd5a4-b32b-4f29-bd1c-b033bd2c35cc} - - - {af0a096a-8b3a-4949-81ef-7df8f0fee91f} - - - {1c959542-bac2-4e55-9a6d-13251914cbb9} - - - {18d09a24-8240-42d6-8cb6-236eee820262} - - - {2fd12fbb-1ddb-46d8-b818-1023c624caca} - {18d09a24-8240-42d6-8cb6-236eee820263} @@ -57,9 +36,6 @@ {dcf55140-ef6a-4736-a403-957e4f7430bb} - - {0cf235bd-2da0-407e-90ee-c467e8bbc714} - diff --git a/src/terminal/adapter/ut_adapter/inputTest.cpp b/src/terminal/adapter/ut_adapter/inputTest.cpp index fe19266a474..42f75ae726b 100644 --- a/src/terminal/adapter/ut_adapter/inputTest.cpp +++ b/src/terminal/adapter/ut_adapter/inputTest.cpp @@ -9,10 +9,6 @@ #include "../../input/terminalInput.hpp" -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../../interactivity/inc/VtApiRedirection.hpp" -#endif - using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; diff --git a/src/terminal/adapter/ut_adapter/sources b/src/terminal/adapter/ut_adapter/sources index 73a19ce6f86..7bf618415a3 100644 --- a/src/terminal/adapter/ut_adapter/sources +++ b/src/terminal/adapter/ut_adapter/sources @@ -41,101 +41,14 @@ TARGETLIBS = \ $(ONECORE_INTERNAL_SDK_LIB_PATH)\onecoreuuid.lib \ $(ONECOREUAP_INTERNAL_SDK_LIB_PATH)\onecoreuapuuid.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecore_internal.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\propsys.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d2d1.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dwrite.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dxgi.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-font-l1.lib \ - $(ONECOREWINDOWS_INTERNAL_LIB_PATH_L)\ext-ms-win-gdi-internal-desktop-l1-1-0.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-caret-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-dialogbox-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-draw-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-gui-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-menu-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-misc-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-mouse-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-rectangle-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-server-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-window-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-gdi-object-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-gdi-rgn-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-cursor-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-dc-access-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-rawinput-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ - $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ - $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ - $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\tsf\$(O)\contsf.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\propslib\$(O)\conprops.lib \ $(WINCORE_OBJ_PATH)\console\open\src\terminal\adapter\lib\$(O)\ConTermAdapter.lib \ $(WINCORE_OBJ_PATH)\console\open\src\terminal\input\lib\$(O)\ConTermInput.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\terminal\parser\lib\$(O)\ConTermParser.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\base\lib\$(O)\ConRenderBase.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\renderer\vt\lib\$(O)\ConRenderVt.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\server\lib\$(O)\ConServer.lib \ $(WINCORE_OBJ_PATH)\console\open\src\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\interactivity\win32\lib\$(O)\ConInteractivityWin32Lib.lib \ - $(WINCORE_OBJ_PATH)\console\open\src\interactivity\onecore\lib\$(O)\ConInteractivityOneCoreLib.lib \ $(WINCORE_OBJ_PATH)\console\open\src\types\lib\$(O)\ConTypes.lib \ DELAYLOAD = \ - PROPSYS.dll; \ - D2D1.dll; \ - DWrite.dll; \ - DXGI.dll; \ - D3D11.dll; \ - OLEAUT32.dll; \ - api-ms-win-mm-playsound-l1.dll; \ - api-ms-win-shcore-scaling-l1.dll; \ - api-ms-win-shell-dataobject-l1.dll; \ - api-ms-win-shell-namespace-l1.dll; \ - ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ - ext-ms-win-gdi-dc-l1.dll; \ - ext-ms-win-gdi-dc-create-l1.dll; \ - ext-ms-win-gdi-draw-l1.dll; \ - ext-ms-win-gdi-font-l1.dll; \ - ext-ms-win-gdi-internal-desktop-l1.dll; \ - ext-ms-win-ntuser-caret-l1.dll; \ - ext-ms-win-ntuser-dialogbox-l1.dll; \ - ext-ms-win-ntuser-draw-l1.dll; \ ext-ms-win-ntuser-keyboard-l1.dll; \ - ext-ms-win-ntuser-gui-l1.dll; \ - ext-ms-win-ntuser-menu-l1.dll; \ - ext-ms-win-ntuser-misc-l1.dll; \ - ext-ms-win-ntuser-mouse-l1.dll; \ - ext-ms-win-ntuser-rectangle-ext-l1.dll; \ - ext-ms-win-ntuser-server-l1.dll; \ - ext-ms-win-ntuser-window-l1.dll; \ - ext-ms-win-rtcore-gdi-object-l1.dll; \ - ext-ms-win-rtcore-gdi-rgn-l1.dll; \ - ext-ms-win-rtcore-ntuser-cursor-l1.dll; \ - ext-ms-win-rtcore-ntuser-dc-access-l1.dll; \ - ext-ms-win-rtcore-ntuser-rawinput-l1.dll; \ - ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ - ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ - ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ - ext-ms-win-shell-shell32-l1.dll; \ - ext-ms-win-uiacore-l1.dll; \ - ext-ms-win-uxtheme-themes-l1.dll; \ DLOAD_ERROR_HANDLER = kernelbase diff --git a/src/terminal/input/mouseInput.cpp b/src/terminal/input/mouseInput.cpp index e20578e6606..82d8c358303 100644 --- a/src/terminal/input/mouseInput.cpp +++ b/src/terminal/input/mouseInput.cpp @@ -8,9 +8,6 @@ using namespace Microsoft::Console::VirtualTerminal; -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../interactivity/inc/VtApiRedirection.hpp" -#endif static constexpr int s_MaxDefaultCoordinate = 94; // Alternate scroll sequences diff --git a/src/terminal/input/terminalInput.cpp b/src/terminal/input/terminalInput.cpp index 19bd1b3d8cd..1408ad56cbd 100644 --- a/src/terminal/input/terminalInput.cpp +++ b/src/terminal/input/terminalInput.cpp @@ -10,10 +10,6 @@ #define WIL_SUPPORT_BITOPERATION_PASCAL_NAMES #include -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../interactivity/inc/VtApiRedirection.hpp" -#endif - #include "../../inc/unicode.hpp" #include "../../types/inc/Utf16Parser.hpp" diff --git a/src/terminal/parser/InputStateMachineEngine.cpp b/src/terminal/parser/InputStateMachineEngine.cpp index ca469663849..e609b0cec56 100644 --- a/src/terminal/parser/InputStateMachineEngine.cpp +++ b/src/terminal/parser/InputStateMachineEngine.cpp @@ -9,10 +9,6 @@ #include "../../inc/unicode.hpp" #include "ascii.hpp" -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../interactivity/inc/VtApiRedirection.hpp" -#endif - using namespace Microsoft::Console::VirtualTerminal; struct CsiToVkey diff --git a/src/terminal/parser/ut_parser/InputEngineTest.cpp b/src/terminal/parser/ut_parser/InputEngineTest.cpp index cb1ef806251..0cd5c025e7f 100644 --- a/src/terminal/parser/ut_parser/InputEngineTest.cpp +++ b/src/terminal/parser/ut_parser/InputEngineTest.cpp @@ -18,10 +18,6 @@ #include #include -#ifdef BUILD_ONECORE_INTERACTIVITY -#include "../../../interactivity/inc/VtApiRedirection.hpp" -#endif - using namespace WEX::Common; using namespace WEX::Logging; using namespace WEX::TestExecution; diff --git a/src/terminal/parser/ut_parser/sources b/src/terminal/parser/ut_parser/sources index 57b201b9985..54846ecf1dc 100644 --- a/src/terminal/parser/ut_parser/sources +++ b/src/terminal/parser/ut_parser/sources @@ -26,113 +26,21 @@ SOURCES = \ StateMachineTest.cpp \ Base64Test.cpp \ -# The InputEngineTest requires VTRedirMapVirtualKeyW, which means we need the -# ServiceLocator, which means we need the entire host and all it's dependencies, -# as well as the -DBUILD_ONECORE_INTERACTIVITY above - TARGETLIBS = \ $(TARGETLIBS) \ $(ONECORE_INTERNAL_SDK_LIB_PATH)\onecoreuuid.lib \ $(ONECOREUAP_INTERNAL_SDK_LIB_PATH)\onecoreuapuuid.lib \ $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\onecore_internal.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\propsys.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d2d1.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dwrite.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\dxgi.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3d11.lib \ - $(ONECOREUAP_EXTERNAL_SDK_LIB_PATH)\d3dcompiler.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\api-ms-win-mm-playsound-l1.lib \ - $(ONECORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-dwmapi-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-edputil-policy-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-dc-create-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-draw-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-gdi-font-l1.lib \ - $(ONECOREWINDOWS_INTERNAL_LIB_PATH_L)\ext-ms-win-gdi-internal-desktop-l1-1-0.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-caret-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-dialogbox-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-draw-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-gui-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-menu-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-misc-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-mouse-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-rectangle-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-server-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-ntuser-window-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-gdi-object-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-gdi-rgn-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-cursor-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-dc-access-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-rawinput-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-sysparams-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-window-ext-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-winstamin-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-shell-shell32-l1.lib \ - $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uxtheme-themes-l1.lib \ - $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ - $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ - $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ - $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ - $(CONSOLE_OBJ_PATH)\buffer\out\lib\$(O)\conbufferout.lib \ - $(CONSOLE_OBJ_PATH)\host\lib\$(O)\conhostv2.lib \ - $(CONSOLE_OBJ_PATH)\tsf\$(O)\contsf.lib \ - $(CONSOLE_OBJ_PATH)\propslib\$(O)\conprops.lib \ - $(CONSOLE_OBJ_PATH)\terminal\input\lib\$(O)\ConTermInput.lib \ - $(CONSOLE_OBJ_PATH)\terminal\adapter\lib\$(O)\ConTermAdapter.lib \ + $(MINCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-rtcore-ntuser-mouse-l1.lib \ $(CONSOLE_OBJ_PATH)\terminal\parser\lib\$(O)\ConTermParser.lib \ - $(CONSOLE_OBJ_PATH)\renderer\base\lib\$(O)\ConRenderBase.lib \ - $(CONSOLE_OBJ_PATH)\renderer\gdi\lib\$(O)\ConRenderGdi.lib \ - $(CONSOLE_OBJ_PATH)\renderer\wddmcon\lib\$(O)\ConRenderWddmCon.lib \ - $(CONSOLE_OBJ_PATH)\renderer\vt\lib\$(O)\ConRenderVt.lib \ - $(CONSOLE_OBJ_PATH)\server\lib\$(O)\ConServer.lib \ + $(CONSOLE_OBJ_PATH)\terminal\input\lib\$(O)\ConTermInput.lib \ $(CONSOLE_OBJ_PATH)\interactivity\base\lib\$(O)\ConInteractivityBaseLib.lib \ - $(CONSOLE_OBJ_PATH)\interactivity\win32\lib\$(O)\ConInteractivityWin32Lib.lib \ - $(CONSOLE_OBJ_PATH)\interactivity\onecore\lib\$(O)\ConInteractivityOneCoreLib.lib \ $(CONSOLE_OBJ_PATH)\types\lib\$(O)\ConTypes.lib \ DELAYLOAD = \ - PROPSYS.dll; \ - D2D1.dll; \ - DWrite.dll; \ - DXGI.dll; \ - D3D11.dll; \ - OLEAUT32.dll; \ - api-ms-win-mm-playsound-l1.dll; \ - api-ms-win-shcore-scaling-l1.dll; \ - api-ms-win-shell-dataobject-l1.dll; \ - api-ms-win-shell-namespace-l1.dll; \ - ext-ms-win-dwmapi-ext-l1.dll; \ - ext-ms-win-edputil-policy-l1.dll; \ - ext-ms-win-gdi-dc-l1.dll; \ - ext-ms-win-gdi-dc-create-l1.dll; \ - ext-ms-win-gdi-draw-l1.dll; \ - ext-ms-win-gdi-font-l1.dll; \ - ext-ms-win-gdi-internal-desktop-l1.dll; \ - ext-ms-win-ntuser-caret-l1.dll; \ - ext-ms-win-ntuser-dialogbox-l1.dll; \ - ext-ms-win-ntuser-draw-l1.dll; \ ext-ms-win-ntuser-keyboard-l1.dll; \ - ext-ms-win-ntuser-gui-l1.dll; \ - ext-ms-win-ntuser-menu-l1.dll; \ - ext-ms-win-ntuser-message-l1.dll; \ - ext-ms-win-ntuser-misc-l1.dll; \ - ext-ms-win-ntuser-mouse-l1.dll; \ - ext-ms-win-ntuser-rectangle-ext-l1.dll; \ - ext-ms-win-ntuser-server-l1.dll; \ - ext-ms-win-ntuser-sysparams-ext-l1.dll; \ - ext-ms-win-ntuser-window-l1.dll; \ - ext-ms-win-rtcore-gdi-object-l1.dll; \ - ext-ms-win-rtcore-gdi-rgn-l1.dll; \ - ext-ms-win-rtcore-ntuser-cursor-l1.dll; \ - ext-ms-win-rtcore-ntuser-dc-access-l1.dll; \ - ext-ms-win-rtcore-ntuser-rawinput-l1.dll; \ - ext-ms-win-rtcore-ntuser-sysparams-l1.dll; \ - ext-ms-win-rtcore-ntuser-window-ext-l1.dll; \ - ext-ms-win-rtcore-ntuser-winstamin-l1.dll; \ - ext-ms-win-shell-shell32-l1.dll; \ - ext-ms-win-uiacore-l1.dll; \ - ext-ms-win-uxtheme-themes-l1.dll; \ + ext-ms-win-rtcore-ntuser-mouse-l1.dll; \ DLOAD_ERROR_HANDLER = kernelbase From c857f83aea13e1726c40b72f36bd71050007c1a0 Mon Sep 17 00:00:00 2001 From: abdoulkkonate <73804112+abdoulkkonate@users.noreply.github.com> Date: Mon, 31 Jan 2022 11:57:48 -0800 Subject: [PATCH 59/75] Replaced the sizeof parameter of the if statement with ARRAYSIZE (#12273) The pull request fixes the issue where "sizeof" parameter was use instead of "ARRAYSIZE". ## PR Checklist * [x] Closes #xxx * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx * [x] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx ## Detailed Description of the Pull Request / Additional comments This was a pretty straight forward issue, i just replace sizeof which gives the byte size with ARRAYSIZE which give the number of elements in the array. (cherry picked from commit 5fa1ba8dab00259b1a4f5824460752d3f4346b3b) --- src/interactivity/win32/SystemConfigurationProvider.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interactivity/win32/SystemConfigurationProvider.cpp b/src/interactivity/win32/SystemConfigurationProvider.cpp index af37c4c5cd9..0e3dbb790ab 100644 --- a/src/interactivity/win32/SystemConfigurationProvider.cpp +++ b/src/interactivity/win32/SystemConfigurationProvider.cpp @@ -174,9 +174,9 @@ void SystemConfigurationProvider::GetSettingsFromLink( const DWORD dwLinkLen = SearchPathW(pwszCurrDir, pwszAppName, nullptr, ARRAYSIZE(wszIconLocation), wszIconLocation, nullptr); // If we cannot find the application in the path, then try to fall back and see if the window title is a valid path and use that. - if (dwLinkLen <= 0 || dwLinkLen > sizeof(wszIconLocation)) + if (dwLinkLen <= 0 || dwLinkLen > ARRAYSIZE(wszIconLocation)) { - if (PathFileExistsW(pwszTitle) && (wcslen(pwszTitle) < sizeof(wszIconLocation))) + if (PathFileExistsW(pwszTitle) && (wcslen(pwszTitle) < ARRAYSIZE(wszIconLocation))) { StringCchCopyW(wszIconLocation, ARRAYSIZE(wszIconLocation), pwszTitle); } From 4abd57e16972b94f8b6784b12168a91cc6b39573 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Wed, 9 Feb 2022 00:08:00 +0100 Subject: [PATCH 60/75] XtermEngine: Explicitly emit cursor state on the first frame (#12434) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit fixes an issue, where we failed to emit a DECTCEM sequence to hide the cursor if it was hidden before XtermEngine's first frame was finalized. Even in such cases we need to emit a DECTCEM sequence in order to ensure we're in a consistent state. ## Validation Steps Performed * Added test✅ * Run #12401's repro steps around 30 times✅ Closes #12401 (cherry picked from commit dbb70778d470e163c16b30dda0f294a9cbda07a0) --- .github/actions/spelling/expect/expect.txt | 1 + src/host/ut_host/VtRendererTests.cpp | 125 +++++++-------------- src/renderer/vt/XtermEngine.cpp | 25 ++--- src/renderer/vt/XtermEngine.hpp | 11 +- 4 files changed, 58 insertions(+), 104 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 84fcd8e14b5..e3277a26eed 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -2437,6 +2437,7 @@ TREX triaged triaging TRIANGLESTRIP +Tribool TRIMZEROHEADINGS truetype trx diff --git a/src/host/ut_host/VtRendererTests.cpp b/src/host/ut_host/VtRendererTests.cpp index 9678380e674..c9ef9e8371a 100644 --- a/src/host/ut_host/VtRendererTests.cpp +++ b/src/host/ut_host/VtRendererTests.cpp @@ -90,7 +90,29 @@ class Microsoft::Console::Render::VtRendererTest void TestPaint(VtEngine& engine, std::function pfn); Viewport SetUpViewport(); - void VerifyExpectedInputsDrained(); + void VerifyFirstPaint(VtEngine& engine) + { + // Verify the first BeginPaint emits a clear and go home + qExpectedInput.push_back("\x1b[2J"); + // Verify the first EndPaint sets the cursor state + qExpectedInput.push_back("\x1b[?25l"); + VERIFY_IS_TRUE(engine._firstPaint); + TestPaint(engine, [&]() { + VERIFY_IS_FALSE(engine._firstPaint); + }); + } + + void VerifyExpectedInputsDrained() + { + if (!qExpectedInput.empty()) + { + for (const auto& exp : qExpectedInput) + { + Log::Error(NoThrowString().Format(L"EXPECTED INPUT NEVER RECEIVED: %hs", exp.c_str())); + } + VERIFY_FAIL(L"there should be no remaining un-drained expected input"); + } + } }; Viewport VtRendererTest::SetUpViewport() @@ -103,18 +125,6 @@ Viewport VtRendererTest::SetUpViewport() return Viewport::FromInclusive(view); } -void VtRendererTest::VerifyExpectedInputsDrained() -{ - if (!qExpectedInput.empty()) - { - for (const auto& exp : qExpectedInput) - { - Log::Error(NoThrowString().Format(L"EXPECTED INPUT NEVER RECEIVED: %hs", exp.c_str())); - } - VERIFY_FAIL(L"there should be no remaining un-drained expected input"); - } -} - bool VtRendererTest::WriteCallback(const char* const pch, size_t const cch) { std::string actualString = std::string(pch, cch); @@ -212,12 +222,7 @@ void VtRendererTest::Xterm256TestInvalidate() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); const Viewport view = SetUpViewport(); @@ -402,12 +407,7 @@ void VtRendererTest::Xterm256TestColors() RenderSettings renderSettings; RenderData renderData; - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -585,12 +585,7 @@ void VtRendererTest::Xterm256TestCursor() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -766,12 +761,7 @@ void VtRendererTest::Xterm256TestExtendedAttributes() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -907,12 +897,7 @@ void VtRendererTest::XtermTestInvalidate() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -1096,12 +1081,7 @@ void VtRendererTest::XtermTestColors() RenderSettings renderSettings; RenderData renderData; - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -1234,12 +1214,7 @@ void VtRendererTest::XtermTestCursor() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -1412,12 +1387,7 @@ void VtRendererTest::TestWrapping() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_firstPaint); - }); + VerifyFirstPaint(*engine); Viewport view = SetUpViewport(); @@ -1465,8 +1435,10 @@ void VtRendererTest::TestResize() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear and go home + // Verify the first BeginPaint emits a clear and go home qExpectedInput.push_back("\x1b[2J"); + // Verify the first EndPaint sets the cursor state + qExpectedInput.push_back("\x1b[?25l"); VERIFY_IS_TRUE(engine->_firstPaint); VERIFY_IS_TRUE(engine->_suppressResizeRepaint); @@ -1502,21 +1474,6 @@ void VtRendererTest::TestCursorVisibility() auto pfn = std::bind(&VtRendererTest::WriteCallback, this, std::placeholders::_1, std::placeholders::_2); engine->SetTestCallback(pfn); - // Verify the first paint emits a clear - qExpectedInput.push_back("\x1b[2J"); - VERIFY_IS_TRUE(engine->_firstPaint); - VERIFY_IS_FALSE(engine->_lastCursorIsVisible); - VERIFY_IS_TRUE(engine->_nextCursorIsVisible); - TestPaint(*engine, [&]() { - // During StartPaint, we'll mark the cursor as off. make sure that happens. - VERIFY_IS_FALSE(engine->_nextCursorIsVisible); - VERIFY_IS_FALSE(engine->_firstPaint); - }); - - // The cursor wasn't painted in the last frame. - VERIFY_IS_FALSE(engine->_lastCursorIsVisible); - VERIFY_IS_FALSE(engine->_nextCursorIsVisible); - COORD origin{ 0, 0 }; VERIFY_ARE_NOT_EQUAL(origin, engine->_lastText); @@ -1527,8 +1484,8 @@ void VtRendererTest::TestCursorVisibility() // Frame 1: Paint the cursor at the home position. At the end of the frame, // the cursor should be on. Because we're moving the cursor with CUP, we // need to disable the cursor during this frame. + qExpectedInput.push_back("\x1b[2J"); TestPaint(*engine, [&]() { - VERIFY_IS_FALSE(engine->_lastCursorIsVisible); VERIFY_IS_FALSE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1539,10 +1496,13 @@ void VtRendererTest::TestCursorVisibility() VERIFY_IS_TRUE(engine->_nextCursorIsVisible); VERIFY_IS_TRUE(engine->_needToDisableCursor); + // GH#12401: + // The other tests verify that the cursor is explicitly hidden on the + // first frame (VerifyFirstPaint). This test on the other hand does + // the opposite by calling PaintCursor() during the first paint cycle. qExpectedInput.push_back("\x1b[?25h"); }); - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_TRUE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1550,7 +1510,6 @@ void VtRendererTest::TestCursorVisibility() // frame, the cursor should be on, the same as before. We aren't moving the // cursor during this frame, so _needToDisableCursor will stay false. TestPaint(*engine, [&]() { - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_FALSE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1561,7 +1520,6 @@ void VtRendererTest::TestCursorVisibility() VERIFY_IS_FALSE(engine->_needToDisableCursor); }); - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_TRUE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1569,7 +1527,6 @@ void VtRendererTest::TestCursorVisibility() // should be on, the same as before. Because we're moving the cursor with // CUP, we need to disable the cursor during this frame. TestPaint(*engine, [&]() { - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_FALSE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1580,7 +1537,6 @@ void VtRendererTest::TestCursorVisibility() VERIFY_SUCCEEDED(engine->PaintCursor(options)); - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_TRUE(engine->_nextCursorIsVisible); VERIFY_IS_TRUE(engine->_needToDisableCursor); @@ -1590,7 +1546,6 @@ void VtRendererTest::TestCursorVisibility() qExpectedInput.push_back("\x1b[?25h"); }); - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_TRUE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); @@ -1598,14 +1553,12 @@ void VtRendererTest::TestCursorVisibility() // should be off. Log::Comment(NoThrowString().Format(L"Painting without calling PaintCursor will hide the cursor")); TestPaint(*engine, [&]() { - VERIFY_IS_TRUE(engine->_lastCursorIsVisible); VERIFY_IS_FALSE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); qExpectedInput.push_back("\x1b[?25l"); }); - VERIFY_IS_FALSE(engine->_lastCursorIsVisible); VERIFY_IS_FALSE(engine->_nextCursorIsVisible); VERIFY_IS_FALSE(engine->_needToDisableCursor); } diff --git a/src/renderer/vt/XtermEngine.cpp b/src/renderer/vt/XtermEngine.cpp index 4b3807df270..cb8004cfd0f 100644 --- a/src/renderer/vt/XtermEngine.cpp +++ b/src/renderer/vt/XtermEngine.cpp @@ -15,7 +15,9 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, VtEngine(std::move(hPipe), initialViewport), _fUseAsciiOnly(fUseAsciiOnly), _needToDisableCursor(false), - _lastCursorIsVisible(false), + // GH#12401: Ensure a DECTCEM cursor show/hide sequence + // is emitted on the first frame no matter what. + _lastCursorIsVisible(Tribool::Invalid), _nextCursorIsVisible(true) { // Set out initial cursor position to -1, -1. This will force our initial @@ -98,31 +100,20 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe, { // If the cursor was previously visible, let's hide it for this frame, // by prepending a cursor off. - if (_lastCursorIsVisible) + if (_lastCursorIsVisible != Tribool::False) { _buffer.insert(0, "\x1b[?25l"); - _lastCursorIsVisible = false; + _lastCursorIsVisible = Tribool::False; } // If the cursor was NOT previously visible, then that's fine! we don't // need to worry, it's already off. } - // If the cursor is moving from off -> on (including cases where we just - // disabled if for this frame), show the cursor at the end of the frame - if (_nextCursorIsVisible && !_lastCursorIsVisible) + if (_lastCursorIsVisible != static_cast(_nextCursorIsVisible)) { - RETURN_IF_FAILED(_ShowCursor()); + RETURN_IF_FAILED(_nextCursorIsVisible ? _ShowCursor() : _HideCursor()); + _lastCursorIsVisible = static_cast(_nextCursorIsVisible); } - // Otherwise, if the cursor previously was visible, and it should be hidden - // (on -> off), hide it at the end of the frame. - else if (!_nextCursorIsVisible && _lastCursorIsVisible) - { - RETURN_IF_FAILED(_HideCursor()); - } - - // Update our tracker of what we thought the last cursor state of the - // terminal was. - _lastCursorIsVisible = _nextCursorIsVisible; RETURN_IF_FAILED(VtEngine::EndPaint()); diff --git a/src/renderer/vt/XtermEngine.hpp b/src/renderer/vt/XtermEngine.hpp index 1cda1782710..2e2b58affea 100644 --- a/src/renderer/vt/XtermEngine.hpp +++ b/src/renderer/vt/XtermEngine.hpp @@ -54,9 +54,18 @@ namespace Microsoft::Console::Render [[nodiscard]] HRESULT WriteTerminalW(const std::wstring_view str) noexcept override; protected: + // I'm using a non-class enum here, so that the values + // are trivially convertible and comparable to bool. + enum class Tribool : uint8_t + { + False = 0, + True, + Invalid, + }; + const bool _fUseAsciiOnly; bool _needToDisableCursor; - bool _lastCursorIsVisible; + Tribool _lastCursorIsVisible; bool _nextCursorIsVisible; [[nodiscard]] HRESULT _MoveCursor(const COORD coord) noexcept override; From 07c2a597d231e69a022c0afa4970caa1f9eb4d6a Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Tue, 8 Feb 2022 15:55:27 -0800 Subject: [PATCH 61/75] Validate cursor position in UIA UTR ctor (#12436) This adds some validation in the `UiaTextRange` ctor for the cursor position. #8730 was caused by creating a `UiaTextRange` at the cursor position when it was in a delayed state (meaning it's purposefully hanging off of the right edge of the buffer). Normally, `Cursor` maintains a flag to keep track of when that occurs, but Windows Terminal isn't maintaining that properly in `Terminal::WriteBuffer`. The _correct_ approach would be to fix `WriteBuffer` then leverage that flag for validation in `UiaTextRange`. However, messing with `WriteBuffer` is a little too risky for our comfort right now. So we'll do the second half of that by checking if the cursor position is valid. Since the cursor is really only expected to be out of bounds when it's in that delayed state, we get the same result (just maybe a tad slower than simply checking a flag). Closes #8730 Filed #12440 to track changes in `Terminal::_WriteBuffer` for delayed EOL wrap. ## Validation Steps Performed While using magnifier, input/delete wrapped text in input buffer. (cherry picked from commit 5dcf5262b46a1dbdf11bc536fe88439df437e211) --- src/types/UiaTextRangeBase.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/types/UiaTextRangeBase.cpp b/src/types/UiaTextRangeBase.cpp index 58a6f4f982f..4587b08416b 100644 --- a/src/types/UiaTextRangeBase.cpp +++ b/src/types/UiaTextRangeBase.cpp @@ -42,9 +42,15 @@ HRESULT UiaTextRangeBase::RuntimeClassInitialize(_In_ IUiaData* pData, _In_ std::wstring_view wordDelimiters) noexcept try { + RETURN_HR_IF_NULL(E_INVALIDARG, pData); RETURN_IF_FAILED(RuntimeClassInitialize(pData, pProvider, wordDelimiters)); + // GH#8730: The cursor position may be in a delayed state, resulting in it being out of bounds. + // If that's the case, clamp it to be within bounds. + // TODO GH#12440: We should be able to just check some fields off of the Cursor object, + // but Windows Terminal isn't updating those flags properly. _start = cursor.GetPosition(); + pData->GetTextBuffer().GetSize().Clamp(_start); _end = _start; UiaTracing::TextRange::Constructor(*this); From 6cd3e6a24d93ba5f628b9134916c1bfd9b2252cd Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Tue, 15 Feb 2022 08:30:46 -0600 Subject: [PATCH 62/75] Fix a memory leak in onecore interactivity (#12340) As noted in #6759: > `RtlCreateUnicodeString` creates a copy of the string on the process heap and the `PortName` variable has local-scope. The string doesn't get freed with `RtlFreeUnicodeString` before the function returns creating a memory leak. > `CIS_ALPC_PORT_NAME` is a constant string and the `PortName` variable should instead be initialized using the `RTL_CONSTANT_STRING` macro: > > ```c++ > static UNICODE_STRING PortName = RTL_CONSTANT_STRING(CIS_ALPC_PORT_NAME); > ``` I actually built this in the OS repo to make sure it'll still build, because this code doesn't even build outside Windows. * [x] Closes #6759 * I work here. (cherry picked from commit 349b76795f4f2cd5369d088305d69e5b96e4238e) --- src/interactivity/onecore/ConIoSrvComm.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/interactivity/onecore/ConIoSrvComm.cpp b/src/interactivity/onecore/ConIoSrvComm.cpp index e781f6b786a..c65ad00c0c7 100644 --- a/src/interactivity/onecore/ConIoSrvComm.cpp +++ b/src/interactivity/onecore/ConIoSrvComm.cpp @@ -71,12 +71,11 @@ ConIoSrvComm::~ConIoSrvComm() [[nodiscard]] NTSTATUS ConIoSrvComm::Connect() { - BOOL Ret = TRUE; NTSTATUS Status = STATUS_SUCCESS; // Port handle and name. HANDLE PortHandle; - UNICODE_STRING PortName; + static UNICODE_STRING PortName = RTL_CONSTANT_STRING(CIS_ALPC_PORT_NAME); // Generic Object Manager attributes for the port object and ALPC-specific // port attributes. @@ -98,13 +97,6 @@ ConIoSrvComm::~ConIoSrvComm() // Structure used to iterate over the handles given to us by the server. ALPC_MESSAGE_HANDLE_INFORMATION HandleInfo; - // Initialize the server port name. - Ret = RtlCreateUnicodeString(&PortName, CIS_ALPC_PORT_NAME); - if (!Ret) - { - return STATUS_NO_MEMORY; - } - // Initialize the attributes of the port object. InitializeObjectAttributes(&ObjectAttributes, NULL, From 9c1fe1fa78d30f5cd59ba004306c041fcfaa5413 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Mar 2022 22:14:06 +0100 Subject: [PATCH 63/75] Fix overflow in Viewport::FromDimensions (#12669) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes one source of potential integer overflows from the Viewport class. Other parts were left untouched, as this entire class of overflow issues gets fixed all at once, as soon as we replace COORD with til::coord (etc.). ## PR Checklist * [x] Closes #5271 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Call `ScrollConsoleScreenBufferW` with out of bounds coordinates * Doesn't crash ✅ (cherry picked from commit a4a6dfcc8d691dafd5601c82ff2014aec823a7d5) --- src/renderer/base/renderer.cpp | 3 +-- src/types/viewport.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index 41208fc0346..ebe63ddbad0 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -677,8 +677,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) for (const auto& dirtyRect : dirtyAreas) { - // Shortcut: don't bother redrawing if the width is 0. - if (dirtyRect.left == dirtyRect.right) + if (!dirtyRect) { continue; } diff --git a/src/types/viewport.cpp b/src/types/viewport.cpp index ac3ac498f49..a135546f558 100644 --- a/src/types/viewport.cpp +++ b/src/types/viewport.cpp @@ -46,7 +46,12 @@ Viewport Viewport::FromDimensions(const COORD origin, const short width, const short height) noexcept { - return Viewport::FromExclusive({ origin.X, origin.Y, origin.X + width, origin.Y + height }); + return Viewport::FromInclusive({ + origin.X, + origin.Y, + base::saturated_cast(origin.X + width - 1), + base::saturated_cast(origin.Y + height - 1), + }); } // Function Description: @@ -60,7 +65,12 @@ Viewport Viewport::FromDimensions(const COORD origin, Viewport Viewport::FromDimensions(const COORD origin, const COORD dimensions) noexcept { - return Viewport::FromExclusive({ origin.X, origin.Y, origin.X + dimensions.X, origin.Y + dimensions.Y }); + return Viewport::FromInclusive({ + origin.X, + origin.Y, + base::saturated_cast(origin.X + dimensions.X - 1), + base::saturated_cast(origin.Y + dimensions.Y - 1), + }); } // Function Description: From 442a46e787ae7492bd42fc1692fc2fd49a0196f6 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 17 Mar 2022 20:13:33 +0100 Subject: [PATCH 64/75] Fix uninitialized memory access in GetConsoleTitleA test (#12699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `WideCharToMultiByte` doesn't write a final null-byte by default. `til::u16u8` avoids the problem. ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Test passes in Debug builds ✅ (cherry picked from commit 5072ee640fd6f50b7681e37e8e8be76ef32d4767) --- src/host/ut_host/ApiRoutinesTests.cpp | 28 ++++----------------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/host/ut_host/ApiRoutinesTests.cpp b/src/host/ut_host/ApiRoutinesTests.cpp index a8dfcf4c143..36e8a156c01 100644 --- a/src/host/ut_host/ApiRoutinesTests.cpp +++ b/src/host/ut_host/ApiRoutinesTests.cpp @@ -215,30 +215,10 @@ class ApiRoutinesTests TEST_METHOD(ApiGetConsoleTitleA) { CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation(); - gci.SetTitle(L"Test window title."); - - const auto title = gci.GetTitle(); - - int const iBytesNeeded = WideCharToMultiByte(gci.OutputCP, - 0, - title.data(), - gsl::narrow_cast(title.size()), - nullptr, - 0, - nullptr, - nullptr); - wistd::unique_ptr pszExpected = wil::make_unique_nothrow(iBytesNeeded); - VERIFY_IS_NOT_NULL(pszExpected); - - VERIFY_WIN32_BOOL_SUCCEEDED(WideCharToMultiByte(gci.OutputCP, - 0, - title.data(), - gsl::narrow_cast(title.size()), - pszExpected.get(), - iBytesNeeded, - nullptr, - nullptr)); + // SetTitle() runs some extra code. Let's not skip it since this is a test. + gci.SetTitle(L"Test window title."); + const auto pszExpected = til::u16u8(gci.GetTitle()); char pszTitle[MAX_PATH]; // most applications use MAX_PATH size_t cchWritten = 0; @@ -249,7 +229,7 @@ class ApiRoutinesTests // NOTE: W version of API returns string length. A version of API returns buffer length (string + null). VERIFY_ARE_EQUAL(gci.GetTitle().length() + 1, cchWritten); VERIFY_ARE_EQUAL(gci.GetTitle().length(), cchNeeded); - VERIFY_ARE_EQUAL(WEX::Common::String(pszExpected.get()), WEX::Common::String(pszTitle)); + VERIFY_ARE_EQUAL(std::string_view{ pszExpected }, std::string_view{ pszTitle }); } TEST_METHOD(ApiGetConsoleTitleW) From 31c66efae026b841123efc6999aeb91eec878d7d Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Sat, 19 Mar 2022 00:55:25 +0100 Subject: [PATCH 65/75] Make conhost history deduplication case-sensitive (#12700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The legacy console used to use case-sensitive history deduplication and this change reverts the logic to restore ye olde history functionality. This commit additionally changes the other remaining `std::equal` plus `std::towlower` check into a `CompareStringOrdinal` call, just because that's what MSDN suggests to use in such situations. ## PR Checklist * [x] Closes #4186 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Enter `test /v` * Enter `test /V` * Browsing through the history yields both items ✅ (cherry picked from commit 6bc2b4af09cf3032e2fccd405302ffac70fd348c) --- src/host/history.cpp | 13 ++----------- src/host/ut_host/CommandLineTests.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/host/history.cpp b/src/host/history.cpp index 2b5adf86290..5c655d647e4 100644 --- a/src/host/history.cpp +++ b/src/host/history.cpp @@ -72,14 +72,9 @@ void CommandHistory::s_ResizeAll(const size_t commands) } } -static bool CaseInsensitiveEquality(wchar_t a, wchar_t b) -{ - return ::towlower(a) == ::towlower(b); -} - bool CommandHistory::IsAppNameMatch(const std::wstring_view other) const { - return std::equal(_appName.cbegin(), _appName.cend(), other.cbegin(), other.cend(), CaseInsensitiveEquality); + return CompareStringOrdinal(_appName.data(), gsl::narrow(_appName.size()), other.data(), gsl::narrow(other.size()), TRUE) == CSTR_EQUAL; } // Routine Description: @@ -534,11 +529,7 @@ std::wstring CommandHistory::Remove(const SHORT iDel) const auto& storedCommand = _commands.at(indexFound); if ((WI_IsFlagClear(options, MatchOptions::ExactMatch) && (givenCommand.size() <= storedCommand.size())) || (givenCommand.size() == storedCommand.size())) { - if (std::equal(storedCommand.begin(), - storedCommand.begin() + givenCommand.size(), - givenCommand.begin(), - givenCommand.end(), - CaseInsensitiveEquality)) + if (til::starts_with(storedCommand, givenCommand)) { return true; } diff --git a/src/host/ut_host/CommandLineTests.cpp b/src/host/ut_host/CommandLineTests.cpp index 78f253b1b13..6062690ea2a 100644 --- a/src/host/ut_host/CommandLineTests.cpp +++ b/src/host/ut_host/CommandLineTests.cpp @@ -450,20 +450,21 @@ class CommandLineTests VERIFY_SUCCEEDED(m_pHistory->Add(L"I'm a little teapot", false)); VERIFY_SUCCEEDED(m_pHistory->Add(L"short and stout", false)); VERIFY_SUCCEEDED(m_pHistory->Add(L"inflammable", false)); + VERIFY_SUCCEEDED(m_pHistory->Add(L"Indestructible", false)); - SetPrompt(cookedReadData, L"i"); + SetPrompt(cookedReadData, L"I"); auto& commandLine = CommandLine::Instance(); commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); - VerifyPromptText(cookedReadData, L"inflammable"); + VerifyPromptText(cookedReadData, L"Indestructible"); - // make sure we skip to the next "i" history item + // make sure we skip to the next "I" history item commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); VerifyPromptText(cookedReadData, L"I'm a little teapot"); // should cycle back to the start of the command history commandLine._cycleMatchingCommandHistoryToPrompt(cookedReadData); - VerifyPromptText(cookedReadData, L"inflammable"); + VerifyPromptText(cookedReadData, L"Indestructible"); } TEST_METHOD(CmdlineCtrlHomeFullwidthChars) From 660c000ee0d029e6bc469d017a6486ed9ebe82d8 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Tue, 22 Mar 2022 00:32:45 +0100 Subject: [PATCH 66/75] Force LTR / logical order for text in GdiEngine (#12722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some applications like `vim -H` implement their own BiDi reordering. Previously we used `PolyTextOutW` which supported such arrangements, but with a0527a1 and the switch to `ExtTextOutW` we broke such applications. This commit restores the old behavior by reimplementing the basics of `ExtTextOutW`'s internal workings while enforcing LTR ordering. ## Validation Steps Performed * Create a text file with "ץחסק פחופפסנ חס קוח ז׳חסש ץקקטק פחטסץ" Viewing the text file with `vim -H` presents the contents as expected ✅ * Printing enwik8 is as fast as before ✅ * Font fallback for various eastern scripts in enwik8 works as expected ✅ * `DECDWL` double-width sequences ✅ * Horizontal scrolling (apart from producing expected artifacts) ✅ Closes #12294 (cherry picked from commit d97d9f0fcf89c3c772fcf8c776321e5cb01e26e6) --- .github/actions/spelling/expect/expect.txt | 6 +++ src/renderer/gdi/gdirenderer.hpp | 6 ++- src/renderer/gdi/lib/gdi.vcxproj | 7 +++- src/renderer/gdi/paint.cpp | 49 ++++++++++++++++++++-- src/renderer/gdi/precomp.h | 3 +- src/renderer/gdi/state.cpp | 19 +++------ 6 files changed, 70 insertions(+), 20 deletions(-) diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index e3277a26eed..2b04658e5b3 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -239,6 +239,7 @@ CHARSETINFO chcp checkbox checkboxes +Checkin chh Childitem chk @@ -915,6 +916,7 @@ getwriter GFEh Gfun gfx +GGI GHIJK GHIJKL GHIJKLM @@ -1092,6 +1094,7 @@ ifndef IFont ifstream IGNOREEND +IGNORELANGUAGE IHigh IHosted iid @@ -1458,6 +1461,7 @@ mscorlib msctf msctls msdata +MSDL msdn msft MSGCMDLINEF @@ -2229,6 +2233,7 @@ srect srv srvinit srvpipe +ssa ssh sstream stackoverflow @@ -2554,6 +2559,7 @@ USESHOWWINDOW USESIZE USESTDHANDLES ushort +usp USRDLL utf utils diff --git a/src/renderer/gdi/gdirenderer.hpp b/src/renderer/gdi/gdirenderer.hpp index 6ae3b0e20ea..8b0cab05b4d 100644 --- a/src/renderer/gdi/gdirenderer.hpp +++ b/src/renderer/gdi/gdirenderer.hpp @@ -91,6 +91,8 @@ namespace Microsoft::Console::Render const int nIndex, const LONG dwNewLong) noexcept; + static bool FontHasWesternScript(HDC hdc); + bool _fPaintStarted; til::rect _invalidCharacters; @@ -138,13 +140,15 @@ namespace Microsoft::Console::Render COLORREF _lastFg; COLORREF _lastBg; - enum class FontType : size_t + enum class FontType : uint8_t { + Undefined, Default, Italic, Soft }; FontType _lastFontType; + bool _fontHasWesternScript = false; XFORM _currentLineTransform; LineRendition _currentLineRendition; diff --git a/src/renderer/gdi/lib/gdi.vcxproj b/src/renderer/gdi/lib/gdi.vcxproj index 1a1b96b3b62..bf1fe0758f9 100644 --- a/src/renderer/gdi/lib/gdi.vcxproj +++ b/src/renderer/gdi/lib/gdi.vcxproj @@ -6,7 +6,7 @@ gdi RendererGdi ConRenderGdi - StaticLibrary + StaticLibrary @@ -22,6 +22,11 @@ + + + usp10.lib;%(AdditionalDependencies) + + diff --git a/src/renderer/gdi/paint.cpp b/src/renderer/gdi/paint.cpp index 598ce348987..2a7c38ff443 100644 --- a/src/renderer/gdi/paint.cpp +++ b/src/renderer/gdi/paint.cpp @@ -11,6 +11,15 @@ using namespace Microsoft::Console::Render; +// This is an excerpt of GDI's FontHasWesternScript() as +// used by InternalTextOut() which is part of ExtTextOutW(). +bool GdiEngine::FontHasWesternScript(HDC hdc) +{ + WORD glyphs[4]; + return (GetGlyphIndicesW(hdc, L"dMr\"", 4, glyphs, GGI_MARK_NONEXISTING_GLYPHS) == 4) && + (glyphs[0] != 0xFFFF && glyphs[1] != 0xFFFF && glyphs[2] != 0xFFFF && glyphs[3] != 0xFFFF); +} + // Routine Description: // - Prepares internal structures for a painting operation. // Arguments: @@ -54,6 +63,8 @@ using namespace Microsoft::Console::Render; _debugContext = GetDC(_debugWindow); #endif + _lastFontType = FontType::Undefined; + return S_OK; } @@ -445,10 +456,42 @@ using namespace Microsoft::Console::Render; for (size_t i = 0; i != _cPolyText; ++i) { const auto& t = _pPolyText[i]; - if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags, &t.rcl, t.lpstr, t.n, t.pdx)) + + // The following if/else replicates the essentials of how ExtTextOutW() without ETO_IGNORELANGUAGE works. + // See InternalTextOut(). + // + // Unlike the original, we don't check for `GetTextCharacterExtra(hdc) != 0`, + // because we don't ever call SetTextCharacterExtra() anyways. + // + // GH#12294: + // Additionally we set ss.fOverrideDirection to TRUE, because we need to present RTL + // text in logical order in order to be compatible with applications like `vim -H`. + if (_fontHasWesternScript && ScriptIsComplex(t.lpstr, t.n, SIC_COMPLEX) == S_FALSE) { - hr = E_FAIL; - break; + if (!ExtTextOutW(_hdcMemoryContext, t.x, t.y, t.uiFlags | ETO_IGNORELANGUAGE, &t.rcl, t.lpstr, t.n, t.pdx)) + { + hr = E_FAIL; + break; + } + } + else + { + SCRIPT_STATE ss{}; + ss.fOverrideDirection = TRUE; + + SCRIPT_STRING_ANALYSIS ssa; + hr = ScriptStringAnalyse(_hdcMemoryContext, t.lpstr, t.n, 0, -1, SSA_GLYPHS | SSA_FALLBACK, 0, nullptr, &ss, t.pdx, nullptr, nullptr, &ssa); + if (FAILED(hr)) + { + break; + } + + hr = ScriptStringOut(ssa, t.x, t.y, t.uiFlags, &t.rcl, 0, 0, FALSE); + std::ignore = ScriptStringFree(&ssa); + if (FAILED(hr)) + { + break; + } } } diff --git a/src/renderer/gdi/precomp.h b/src/renderer/gdi/precomp.h index 9043621d11e..4b402939d5e 100644 --- a/src/renderer/gdi/precomp.h +++ b/src/renderer/gdi/precomp.h @@ -16,8 +16,9 @@ Module Name: // This includes support libraries from the CRT, STL, WIL, and GSL #include "LibraryIncludes.h" -#include +#include #include +#include #ifndef _NTSTATUS_DEFINED #define _NTSTATUS_DEFINED diff --git a/src/renderer/gdi/state.cpp b/src/renderer/gdi/state.cpp index 17b939591c2..0d36d39c6cd 100644 --- a/src/renderer/gdi/state.cpp +++ b/src/renderer/gdi/state.cpp @@ -29,7 +29,7 @@ GdiEngine::GdiEngine() : _fInvalidRectUsed(false), _lastFg(INVALID_COLOR), _lastBg(INVALID_COLOR), - _lastFontType(FontType::Default), + _lastFontType(FontType::Undefined), _currentLineTransform(IDENTITY_XFORM), _currentLineRendition(LineRendition::SingleWidth), _fPaintStarted(false), @@ -142,15 +142,6 @@ GdiEngine::~GdiEngine() _hwndTargetWindow = hwnd; _hdcMemoryContext = hdcNewMemoryContext; - // If we have a font, apply it to the context. - if (nullptr != _hfont) - { - LOG_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont)); - } - - // Record the fact that the selected font is the default. - _lastFontType = FontType::Default; - if (nullptr != hdcRealWindow) { LOG_HR_IF(E_FAIL, !(ReleaseDC(_hwndTargetWindow, hdcRealWindow))); @@ -327,6 +318,7 @@ GdiEngine::~GdiEngine() break; } _lastFontType = fontType; + _fontHasWesternScript = FontHasWesternScript(_hdcMemoryContext); } return S_OK; @@ -348,9 +340,6 @@ GdiEngine::~GdiEngine() // Select into DC RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, hFont.get())); - // Record the fact that the selected font is the default. - _lastFontType = FontType::Default; - // Save off the font metrics for various other calculations RETURN_HR_IF(E_FAIL, !(GetTextMetricsW(_hdcMemoryContext, &_tmFontMetrics))); @@ -456,7 +445,9 @@ GdiEngine::~GdiEngine() const SIZE cellSize, const size_t centeringHint) noexcept { - // If the soft font is currently selected, replace it with the default font. + // If we previously called SelectFont(_hdcMemoryContext, _softFont), it will + // still hold a reference to the _softFont object we're planning to overwrite. + // --> First revert back to the standard _hfont, lest we have dangling pointers. if (_lastFontType == FontType::Soft) { RETURN_HR_IF_NULL(E_FAIL, SelectFont(_hdcMemoryContext, _hfont)); From eddeaab829cbd6cbd870c4aceb238dc236d72edb Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 23 Mar 2022 14:20:03 -0700 Subject: [PATCH 67/75] [Conhost] Notify UIA when letter deleted via backspace (#12735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request Fixes a bug in ConHost where Narrator wouldn't read the deleted letter after the user pressed backspace. ## References MSFT:31748387 ## Detailed Description of the Pull Request / Additional comments `WriteCharsLegacy()` already calls `NotifyAccessibilityEventing()` when text is inserted into the buffer ([see code](https://github.com/microsoft/terminal/blob/855e1360c0ff810decf862f1d90e15b5f49e7bbd/src/host/_stream.cpp#L559-L563)). However, when backspace is pressed, the entire if-condition is skipped over, resulting in the accessibility event not being fired. `WriteCharsLegacy()` has a separate branch that is dedicated to handling backspace, so I added a call to the relevant logic to notify UIA at the end of that. ## Validation Steps Performed ✅ Backspace deletes a character and Narrator reads it ✅ Backspace still works with NVDA and JAWS (unchanged behavior) ✅ if the input buffer had wrapped text, the above scenario works as expected ✅ scenario works for CMD, PowerShell Core, and WSL --- src/host/_stream.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index 544b89af995..8407f5e0759 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -765,6 +765,15 @@ using Microsoft::Console::VirtualTerminal::StateMachine; Status = AdjustCursorPosition(screenInfo, CursorPosition, dwFlags & WC_KEEP_CURSOR_VISIBLE, psScrollY); } } + // Notify accessibility to read the backspaced character. + // See GH:12735, MSFT:31748387 + if (screenInfo.HasAccessibilityEventing()) + { + if (IConsoleWindow* pConsoleWindow = ServiceLocator::LocateConsoleWindow()) + { + LOG_IF_FAILED(pConsoleWindow->SignalUia(UIA_Text_TextChangedEventId)); + } + } break; } case UNICODE_TAB: From a06db87d7e762628ffed41bdc7c8bc16d8555830 Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Wed, 23 Mar 2022 21:58:19 +0000 Subject: [PATCH 68/75] Merged PR 7106028: Delay-load usp10 in the Windows build PR #12722 introduced a dependency on usp10.dll. The Script* APIs are split between gdi32 and usp10, so we needed to add a new entry to the sources files for anybody who consumed the GDI renderer. --- src/host/sources.inc | 2 ++ src/interactivity/win32/ut_interactivity_win32/sources | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/host/sources.inc b/src/host/sources.inc index 042622c404e..6ab23269b8d 100644 --- a/src/host/sources.inc +++ b/src/host/sources.inc @@ -176,6 +176,7 @@ TARGETLIBS = \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ @@ -210,6 +211,7 @@ DELAYLOAD = \ api-ms-win-shell-namespace-l1.dll; \ ext-ms-win-dwmapi-ext-l1.dll; \ ext-ms-win-edputil-policy-l1.dll; \ + ext-ms-win-usp10-l1.dll; \ ext-ms-win-gdi-dc-l1.dll; \ ext-ms-win-gdi-dc-create-l1.dll; \ ext-ms-win-gdi-draw-l1.dll; \ diff --git a/src/interactivity/win32/ut_interactivity_win32/sources b/src/interactivity/win32/ut_interactivity_win32/sources index 0be29330cb6..cf6c8f6bdf3 100644 --- a/src/interactivity/win32/ut_interactivity_win32/sources +++ b/src/interactivity/win32/ut_interactivity_win32/sources @@ -75,6 +75,7 @@ TARGETLIBS = \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-dataobject-l1.lib \ $(ONECORESHELL_INTERNAL_LIB_VPATH_L)\api-ms-win-shell-namespace-l1.lib \ $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-uiacore-l1.lib \ + $(MODERNCORE_INTERNAL_PRIV_SDK_LIB_VPATH_L)\ext-ms-win-usp10-l1.lib \ $(WINCORE_OBJ_PATH)\console\conint\$(O)\conint.lib \ $(WINCORE_OBJ_PATH)\console\open\src\buffer\out\lib\$(O)\conbufferout.lib \ $(WINCORE_OBJ_PATH)\console\open\src\host\lib\$(O)\conhostv2.lib \ @@ -137,6 +138,7 @@ DELAYLOAD = \ ext-ms-win-shell-shell32-l1.dll; \ ext-ms-win-uiacore-l1.dll; \ ext-ms-win-uxtheme-themes-l1.dll; \ + ext-ms-win-usp10-l1.dll; \ DLOAD_ERROR_HANDLER = kernelbase From d44117c755d547d7885bf015a57839f15e2ea395 Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Wed, 23 Mar 2022 14:20:03 -0700 Subject: [PATCH 69/75] [Conhost] Notify UIA when letter deleted via backspace (#12735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request Fixes a bug in ConHost where Narrator wouldn't read the deleted letter after the user pressed backspace. ## References MSFT:31748387 ## Detailed Description of the Pull Request / Additional comments `WriteCharsLegacy()` already calls `NotifyAccessibilityEventing()` when text is inserted into the buffer ([see code](https://github.com/microsoft/terminal/blob/855e1360c0ff810decf862f1d90e15b5f49e7bbd/src/host/_stream.cpp#L559-L563)). However, when backspace is pressed, the entire if-condition is skipped over, resulting in the accessibility event not being fired. `WriteCharsLegacy()` has a separate branch that is dedicated to handling backspace, so I added a call to the relevant logic to notify UIA at the end of that. ## Validation Steps Performed ✅ Backspace deletes a character and Narrator reads it ✅ Backspace still works with NVDA and JAWS (unchanged behavior) ✅ if the input buffer had wrapped text, the above scenario works as expected ✅ scenario works for CMD, PowerShell Core, and WSL --- src/host/_stream.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/host/_stream.cpp b/src/host/_stream.cpp index dfe8fd51e14..f7849ef991f 100644 --- a/src/host/_stream.cpp +++ b/src/host/_stream.cpp @@ -765,6 +765,15 @@ using Microsoft::Console::VirtualTerminal::StateMachine; Status = AdjustCursorPosition(screenInfo, CursorPosition, dwFlags & WC_KEEP_CURSOR_VISIBLE, psScrollY); } } + // Notify accessibility to read the backspaced character. + // See GH:12735, MSFT:31748387 + if (screenInfo.HasAccessibilityEventing()) + { + if (IConsoleWindow* pConsoleWindow = ServiceLocator::LocateConsoleWindow()) + { + LOG_IF_FAILED(pConsoleWindow->SignalUia(UIA_Text_TextChangedEventId)); + } + } break; } case UNICODE_TAB: From 73f02024d29cf558e42633f93e707631c306bf6f Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 24 Mar 2022 00:48:08 +0100 Subject: [PATCH 70/75] AtlasEngine: Fix grayscale blending shader (#12734) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 5964060 contains a regression were the grayscale blending algorithm used the gamma corrected foreground color as the pixel color, instead of blending that color with the background color first. Due to that the background color got lost / got set to black. This breaks any dark-on-bright outputs. ## PR Checklist * [x] I work here * [x] Tests added/passed ## Validation Steps Performed All 3 "antialiasing" settings work just like in DxEngine. ✅ --- src/renderer/atlas/dwrite.hlsl | 3 ++- src/renderer/atlas/shader_ps.hlsl | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/atlas/dwrite.hlsl b/src/renderer/atlas/dwrite.hlsl index 0f5c683bc5b..ad7493275b7 100644 --- a/src/renderer/atlas/dwrite.hlsl +++ b/src/renderer/atlas/dwrite.hlsl @@ -81,7 +81,8 @@ float4 DWrite_GrayscaleBlend(float4 gammaRatios, float grayscaleEnhancedContrast float blendEnhancedContrast = contrastBoost + DWrite_ApplyLightOnDarkContrastAdjustment(grayscaleEnhancedContrast, foregroundStraight); float intensity = DWrite_CalcColorIntensity(foregroundStraight); float contrasted = DWrite_EnhanceContrast(glyphAlpha, blendEnhancedContrast); - return foregroundColor * DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); + float alphaCorrected = DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); + return alphaCorrected * foregroundColor; } // Call this function to get the same gamma corrected alpha blending effect diff --git a/src/renderer/atlas/shader_ps.hlsl b/src/renderer/atlas/shader_ps.hlsl index fadbf3469d5..789cd98b00a 100644 --- a/src/renderer/atlas/shader_ps.hlsl +++ b/src/renderer/atlas/shader_ps.hlsl @@ -147,7 +147,8 @@ float4 main(float4 pos: SV_Position): SV_Target // See DWrite_GrayscaleBlend float intensity = DWrite_CalcColorIntensity(foregroundStraight); float contrasted = DWrite_EnhanceContrast(glyph.a, blendEnhancedContrast); - color = fg * DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); + float4 alphaCorrected = DWrite_ApplyAlphaCorrection(contrasted, intensity, gammaRatios); + color = alphaBlendPremultiplied(color, alphaCorrected * fg); } } } From a638e0161b3a1fee2e564dd92e12eb8d51331a2c Mon Sep 17 00:00:00 2001 From: Dustin Howett Date: Thu, 24 Mar 2022 00:25:04 +0000 Subject: [PATCH 71/75] PR 7106673: [Git2Git] Remove the rest of IInputServices Since RS1 (CL 3427806), gdi32 has been available on OneCoreUAP-based editions of Windows. Therefore, we no longer need the indirect call to TranslateCharsetInfo and without that, IInputServices doesn't have a reason to exist. In an ideal world. Unfortunately, we actually do use IInputServices as a backhanded way to get at the ConIoSrvComm from other OneCoreInteractivity components... we also use it in a trick we play in RundownAndExit to make sure that the ConIoSrv connection tears down at the right time. I've replaced that trick with an equally dirty trick, but one that is *very explicit* about what it's doing. This change would break CJK+Grid Lines and GetConsoleLangID on OneCore-based editions that do not host the extension apiset ext-ms-win-gdi-font-l1... so we're falling back to the old ConIoSrvComm implementation directly in `dbcs.cpp`. Retrieved from https://microsoft.visualstudio.com os.2020 OS official/rs_wdx_dxp_windev 74ca635710701c45cda9eefd13dafc6f180feb49 Related work items: MSFT-38632962 --- src/host/dbcs.cpp | 32 +++++++++++-- .../base/InteractivityFactory.cpp | 43 ----------------- .../base/InteractivityFactory.hpp | 1 - src/interactivity/base/ServiceLocator.cpp | 40 +++++----------- .../base/lib/InteractivityBase.vcxproj | 1 - .../lib/InteractivityBase.vcxproj.filters | 3 -- src/interactivity/inc/IInputServices.hpp | 33 ------------- .../inc/IInteractivityFactory.hpp | 2 - src/interactivity/inc/Module.hpp | 1 - src/interactivity/inc/ServiceLocator.hpp | 13 ++--- src/interactivity/onecore/BgfxEngine.cpp | 4 +- src/interactivity/onecore/ConIoSrvComm.cpp | 47 +++++-------------- src/interactivity/onecore/ConIoSrvComm.hpp | 10 ++-- .../onecore/ConsoleInputThread.cpp | 9 +--- .../onecore/ConsoleInputThread.hpp | 5 -- src/interactivity/onecore/WindowMetrics.cpp | 2 +- .../onecore/lib/onecore.LIB.vcxproj | 2 - .../onecore/lib/onecore.LIB.vcxproj.filters | 8 +--- src/interactivity/win32/InputServices.cpp | 15 ------ src/interactivity/win32/InputServices.hpp | 26 ---------- src/interactivity/win32/lib/win32.LIB.vcxproj | 2 - .../win32/lib/win32.LIB.vcxproj.filters | 8 +--- src/interactivity/win32/sources.inc | 1 - 23 files changed, 69 insertions(+), 239 deletions(-) delete mode 100644 src/interactivity/inc/IInputServices.hpp delete mode 100644 src/interactivity/win32/InputServices.cpp delete mode 100644 src/interactivity/win32/InputServices.hpp diff --git a/src/host/dbcs.cpp b/src/host/dbcs.cpp index eebfcf52a7e..2a25141f159 100644 --- a/src/host/dbcs.cpp +++ b/src/host/dbcs.cpp @@ -127,12 +127,36 @@ bool IsDBCSLeadByteConsole(const CHAR ch, const CPINFO* const pCPInfo) BYTE CodePageToCharSet(const UINT uiCodePage) { - CHARSETINFO csi; + CHARSETINFO csi{}; - const auto inputServices = ServiceLocator::LocateInputServices(); - if (nullptr == inputServices || !inputServices->TranslateCharsetInfo((DWORD*)IntToPtr(uiCodePage), &csi, TCI_SRCCODEPAGE)) + if (!TranslateCharsetInfo((DWORD*)IntToPtr(uiCodePage), &csi, TCI_SRCCODEPAGE)) { - csi.ciCharset = OEM_CHARSET; + // On OneCore-based editions of Windows, the extension APIset containing + // TranslateCharsetInfo is not hosted. OneCoreUAP hosts it, but the lower + // editions do not. If we find that we failed to delay-load it, fall back + // to our "simple" OneCore-OK implementation. + if (GetLastError() == ERROR_PROC_NOT_FOUND) + { + switch (uiCodePage) + { + case CP_JAPANESE: + csi.ciCharset = SHIFTJIS_CHARSET; + break; + case CP_CHINESE_SIMPLIFIED: + csi.ciCharset = GB2312_CHARSET; + break; + case CP_KOREAN: + csi.ciCharset = HANGEUL_CHARSET; + break; + case CP_CHINESE_TRADITIONAL: + csi.ciCharset = CHINESEBIG5_CHARSET; + break; + } + } + else + { + csi.ciCharset = OEM_CHARSET; + } } return (BYTE)csi.ciCharset; diff --git a/src/interactivity/base/InteractivityFactory.cpp b/src/interactivity/base/InteractivityFactory.cpp index 3c69ebb46bf..fd93f6ec158 100644 --- a/src/interactivity/base/InteractivityFactory.cpp +++ b/src/interactivity/base/InteractivityFactory.cpp @@ -18,7 +18,6 @@ #include "../win32/AccessibilityNotifier.hpp" #include "../win32/ConsoleControl.hpp" #include "../win32/ConsoleInputThread.hpp" -#include "../win32/InputServices.hpp" #include "../win32/WindowDpiApi.hpp" #include "../win32/WindowMetrics.hpp" #include "../win32/SystemConfigurationProvider.hpp" @@ -282,48 +281,6 @@ using namespace Microsoft::Console::Interactivity; return status; } -[[nodiscard]] NTSTATUS InteractivityFactory::CreateInputServices(_Inout_ std::unique_ptr& services) -{ - NTSTATUS status = STATUS_SUCCESS; - - ApiLevel level; - status = ApiDetector::DetectNtUserWindow(&level); - - if (NT_SUCCESS(status)) - { - std::unique_ptr newServices; - try - { - switch (level) - { - case ApiLevel::Win32: - newServices = std::make_unique(); - break; - -#ifdef BUILD_ONECORE_INTERACTIVITY - case ApiLevel::OneCore: - newServices = std::make_unique(); - break; -#endif - default: - status = STATUS_INVALID_LEVEL; - break; - } - } - catch (...) - { - status = NTSTATUS_FROM_HRESULT(wil::ResultFromCaughtException()); - } - - if (NT_SUCCESS(status)) - { - services.swap(newServices); - } - } - - return status; -} - // Method Description: // - Attempts to instantiate a "pseudo window" for when we're operating in // pseudoconsole mode. There are some tools (cygwin & derivatives) that use diff --git a/src/interactivity/base/InteractivityFactory.hpp b/src/interactivity/base/InteractivityFactory.hpp index bf08158343f..97ac10d3b9f 100644 --- a/src/interactivity/base/InteractivityFactory.hpp +++ b/src/interactivity/base/InteractivityFactory.hpp @@ -25,7 +25,6 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] NTSTATUS CreateWindowMetrics(_Inout_ std::unique_ptr& metrics); [[nodiscard]] NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr& notifier); [[nodiscard]] NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr& provider); - [[nodiscard]] NTSTATUS CreateInputServices(_Inout_ std::unique_ptr& services); [[nodiscard]] NTSTATUS CreatePseudoWindow(HWND& hwnd); }; diff --git a/src/interactivity/base/ServiceLocator.cpp b/src/interactivity/base/ServiceLocator.cpp index 3bc691abdc6..91a56d2e5a8 100644 --- a/src/interactivity/base/ServiceLocator.cpp +++ b/src/interactivity/base/ServiceLocator.cpp @@ -21,10 +21,11 @@ std::unique_ptr ServiceLocator::s_windowMetrics; std::unique_ptr ServiceLocator::s_accessibilityNotifier; std::unique_ptr ServiceLocator::s_highDpiApi; std::unique_ptr ServiceLocator::s_systemConfigurationProvider; -std::unique_ptr ServiceLocator::s_inputServices; IConsoleWindow* ServiceLocator::s_consoleWindow = nullptr; +void (*ServiceLocator::s_oneCoreTeardownFunction)() = nullptr; + Globals ServiceLocator::s_globals; bool ServiceLocator::s_pseudoWindowInitialized = false; @@ -34,6 +35,12 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr; #pragma region Public Methods +void ServiceLocator::SetOneCoreTeardownFunction(void (*pfn)()) noexcept +{ + FAIL_FAST_IF(nullptr != s_oneCoreTeardownFunction); + s_oneCoreTeardownFunction = pfn; +} + [[noreturn]] void ServiceLocator::RundownAndExit(const HRESULT hr) { // MSFT:15506250 @@ -59,14 +66,13 @@ wil::unique_hwnd ServiceLocator::s_pseudoWindow = nullptr; // We don't want to have other execution in the system get stuck, so this is a great // place to clean up and notify any objects or threads in the system that have to cleanup safely before // we head into TerminateProcess and tear everything else down less gracefully. - - // TODO: MSFT: 14397093 - Expand graceful rundown beyond just the Hot Bug input services case. - - if (s_inputServices.get() != nullptr) + if (s_oneCoreTeardownFunction) { - s_inputServices.reset(nullptr); + s_oneCoreTeardownFunction(); } + // TODO: MSFT: 14397093 - Expand graceful rundown beyond just the Hot Bug input services case. + ExitProcess(hr); } @@ -267,28 +273,6 @@ ISystemConfigurationProvider* ServiceLocator::LocateSystemConfigurationProvider( return s_systemConfigurationProvider.get(); } -IInputServices* ServiceLocator::LocateInputServices() -{ - NTSTATUS status = STATUS_SUCCESS; - - if (!s_inputServices) - { - if (s_interactivityFactory.get() == nullptr) - { - status = ServiceLocator::LoadInteractivityFactory(); - } - - if (NT_SUCCESS(status)) - { - status = s_interactivityFactory->CreateInputServices(s_inputServices); - } - } - - LOG_IF_NTSTATUS_FAILED(status); - - return s_inputServices.get(); -} - Globals& ServiceLocator::LocateGlobals() { return s_globals; diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj b/src/interactivity/base/lib/InteractivityBase.vcxproj index 5f3f7c4e8e5..0161b56daa4 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj @@ -36,7 +36,6 @@ - diff --git a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters index 4c7b8495037..5b833a07127 100644 --- a/src/interactivity/base/lib/InteractivityBase.vcxproj.filters +++ b/src/interactivity/base/lib/InteractivityBase.vcxproj.filters @@ -62,9 +62,6 @@ Header Files - - Header Files - Header Files diff --git a/src/interactivity/inc/IInputServices.hpp b/src/interactivity/inc/IInputServices.hpp deleted file mode 100644 index 390a76a5e40..00000000000 --- a/src/interactivity/inc/IInputServices.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- IInputServices.hpp - -Abstract: -- Defines the methods used by the console to process input. - -Author(s): -- Hernan Gatta (HeGatta) 29-Mar-2017 ---*/ - -#pragma once - -namespace Microsoft::Console::Interactivity -{ - class IInputServices - { - public: - virtual BOOL TranslateCharsetInfo(_Inout_ DWORD FAR* lpSrc, _Out_ LPCHARSETINFO lpCs, _In_ DWORD dwFlags) = 0; - virtual ~IInputServices() = 0; - - protected: - IInputServices() {} - - IInputServices(IInputServices const&) = delete; - IInputServices& operator=(IInputServices const&) = delete; - }; - - inline IInputServices::~IInputServices() {} -} diff --git a/src/interactivity/inc/IInteractivityFactory.hpp b/src/interactivity/inc/IInteractivityFactory.hpp index 4996f30d689..65c28428b4f 100644 --- a/src/interactivity/inc/IInteractivityFactory.hpp +++ b/src/interactivity/inc/IInteractivityFactory.hpp @@ -23,7 +23,6 @@ Author(s): #include "IWindowMetrics.hpp" #include "IAccessibilityNotifier.hpp" #include "ISystemConfigurationProvider.hpp" -#include "IInputServices.hpp" #include @@ -40,7 +39,6 @@ namespace Microsoft::Console::Interactivity [[nodiscard]] virtual NTSTATUS CreateWindowMetrics(_Inout_ std::unique_ptr& metrics) = 0; [[nodiscard]] virtual NTSTATUS CreateAccessibilityNotifier(_Inout_ std::unique_ptr& notifier) = 0; [[nodiscard]] virtual NTSTATUS CreateSystemConfigurationProvider(_Inout_ std::unique_ptr& provider) = 0; - [[nodiscard]] virtual NTSTATUS CreateInputServices(_Inout_ std::unique_ptr& services) = 0; [[nodiscard]] virtual NTSTATUS CreatePseudoWindow(HWND& hwnd) = 0; }; diff --git a/src/interactivity/inc/Module.hpp b/src/interactivity/inc/Module.hpp index cea667e43c0..320566a6e7b 100644 --- a/src/interactivity/inc/Module.hpp +++ b/src/interactivity/inc/Module.hpp @@ -24,7 +24,6 @@ namespace Microsoft::Console::Interactivity ConsoleInputThread, ConsoleWindowMetrics, HighDpiApi, - InputServices, SystemConfigurationProvider }; } diff --git a/src/interactivity/inc/ServiceLocator.hpp b/src/interactivity/inc/ServiceLocator.hpp index 41e27bcdf9f..5b6aaee499f 100644 --- a/src/interactivity/inc/ServiceLocator.hpp +++ b/src/interactivity/inc/ServiceLocator.hpp @@ -30,6 +30,8 @@ namespace Microsoft::Console::Interactivity class ServiceLocator final { public: + static void SetOneCoreTeardownFunction(void (*pfn)()) noexcept; + [[noreturn]] static void RundownAndExit(const HRESULT hr); // N.B.: Location methods without corresponding creation methods @@ -78,13 +80,6 @@ namespace Microsoft::Console::Interactivity return static_cast(LocateHighDpiApi()); } - static IInputServices* LocateInputServices(); - template - static T* LocateInputServices() - { - return static_cast(LocateInputServices()); - } - static ISystemConfigurationProvider* LocateSystemConfigurationProvider(); static Globals& LocateGlobals(); @@ -110,7 +105,9 @@ namespace Microsoft::Console::Interactivity static std::unique_ptr s_windowMetrics; static std::unique_ptr s_highDpiApi; static std::unique_ptr s_systemConfigurationProvider; - static std::unique_ptr s_inputServices; + + // See the big block comment in RundownAndExit for more info. + static void (*s_oneCoreTeardownFunction)(); static Globals s_globals; static bool s_pseudoWindowInitialized; diff --git a/src/interactivity/onecore/BgfxEngine.cpp b/src/interactivity/onecore/BgfxEngine.cpp index 87484455935..b3bdd606651 100644 --- a/src/interactivity/onecore/BgfxEngine.cpp +++ b/src/interactivity/onecore/BgfxEngine.cpp @@ -87,7 +87,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid PVOID OldRunBase; PVOID NewRunBase; - Status = ServiceLocator::LocateInputServices()->RequestUpdateDisplay(0); + Status = ConIoSrvComm::GetConIoSrvComm()->RequestUpdateDisplay(0); if (NT_SUCCESS(Status)) { @@ -189,7 +189,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid CursorInfo.Height = options.ulCursorHeightPercent; CursorInfo.IsVisible = TRUE; - NTSTATUS Status = ServiceLocator::LocateInputServices()->RequestSetCursor(&CursorInfo); + NTSTATUS Status = ConIoSrvComm::GetConIoSrvComm()->RequestSetCursor(&CursorInfo); return HRESULT_FROM_NT(Status); } diff --git a/src/interactivity/onecore/ConIoSrvComm.cpp b/src/interactivity/onecore/ConIoSrvComm.cpp index c65ad00c0c7..f33e2cb1a08 100644 --- a/src/interactivity/onecore/ConIoSrvComm.cpp +++ b/src/interactivity/onecore/ConIoSrvComm.cpp @@ -24,6 +24,19 @@ extern void UnlockConsole(); using namespace Microsoft::Console::Render; using namespace Microsoft::Console::Interactivity::OneCore; +static std::unique_ptr s_conIoSrvComm; +ConIoSrvComm* ConIoSrvComm::GetConIoSrvComm() +{ + static bool initialized = []() { + s_conIoSrvComm = std::make_unique(); + ServiceLocator::SetOneCoreTeardownFunction([] { + s_conIoSrvComm.reset(nullptr); + }); + return true; + }(); + return s_conIoSrvComm.get(); +} + ConIoSrvComm::ConIoSrvComm() : _inputPipeThreadHandle(nullptr), _pipeReadHandle(INVALID_HANDLE_VALUE), @@ -536,40 +549,6 @@ PVOID ConIoSrvComm::GetSharedViewBase() const #pragma endregion -#pragma region IInputServices Members - -BOOL ConIoSrvComm::TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags) -{ - SetLastError(ERROR_SUCCESS); - - if (TCI_SRCCODEPAGE == dwFlags) - { - *lpCs = { 0 }; - - DWORD dwSrc = (DWORD)lpSrc; - switch (dwSrc) - { - case CP_JAPANESE: - lpCs->ciCharset = SHIFTJIS_CHARSET; - return TRUE; - case CP_CHINESE_SIMPLIFIED: - lpCs->ciCharset = GB2312_CHARSET; - return TRUE; - case CP_KOREAN: - lpCs->ciCharset = HANGEUL_CHARSET; - return TRUE; - case CP_CHINESE_TRADITIONAL: - lpCs->ciCharset = CHINESEBIG5_CHARSET; - return TRUE; - } - } - - SetLastError(ERROR_NOT_SUPPORTED); - return FALSE; -} - -#pragma endregion - [[nodiscard]] NTSTATUS ConIoSrvComm::InitializeBgfx() { NTSTATUS Status; diff --git a/src/interactivity/onecore/ConIoSrvComm.hpp b/src/interactivity/onecore/ConIoSrvComm.hpp index 4a52ed53617..b94c1f226f6 100644 --- a/src/interactivity/onecore/ConIoSrvComm.hpp +++ b/src/interactivity/onecore/ConIoSrvComm.hpp @@ -17,7 +17,6 @@ Author(s): #include #include "ConIoSrv.h" -#include "../../inc/IInputServices.hpp" #include "BgfxEngine.hpp" #include "../../renderer/wddmcon/wddmconrenderer.hpp" @@ -26,11 +25,13 @@ Author(s): namespace Microsoft::Console::Interactivity::OneCore { - class ConIoSrvComm final : public IInputServices + class ConIoSrvComm final { public: ConIoSrvComm(); - ~ConIoSrvComm() override; + ~ConIoSrvComm(); + + static ConIoSrvComm* GetConIoSrvComm(); [[nodiscard]] NTSTATUS Connect(); VOID ServiceInputPipe(); @@ -46,9 +47,6 @@ namespace Microsoft::Console::Interactivity::OneCore VOID CleanupForHeadless(const NTSTATUS status); - // IInputServices Members - BOOL TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags); - [[nodiscard]] NTSTATUS InitializeBgfx(); [[nodiscard]] NTSTATUS InitializeWddmCon(); diff --git a/src/interactivity/onecore/ConsoleInputThread.cpp b/src/interactivity/onecore/ConsoleInputThread.cpp index b49b06fbf2b..53a3949c882 100644 --- a/src/interactivity/onecore/ConsoleInputThread.cpp +++ b/src/interactivity/onecore/ConsoleInputThread.cpp @@ -19,7 +19,7 @@ using namespace Microsoft::Console::Interactivity::OneCore; DWORD WINAPI ConsoleInputThreadProcOneCore(LPVOID /*lpParam*/) { Globals& globals = ServiceLocator::LocateGlobals(); - ConIoSrvComm* const Server = ServiceLocator::LocateInputServices(); + ConIoSrvComm* const Server = ConIoSrvComm::GetConIoSrvComm(); NTSTATUS Status = Server->Connect(); @@ -114,7 +114,7 @@ HANDLE ConsoleInputThread::Start() hThread = CreateThread(nullptr, 0, ConsoleInputThreadProcOneCore, - _pConIoSrvComm, + nullptr, 0, &dwThreadId); if (hThread) @@ -125,8 +125,3 @@ HANDLE ConsoleInputThread::Start() return hThread; } - -ConIoSrvComm* ConsoleInputThread::GetConIoSrvComm() -{ - return _pConIoSrvComm; -} diff --git a/src/interactivity/onecore/ConsoleInputThread.hpp b/src/interactivity/onecore/ConsoleInputThread.hpp index fef4fedee91..f47072f9d2e 100644 --- a/src/interactivity/onecore/ConsoleInputThread.hpp +++ b/src/interactivity/onecore/ConsoleInputThread.hpp @@ -25,10 +25,5 @@ namespace Microsoft::Console::Interactivity::OneCore { public: HANDLE Start(); - - ConIoSrvComm* GetConIoSrvComm(); - - private: - ConIoSrvComm* _pConIoSrvComm; }; } diff --git a/src/interactivity/onecore/WindowMetrics.cpp b/src/interactivity/onecore/WindowMetrics.cpp index ed38cac4fd6..f5721094576 100644 --- a/src/interactivity/onecore/WindowMetrics.cpp +++ b/src/interactivity/onecore/WindowMetrics.cpp @@ -46,7 +46,7 @@ RECT WindowMetrics::GetMinClientRectInPixels() USHORT DisplayMode; // Fetch a reference to the Console IO Server. - Server = ServiceLocator::LocateInputServices(); + Server = ConIoSrvComm::GetConIoSrvComm(); // Figure out what kind of display we are using. DisplayMode = Server->GetDisplayMode(); diff --git a/src/interactivity/onecore/lib/onecore.LIB.vcxproj b/src/interactivity/onecore/lib/onecore.LIB.vcxproj index 70c6fb4cb26..69fb551c0f8 100644 --- a/src/interactivity/onecore/lib/onecore.LIB.vcxproj +++ b/src/interactivity/onecore/lib/onecore.LIB.vcxproj @@ -20,7 +20,6 @@ - Create @@ -33,7 +32,6 @@ - diff --git a/src/interactivity/onecore/lib/onecore.LIB.vcxproj.filters b/src/interactivity/onecore/lib/onecore.LIB.vcxproj.filters index 3d23865a357..203fc8a4bae 100644 --- a/src/interactivity/onecore/lib/onecore.LIB.vcxproj.filters +++ b/src/interactivity/onecore/lib/onecore.LIB.vcxproj.filters @@ -39,9 +39,6 @@ Source Files - - Source Files - @@ -68,8 +65,5 @@ Header Files - - Header Files - - \ No newline at end of file + diff --git a/src/interactivity/win32/InputServices.cpp b/src/interactivity/win32/InputServices.cpp deleted file mode 100644 index 44014fd9284..00000000000 --- a/src/interactivity/win32/InputServices.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "InputServices.hpp" - -#pragma hdrstop - -using namespace Microsoft::Console::Interactivity::Win32; - -BOOL InputServices::TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags) -{ - return ::TranslateCharsetInfo(lpSrc, lpCs, dwFlags); -} diff --git a/src/interactivity/win32/InputServices.hpp b/src/interactivity/win32/InputServices.hpp deleted file mode 100644 index 00d74b1cabb..00000000000 --- a/src/interactivity/win32/InputServices.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/*++ -Copyright (c) Microsoft Corporation -Licensed under the MIT license. - -Module Name: -- InputServices.hpp - -Abstract: -- Win32 implementation of the IInputServices interface. - -Author(s): -- Hernan Gatta (HeGatta) 29-Mar-2017 ---*/ - -#include "../inc/IInputServices.hpp" - -namespace Microsoft::Console::Interactivity::Win32 -{ - class InputServices final : public IInputServices - { - public: - // Inherited via IInputServices - ~InputServices() = default; - BOOL TranslateCharsetInfo(DWORD* lpSrc, LPCHARSETINFO lpCs, DWORD dwFlags); - }; -} diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj b/src/interactivity/win32/lib/win32.LIB.vcxproj index a29cec6458d..82a57c12398 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj @@ -22,7 +22,6 @@ - Create @@ -46,7 +45,6 @@ - diff --git a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters index 4204622caea..52ecd14f5b7 100644 --- a/src/interactivity/win32/lib/win32.LIB.vcxproj.filters +++ b/src/interactivity/win32/lib/win32.LIB.vcxproj.filters @@ -63,9 +63,6 @@ Source Files - - Source Files - Source Files @@ -125,9 +122,6 @@ Header Files - - Header Files - Header Files @@ -141,4 +135,4 @@ - \ No newline at end of file + diff --git a/src/interactivity/win32/sources.inc b/src/interactivity/win32/sources.inc index 25d12cc0685..3580a5d6186 100644 --- a/src/interactivity/win32/sources.inc +++ b/src/interactivity/win32/sources.inc @@ -44,7 +44,6 @@ SOURCES = \ ..\consoleKeyInfo.cpp \ ..\find.cpp \ ..\icon.cpp \ - ..\InputServices.cpp \ ..\menu.cpp \ ..\screenInfoUiaProvider.cpp \ ..\SystemConfigurationProvider.cpp \ From 8ec87dff56a1e9866c1200dc351ec81372d00d40 Mon Sep 17 00:00:00 2001 From: Kayla Cinnamon Date: Thu, 24 Mar 2022 10:54:12 -0700 Subject: [PATCH 72/75] Change trimBlockSelection default to true (#12737) Changed the default value of `"trimBlockSelection"` to `true`. * [x] Closes #12536 ## Validation Steps Performed Without editing the setting, the toggle switch was enabled by default in the settings UI. --- doc/cascadia/profiles.schema.json | 2 +- src/cascadia/TerminalSettingsModel/MTSMSettings.h | 2 +- src/cascadia/TerminalSettingsModel/TerminalSettings.h | 2 +- src/cascadia/TerminalSettingsModel/defaults.json | 2 +- src/cascadia/inc/ControlProperties.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 3a401661150..ac7ab589514 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1633,7 +1633,7 @@ "$ref": "#/$defs/CopyFormat" }, "trimBlockSelection": { - "default": false, + "default": true, "description": "When set to true, trailing white-spaces will be removed from text in rectangular (block) selection while copied to your clipboard. When set to false, the white-spaces will be preserved.", "type": "boolean" }, diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index f2f95a562f5..5cba5156767 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -27,7 +27,7 @@ Author(s): X(bool, ForceFullRepaintRendering, "experimental.rendering.forceFullRepaint", false) \ X(bool, SoftwareRendering, "experimental.rendering.software", false) \ X(bool, ForceVTInput, "experimental.input.forceVT", false) \ - X(bool, TrimBlockSelection, "trimBlockSelection", false) \ + X(bool, TrimBlockSelection, "trimBlockSelection", true) \ X(bool, DetectURLs, "experimental.detectURLs", true) \ X(bool, AlwaysShowTabs, "alwaysShowTabs", true) \ X(bool, ShowTitleInTitlebar, "showTerminalTitleInTitlebar", true) \ diff --git a/src/cascadia/TerminalSettingsModel/TerminalSettings.h b/src/cascadia/TerminalSettingsModel/TerminalSettings.h index 6409a2861fc..909ad8ab37a 100644 --- a/src/cascadia/TerminalSettingsModel/TerminalSettings.h +++ b/src/cascadia/TerminalSettingsModel/TerminalSettings.h @@ -89,7 +89,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::TerminalSettings, hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); INHERITABLE_SETTING(Model::TerminalSettings, bool, CopyOnSelect, false); INHERITABLE_SETTING(Model::TerminalSettings, bool, FocusFollowMouse, false); - INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, false); + INHERITABLE_SETTING(Model::TerminalSettings, bool, TrimBlockSelection, true); INHERITABLE_SETTING(Model::TerminalSettings, bool, DetectURLs, true); INHERITABLE_SETTING(Model::TerminalSettings, Windows::Foundation::IReference, TabColor, nullptr); diff --git a/src/cascadia/TerminalSettingsModel/defaults.json b/src/cascadia/TerminalSettingsModel/defaults.json index 89f78f630ff..7bff00a7f7b 100644 --- a/src/cascadia/TerminalSettingsModel/defaults.json +++ b/src/cascadia/TerminalSettingsModel/defaults.json @@ -11,7 +11,7 @@ // Selection "copyOnSelect": false, "copyFormatting": true, - "trimBlockSelection": false, + "trimBlockSelection": true, "trimPaste": true, "wordDelimiters": " /\\()\"'-.,:;<>~!@#$%^&*|+=[]{}~?\u2502", diff --git a/src/cascadia/inc/ControlProperties.h b/src/cascadia/inc/ControlProperties.h index 8243177ce11..171cbb7eb4b 100644 --- a/src/cascadia/inc/ControlProperties.h +++ b/src/cascadia/inc/ControlProperties.h @@ -41,7 +41,7 @@ X(bool, FocusFollowMouse, false) \ X(winrt::Windows::Foundation::IReference, TabColor, nullptr) \ X(winrt::Windows::Foundation::IReference, StartingTabColor, nullptr) \ - X(bool, TrimBlockSelection, false) \ + X(bool, TrimBlockSelection, true) \ X(bool, SuppressApplicationTitle) \ X(bool, ForceVTInput, false) \ X(winrt::hstring, StartingTitle) \ From 9fa4169d67f224bcf0577fdc551818ec7ad83b69 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Thu, 24 Mar 2022 18:56:11 +0100 Subject: [PATCH 73/75] Fix engine size not being changed on DPI changes (#12713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we would only call `SetWindowSize` and `TriggerRedrawAll` if the viewport size in characters changed. This commit removes the limitation. Since the if-condition limiting full redraws is now gone, this commit moves the responsibility of limiting the calls up the call chain. With `_refreshSizeUnderLock` now being a heavier function call than before, some surrounding code was thus refactored. ## PR Checklist * [x] Closes #11317 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed Test relevant for #11317: * Print text, filling the entire window * Move the window from a 150% scale to a 300% scale monitor * The application works as expected ✅ Regression tests: * Text zoom with Ctrl+Plus/Minus/0 works as before ✅ * Resizing a window works as before ✅ * No deadlocks, etc. during settings updates ✅ --- src/cascadia/TerminalControl/ControlCore.cpp | 156 +++++++------------ src/cascadia/TerminalControl/ControlCore.h | 4 +- 2 files changed, 54 insertions(+), 106 deletions(-) diff --git a/src/cascadia/TerminalControl/ControlCore.cpp b/src/cascadia/TerminalControl/ControlCore.cpp index 21a71077389..f473a817439 100644 --- a/src/cascadia/TerminalControl/ControlCore.cpp +++ b/src/cascadia/TerminalControl/ControlCore.cpp @@ -620,19 +620,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _runtimeUseAcrylic = true; } - // Initialize our font information. - const auto fontFace = _settings->FontFace(); - const short fontHeight = ::base::saturated_cast(_settings->FontSize()); - const auto fontWeight = _settings->FontWeight(); - // The font width doesn't terribly matter, we'll only be using the - // height to look it up - // The other params here also largely don't matter. - // The family is only used to determine if the font is truetype or - // not, but DX doesn't use that info at all. - // The Codepage is additionally not actually used by the DX engine at all. - _actualFont = { fontFace, 0, fontWeight.Weight, { 0, fontHeight }, CP_UTF8, false }; - _actualFontFaceName = { fontFace }; - _desiredFont = { _actualFont }; + const auto sizeChanged = _setFontSizeUnderLock(_settings->FontSize()); // Update the terminal core with its new Core settings _terminal->UpdateSettings(*_settings); @@ -651,11 +639,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation _updateAntiAliasingMode(); - // Refresh our font with the renderer - const auto actualFontOldSize = _actualFont.GetSize(); - _updateFont(); - const auto actualFontNewSize = _actualFont.GetSize(); - if (actualFontNewSize != actualFontOldSize) + if (sizeChanged) { _refreshSizeUnderLock(); } @@ -768,30 +752,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - Set the font size of the terminal control. // Arguments: // - fontSize: The size of the font. - void ControlCore::_setFontSize(int fontSize) + // Return Value: + // - Returns true if you need to call _refreshSizeUnderLock(). + bool ControlCore::_setFontSizeUnderLock(int fontSize) { - try - { - // Make sure we have a non-zero font size - const auto newSize = std::max(gsl::narrow_cast(fontSize), 1); - const auto fontFace = _settings->FontFace(); - const auto fontWeight = _settings->FontWeight(); - _actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false }; - _actualFontFaceName = { fontFace }; - _desiredFont = { _actualFont }; - - auto lock = _terminal->LockForWriting(); - - // Refresh our font with the renderer - _updateFont(); - - // Resize the terminal's BUFFER to match the new font size. This does - // NOT change the size of the window, because that can lead to more - // problems (like what happens when you change the font size while the - // window is maximized?) - _refreshSizeUnderLock(); - } - CATCH_LOG(); + // Make sure we have a non-zero font size + const auto newSize = std::max(gsl::narrow_cast(fontSize), 1); + const auto fontFace = _settings->FontFace(); + const auto fontWeight = _settings->FontWeight(); + _actualFont = { fontFace, 0, fontWeight.Weight, { 0, newSize }, CP_UTF8, false }; + _actualFontFaceName = { fontFace }; + _desiredFont = { _actualFont }; + + const auto before = _actualFont.GetSize(); + _updateFont(); + const auto after = _actualFont.GetSize(); + return before != after; } // Method Description: @@ -800,7 +776,12 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - none void ControlCore::ResetFontSize() { - _setFontSize(_settings->FontSize()); + const auto lock = _terminal->LockForWriting(); + + if (_setFontSizeUnderLock(_settings->FontSize())) + { + _refreshSizeUnderLock(); + } } // Method Description: @@ -809,69 +790,46 @@ namespace winrt::Microsoft::Terminal::Control::implementation // - fontSizeDelta: The amount to increase or decrease the font size by. void ControlCore::AdjustFontSize(int fontSizeDelta) { - const auto newSize = _desiredFont.GetEngineSize().Y + fontSizeDelta; - _setFontSize(newSize); + const auto lock = _terminal->LockForWriting(); + + if (_setFontSizeUnderLock(_desiredFont.GetEngineSize().Y + fontSizeDelta)) + { + _refreshSizeUnderLock(); + } } // Method Description: - // - Perform a resize for the current size of the swapchainpanel. If the - // font size changed, we'll need to resize the buffer to fit the existing - // swapchain size. This helper will call _doResizeUnderLock with the - // current size of the swapchain, accounting for scaling due to DPI. + // - Process a resize event that was initiated by the user. This can either + // be due to the user resizing the window (causing the swapchain to + // resize) or due to the DPI changing (causing us to need to resize the + // buffer to match) // - Note that a DPI change will also trigger a font size change, and will // call into here. // - The write lock should be held when calling this method, we might be - // changing the buffer size in _doResizeUnderLock. + // changing the buffer size in _refreshSizeUnderLock. // Arguments: // - // Return Value: // - void ControlCore::_refreshSizeUnderLock() { - const auto widthInPixels = _panelWidth * _compositionScale; - const auto heightInPixels = _panelHeight * _compositionScale; - - _doResizeUnderLock(widthInPixels, heightInPixels); - } - - // Method Description: - // - Process a resize event that was initiated by the user. This can either - // be due to the user resizing the window (causing the swapchain to - // resize) or due to the DPI changing (causing us to need to resize the - // buffer to match) - // Arguments: - // - newWidth: the new width of the swapchain, in pixels. - // - newHeight: the new height of the swapchain, in pixels. - void ControlCore::_doResizeUnderLock(const double newWidth, - const double newHeight) - { - SIZE size; - size.cx = static_cast(newWidth); - size.cy = static_cast(newHeight); + auto cx = gsl::narrow_cast(_panelWidth * _compositionScale); + auto cy = gsl::narrow_cast(_panelHeight * _compositionScale); // Don't actually resize so small that a single character wouldn't fit // in either dimension. The buffer really doesn't like being size 0. - if (size.cx < _actualFont.GetSize().X || size.cy < _actualFont.GetSize().Y) - { - return; - } + cx = std::max(cx, _actualFont.GetSize().X); + cy = std::max(cy, _actualFont.GetSize().Y); // Convert our new dimensions to characters - const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, - { static_cast(size.cx), static_cast(size.cy) }); + const auto viewInPixels = Viewport::FromDimensions({ 0, 0 }, { cx, cy }); const auto vp = _renderEngine->GetViewportInCharacters(viewInPixels); const auto currentVP = _terminal->GetViewport(); - // Don't actually resize if viewport dimensions didn't change - if (vp.Height() == currentVP.Height() && vp.Width() == currentVP.Width()) - { - return; - } - _terminal->ClearSelection(); // Tell the dx engine that our window is now the new size. - THROW_IF_FAILED(_renderEngine->SetWindowSize(size)); + THROW_IF_FAILED(_renderEngine->SetWindowSize({ cx, cy })); // Invalidate everything _renderer->TriggerRedrawAll(); @@ -888,15 +846,18 @@ namespace winrt::Microsoft::Terminal::Control::implementation void ControlCore::SizeChanged(const double width, const double height) { + // _refreshSizeUnderLock redraws the entire terminal. + // Don't call it if we don't have to. + if (_panelWidth == width && _panelHeight == height) + { + return; + } + _panelWidth = width; _panelHeight = height; auto lock = _terminal->LockForWriting(); - const auto currentEngineScale = _renderEngine->GetScaling(); - - auto scaledWidth = width * currentEngineScale; - auto scaledHeight = height * currentEngineScale; - _doResizeUnderLock(scaledWidth, scaledHeight); + _refreshSizeUnderLock(); } void ControlCore::ScaleChanged(const double scale) @@ -906,30 +867,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation return; } - const auto currentEngineScale = _renderEngine->GetScaling(); - // If we're getting a notification to change to the DPI we already - // have, then we're probably just beginning the DPI change. Since - // we'll get _another_ event with the real DPI, do nothing here for - // now. We'll also skip the next resize in _swapChainSizeChanged. - const bool dpiWasUnchanged = currentEngineScale == scale; - if (dpiWasUnchanged) + // _refreshSizeUnderLock redraws the entire terminal. + // Don't call it if we don't have to. + if (_compositionScale == scale) { return; } - const auto actualFontOldSize = _actualFont.GetSize(); - - auto lock = _terminal->LockForWriting(); _compositionScale = scale; + auto lock = _terminal->LockForWriting(); // _updateFont relies on the new _compositionScale set above _updateFont(); - - const auto actualFontNewSize = _actualFont.GetSize(); - if (actualFontNewSize != actualFontOldSize) - { - _refreshSizeUnderLock(); - } + _refreshSizeUnderLock(); } void ControlCore::SetSelectionAnchor(til::point const& position) diff --git a/src/cascadia/TerminalControl/ControlCore.h b/src/cascadia/TerminalControl/ControlCore.h index 2a86c2585cd..a8cd204f1ef 100644 --- a/src/cascadia/TerminalControl/ControlCore.h +++ b/src/cascadia/TerminalControl/ControlCore.h @@ -242,11 +242,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation winrt::fire_and_forget _asyncCloseConnection(); - void _setFontSize(int fontSize); + bool _setFontSizeUnderLock(int fontSize); void _updateFont(const bool initialUpdate = false); void _refreshSizeUnderLock(); - void _doResizeUnderLock(const double newWidth, - const double newHeight); void _sendInputToConnection(std::wstring_view wstr); From f5882236dcb8693a0b14c70e4bbe73e6401d844d Mon Sep 17 00:00:00 2001 From: Dan Mezhiborsky Date: Thu, 24 Mar 2022 17:08:57 -0400 Subject: [PATCH 74/75] Fix transparency/opacity inconsistency in help text (#12592) (#12727) In two instances, the help text for the settings UI refers to _transparency_ when we're really talking about _opacity._ This PR changes those occurences to more accurately reflect the setting being described. ## PR Checklist * [x] Closes #12592 --- .../TerminalSettingsEditor/Resources/en-US/Resources.resw | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 3a22ea0bcf4..fcafb0bcf4f 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -528,7 +528,7 @@ Header for a control to determine the level of opacity for the background of the control. The user can choose to make the background of the app more or less opaque. - Sets the transparency of the window. + Sets the opacity of the window. A description for what the "opacity" setting does. Presented near "Profile_Opacity.Header". @@ -648,7 +648,7 @@ Header for a control to choose the opacity of the image presented on the background of the app. - Sets the transparency of the background image. + Sets the opacity of the background image. A description for what the "background image opacity" setting does. Presented near "Profile_BackgroundImageOpacity". From 036cc284bd909a2a4ab836df3c662c5570a0c907 Mon Sep 17 00:00:00 2001 From: David Machaj <46852402+dmachaj@users.noreply.github.com> Date: Mon, 28 Mar 2022 11:31:36 -0700 Subject: [PATCH 75/75] Terminal would benefit from having a single canonical version number for each of its NuGet dependencies (#12707) These changes are purely a refactoring of the build files. There should be no difference to the compiled result or runtime behavior. Currently there are packages.config files in lots of directories, with those same projects referencing props/targets from packages/ with a version string in the path. This is frustrating because version changes or new dependencies require updating lots and lots of build files identically. There is also the possibility of error where locations are missed. With these changes there is a single canonical nuget configuration that takes effect for all of OpenConsole.sln. Updating version numbers should be limited to a single set of global files. The changes were done incrementally but the result is basically that dep\nuget\packages.config serves as the global NuGet dependency list. A pair of common build files (common.nugetversions.props and common.nugetversions.targets) were added to contain the various imports and error checks. There is also a special build target to ensure that the restore happens before builds even though a given directory doesn't have a packages.config for Visual Studio to observe. These new *.nugetversions.* files are imported in pretty much every vcxproj/csproj in the solution in the appropriate place to satisfy the need for packages. There are opt-in configuration values (e.g. `TerminalCppWinrt=true`) that must be set to opt into a given dependency. Adding a new dependency is just a matter of adding a new opt-in value. The ordering of include does matter, which was a difficult challenge to realize and address. There was also a preexisting issue in 3 test projects where cppwinrt.props was included but not cppwinrt.targets. By consolidating things globally that "error" was fixed, but broke the build in a way that was very confusing. Those projects don't need the cppwinrt targets so they were opted out of the cppwinrt build files entirely to fix the breaks and get back to previous behavior. There are two notable exceptions to this canonical versioning. The first is that there are dueling XAML 2.7 dependencies. I avoided that by leaving those as per-project package.config entries. The second is that any projects outside of the .sln (such as the Island samples) were not touched. ## Validation Steps Performed The primary validation is that the solution builds without errors. That is what I'm seeing (x64|Debug). I also ran `git clean -fdx` from the root of the repo to wipe it to clean and then opened the solution and was able to build successfully. The project F5 deploys and looks fine to me with just a cursory glance. The tests also largely pass (7418 pass, 188 fail, 14 other) which is as good or better than the baseline I established from a clean clone. Closes #12708 --- .github/actions/spelling/expect/expect.txt | 2 + OpenConsole.sln | 4 +- build/pipelines/release.yml | 8 ++ .../templates/build-console-audit-job.yml | 13 ++- .../build-console-compliance-job.yml | 8 ++ .../templates/build-console-steps.yml | 9 ++ dep/nuget/packages.config | 21 ++++ .../TextBuffer.Unit.Tests.vcxproj | 2 + .../SettingsModel.LocalTests.vcxproj | 5 +- .../FilteredCommandTests.cpp | 2 +- .../LocalTests_TerminalApp/TabTests.cpp | 2 +- .../TerminalApp.LocalTests.vcxproj | 8 +- .../TestHostApp/TestHostApp.vcxproj | 4 +- .../Microsoft.Terminal.RemotingLib.vcxproj | 8 +- .../dll/Microsoft.Terminal.Remoting.vcxproj | 7 ++ src/cascadia/Remoting/packages.config | 5 - .../WindowsTerminalShellExt.vcxproj | 6 +- src/cascadia/ShellExtension/packages.config | 4 - .../TerminalApp/TerminalAppLib.vcxproj | 12 ++- .../TerminalApp/dll/TerminalApp.vcxproj | 12 ++- src/cascadia/TerminalApp/packages.config | 3 - .../TerminalAzBridge/TerminalAzBridge.vcxproj | 9 +- src/cascadia/TerminalAzBridge/packages.config | 4 - .../TerminalConnection.vcxproj | 16 +-- .../TerminalConnection/packages.config | 6 -- .../TerminalControlLib.vcxproj | 10 +- .../dll/TerminalControl.vcxproj | 9 ++ src/cascadia/TerminalControl/packages.config | 5 - .../TerminalCore/lib/terminalcore-lib.vcxproj | 8 ++ src/cascadia/TerminalCore/packages.config | 4 - ...Microsoft.Terminal.Settings.Editor.vcxproj | 10 +- .../TerminalSettingsEditor/packages.config | 2 - ...crosoft.Terminal.Settings.ModelLib.vcxproj | 10 +- .../Microsoft.Terminal.Settings.Model.vcxproj | 7 ++ .../TerminalSettingsModel/packages.config | 6 -- .../Control.UnitTests.vcxproj | 7 ++ .../Remoting.UnitTests.vcxproj | 4 +- .../UnitTests_TerminalCore/UnitTests.vcxproj | 8 ++ src/cascadia/WinRTUtils/WinRTUtils.vcxproj | 9 +- src/cascadia/WinRTUtils/packages.config | 4 - .../WindowsTerminal/WindowsTerminal.vcxproj | 18 ++-- src/cascadia/WindowsTerminal/packages.config | 5 - .../WindowsTerminal.UIA.Tests.csproj | 33 +++---- .../WindowsTerminal_UIATests/packages.config | 9 -- .../ut_app/TerminalApp.UnitTests.vcxproj | 2 + src/common.build.post.props | 2 - src/common.build.pre.props | 1 - src/common.build.tests.props | 10 +- src/common.nugetversions.props | 42 ++++++++ src/common.nugetversions.targets | 97 +++++++++++++++++++ src/cppwinrt.build.post.props | 11 --- src/cppwinrt.build.pre.props | 2 - src/host/exe/packages.config | 4 - src/host/ft_host/Host.FeatureTests.vcxproj | 2 + src/host/ft_uia/Host.Tests.UIA.csproj | 31 +++--- src/host/ft_uia/packages.config | 9 -- src/host/ut_host/Host.UnitTests.vcxproj | 2 + src/host/ut_lib/host.unittest.vcxproj | 4 +- .../Interactivity.Win32.UnitTests.vcxproj | 2 + src/renderer/dx/ut_dx/Dx.Unit.Tests.vcxproj | 4 +- src/renderer/vt/ut_lib/vt.unittest.vcxproj | 2 + .../ut_adapter/Adapter.UnitTests.vcxproj | 2 + .../parser/ft_fuzzer/VTCommandFuzzer.vcxproj | 2 + .../parser/ft_fuzzwrapper/FuzzWrapper.vcxproj | 4 +- .../parser/ut_parser/Parser.UnitTests.vcxproj | 5 +- .../Parser.UnitTests.vcxproj.filters | 3 - src/terminal/parser/ut_parser/packages.config | 4 - src/til/ut_til/til.unit.tests.vcxproj | 2 + .../MonarchPeasantSample.vcxproj | 12 ++- .../MonarchPeasantSample/packages.config | 4 - src/types/ut_types/Types.Unit.Tests.vcxproj | 2 + .../ft_pty/winconpty.FeatureTests.vcxproj | 2 + tools/OpenConsole.psm1 | 1 + tools/razzle.cmd | 1 + 74 files changed, 423 insertions(+), 196 deletions(-) create mode 100644 dep/nuget/packages.config delete mode 100644 src/cascadia/Remoting/packages.config delete mode 100644 src/cascadia/ShellExtension/packages.config delete mode 100644 src/cascadia/TerminalAzBridge/packages.config delete mode 100644 src/cascadia/TerminalConnection/packages.config delete mode 100644 src/cascadia/TerminalControl/packages.config delete mode 100644 src/cascadia/TerminalCore/packages.config delete mode 100644 src/cascadia/TerminalSettingsModel/packages.config delete mode 100644 src/cascadia/WinRTUtils/packages.config delete mode 100644 src/cascadia/WindowsTerminal_UIATests/packages.config create mode 100644 src/common.nugetversions.props create mode 100644 src/common.nugetversions.targets delete mode 100644 src/host/exe/packages.config delete mode 100644 src/host/ft_uia/packages.config delete mode 100644 src/terminal/parser/ut_parser/packages.config delete mode 100644 src/tools/MonarchPeasantSample/packages.config diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 6b8bd612920..652be19189b 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -52,6 +52,7 @@ apimswincoresynchl apiset APPBARDATA appconsult +appcontainer APPICON appium applet @@ -1626,6 +1627,7 @@ NTVDM ntverp NTWIN nuget +nugetversions nullability nullness nullonfailure diff --git a/OpenConsole.sln b/OpenConsole.sln index 45613781537..f53e2cc56b3 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29001.49 @@ -290,9 +289,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common Props", "Common Prop src\common.build.post.props = src\common.build.post.props src\common.build.pre.props = src\common.build.pre.props src\common.build.tests.props = src\common.build.tests.props + src\common.nugetversions.props = src\common.nugetversions.props + src\common.nugetversions.targets = src\common.nugetversions.targets common.openconsole.props = common.openconsole.props src\cppwinrt.build.post.props = src\cppwinrt.build.post.props src\cppwinrt.build.pre.props = src\cppwinrt.build.pre.props + dep\nuget\packages.config = dep\nuget\packages.config src\wap-common.build.post.props = src\wap-common.build.post.props src\wap-common.build.pre.props = src\wap-common.build.pre.props EndProjectSection diff --git a/build/pipelines/release.yml b/build/pipelines/release.yml index bbe1d0570b7..cd2cc038c1a 100644 --- a/build/pipelines/release.yml +++ b/build/pipelines/release.yml @@ -122,6 +122,14 @@ jobs: selectOrConfig: config nugetConfigPath: NuGet.Config arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory) + - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for global nuget + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: dep/nuget/packages.config + restoreDirectory: '$(Build.SourcesDirectory)\packages' # Pull the Windows SDK for the developer tools like the debuggers so we can index sources later - template: .\templates\install-winsdk-steps.yml - task: UniversalPackages@0 diff --git a/build/pipelines/templates/build-console-audit-job.yml b/build/pipelines/templates/build-console-audit-job.yml index f84c5bca520..591c2a7b12e 100644 --- a/build/pipelines/templates/build-console-audit-job.yml +++ b/build/pipelines/templates/build-console-audit-job.yml @@ -8,7 +8,7 @@ jobs: variables: BuildConfiguration: AuditMode BuildPlatform: ${{ parameters.platform }} - pool: + pool: ${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: name: WinDevPoolOSS-L ${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}: @@ -48,6 +48,17 @@ jobs: restoreSolution: OpenConsole.sln restoreDirectory: '$(Build.SourcesDirectory)\packages' + # In the Microsoft Azure DevOps tenant, NuGetCommand is ambiguous. + # This should be `task: NuGetCommand@2` + - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for global nuget + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: dep/nuget/packages.config + restoreDirectory: '$(Build.SourcesDirectory)\packages' + - task: VSBuild@1 displayName: 'Build solution **\OpenConsole.sln' inputs: diff --git a/build/pipelines/templates/build-console-compliance-job.yml b/build/pipelines/templates/build-console-compliance-job.yml index eafaf6b8062..3ae3fb197ee 100644 --- a/build/pipelines/templates/build-console-compliance-job.yml +++ b/build/pipelines/templates/build-console-compliance-job.yml @@ -52,6 +52,14 @@ jobs: selectOrConfig: config nugetConfigPath: NuGet.Config arguments: restore OpenConsole.sln -SolutionDirectory $(Build.SourcesDirectory) + - task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for global nuget + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: dep/nuget/packages.config + restoreDirectory: '$(Build.SourcesDirectory)\packages' - task: UniversalPackages@0 displayName: Download terminal-internal Universal Package inputs: diff --git a/build/pipelines/templates/build-console-steps.yml b/build/pipelines/templates/build-console-steps.yml index dd609988604..65e1d146aa3 100644 --- a/build/pipelines/templates/build-console-steps.yml +++ b/build/pipelines/templates/build-console-steps.yml @@ -34,6 +34,15 @@ steps: restoreSolution: OpenConsole.sln restoreDirectory: '$(Build.SourcesDirectory)\packages' +- task: 333b11bd-d341-40d9-afcf-b32d5ce6f23b@2 + displayName: Restore NuGet packages for global nuget + inputs: + command: restore + feedsToUse: config + configPath: NuGet.config + restoreSolution: dep/nuget/packages.config + restoreDirectory: '$(Build.SourcesDirectory)\packages' + # The environment variable VCToolsInstallDir isn't defined on lab machines, so we need to retrieve it ourselves. - script: | "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -Latest -requires Microsoft.Component.MSBuild -property InstallationPath > %TEMP%\vsinstalldir.txt diff --git a/dep/nuget/packages.config b/dep/nuget/packages.config new file mode 100644 index 00000000000..fb81204984e --- /dev/null +++ b/dep/nuget/packages.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj b/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj index dab7c2bf390..ca276b2b13f 100644 --- a/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj +++ b/src/buffer/out/ut_textbuffer/TextBuffer.Unit.Tests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -40,4 +41,5 @@ + diff --git a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj index 2ad3e5508e3..47214270725 100644 --- a/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_SettingsModel/SettingsModel.LocalTests.vcxproj @@ -19,9 +19,11 @@ SettingsModel.LocalTests DynamicLibrary true + + @@ -94,6 +96,7 @@ + @@ -102,7 +105,7 @@ <_MUXBinRoot>"$(OpenConsoleDir)packages\Microsoft.UI.Xaml.$(TerminalMUXVersion)\runtimes\win10-$(Native-Platform)\native\" - + diff --git a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp index 4d0f6603103..7a92e2c023c 100644 --- a/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/FilteredCommandTests.cpp @@ -4,7 +4,7 @@ #include "pch.h" #include "../TerminalApp/CommandLinePaletteItem.h" #include "../TerminalApp/CommandPalette.h" -#include "../CppWinrtTailored.h" +#include "CppWinrtTailored.h" using namespace Microsoft::Console; using namespace WEX::Logging; diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index 424f0ddcdc0..fcb8f214de0 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -9,7 +9,7 @@ #include "../TerminalApp/ShortcutActionDispatch.h" #include "../TerminalApp/TerminalTab.h" #include "../TerminalApp/CommandPalette.h" -#include "../CppWinrtTailored.h" +#include "CppWinrtTailored.h" using namespace Microsoft::Console; using namespace TerminalApp; diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj index d44431a5f89..b57c9beb56a 100644 --- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj @@ -21,7 +21,13 @@ true + + + true + + + @@ -87,6 +93,7 @@ + @@ -97,6 +104,5 @@ - diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj index 896547f5ffd..81a175840f5 100644 --- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj +++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj @@ -29,6 +29,7 @@ + @@ -127,6 +128,7 @@ + diff --git a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj index 517c2f56fcb..638229df0d4 100644 --- a/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj +++ b/src/cascadia/Remoting/Microsoft.Terminal.RemotingLib.vcxproj @@ -9,8 +9,10 @@ StaticLibrary Console true + true + @@ -107,7 +109,6 @@ - @@ -139,5 +140,8 @@ + + + - \ No newline at end of file + diff --git a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj index a2eaba668e4..d0b9179f433 100644 --- a/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj +++ b/src/cascadia/Remoting/dll/Microsoft.Terminal.Remoting.vcxproj @@ -12,7 +12,11 @@ true true + + true + + @@ -71,4 +75,7 @@ + + + diff --git a/src/cascadia/Remoting/packages.config b/src/cascadia/Remoting/packages.config deleted file mode 100644 index 64e09e4c8e5..00000000000 --- a/src/cascadia/Remoting/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj index ad10da07a77..2f6fce0cb92 100644 --- a/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj +++ b/src/cascadia/ShellExtension/WindowsTerminalShellExt.vcxproj @@ -11,7 +11,11 @@ false + + true + + @@ -30,7 +34,6 @@ - @@ -50,6 +53,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}. - - @@ -88,15 +92,12 @@ - - 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}. - @@ -111,4 +112,7 @@ + + + diff --git a/src/cascadia/TerminalApp/packages.config b/src/cascadia/TerminalApp/packages.config index e039fa4ae47..e91b14a32bf 100644 --- a/src/cascadia/TerminalApp/packages.config +++ b/src/cascadia/TerminalApp/packages.config @@ -1,7 +1,4 @@ - - - diff --git a/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj b/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj index cf465a2a340..0fddd2db19a 100644 --- a/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj +++ b/src/cascadia/TerminalAzBridge/TerminalAzBridge.vcxproj @@ -13,7 +13,12 @@ Windows + + true + + + @@ -33,9 +38,6 @@ - - - @@ -57,6 +59,7 @@ + diff --git a/src/cascadia/TerminalAzBridge/packages.config b/src/cascadia/TerminalAzBridge/packages.config deleted file mode 100644 index 6ec12ca23d1..00000000000 --- a/src/cascadia/TerminalAzBridge/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj index f9ab61577f7..0fed7d60aa1 100644 --- a/src/cascadia/TerminalConnection/TerminalConnection.vcxproj +++ b/src/cascadia/TerminalConnection/TerminalConnection.vcxproj @@ -9,7 +9,12 @@ true true + + true + true + + @@ -60,7 +65,6 @@ Designer - @@ -81,13 +85,6 @@ - - - - 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}. - - - $(IntDir)..\OpenConsoleProxy;%(AdditionalIncludeDirectories) @@ -98,6 +95,9 @@ + + + @@ -140,7 +146,6 @@ - @@ -175,5 +180,8 @@ + + + diff --git a/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj b/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj index 061b74c23f2..7783493136c 100644 --- a/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj +++ b/src/cascadia/TerminalControl/dll/TerminalControl.vcxproj @@ -22,7 +22,13 @@ --> 3 + + + true + + + @@ -98,4 +104,7 @@ + + + diff --git a/src/cascadia/TerminalControl/packages.config b/src/cascadia/TerminalControl/packages.config deleted file mode 100644 index 64e09e4c8e5..00000000000 --- a/src/cascadia/TerminalControl/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj b/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj index 2f750978bba..7488acf941f 100644 --- a/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj +++ b/src/cascadia/TerminalCore/lib/terminalcore-lib.vcxproj @@ -12,12 +12,17 @@ true + + true + + "$(SolutionDir)\src\cascadia\TerminalCore\Generated Files\winrt";$(SolutionDir)src\cascadia\TerminalCore;$(CAExcludePath) + @@ -65,4 +70,7 @@ + + + diff --git a/src/cascadia/TerminalCore/packages.config b/src/cascadia/TerminalCore/packages.config deleted file mode 100644 index 6ec12ca23d1..00000000000 --- a/src/cascadia/TerminalCore/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj index 7c5f17695a9..8953d1e9b2a 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj @@ -32,7 +32,11 @@ 4 nested + + true + + @@ -345,6 +349,10 @@ + + + + @@ -353,4 +361,4 @@ - \ No newline at end of file + diff --git a/src/cascadia/TerminalSettingsEditor/packages.config b/src/cascadia/TerminalSettingsEditor/packages.config index 9b938bd8686..03d6e483c32 100644 --- a/src/cascadia/TerminalSettingsEditor/packages.config +++ b/src/cascadia/TerminalSettingsEditor/packages.config @@ -1,6 +1,4 @@ - - diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj index bf0e68e2662..c37026831c5 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj @@ -10,7 +10,12 @@ Console true + + true + true + + @@ -184,7 +189,6 @@ - @@ -268,13 +272,14 @@ + + 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}. - @@ -125,4 +129,7 @@ + + + diff --git a/src/cascadia/TerminalSettingsModel/packages.config b/src/cascadia/TerminalSettingsModel/packages.config deleted file mode 100644 index 2ecd2492264..00000000000 --- a/src/cascadia/TerminalSettingsModel/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/cascadia/UnitTests_Control/Control.UnitTests.vcxproj b/src/cascadia/UnitTests_Control/Control.UnitTests.vcxproj index 08cc065238c..b553e3f9ccc 100644 --- a/src/cascadia/UnitTests_Control/Control.UnitTests.vcxproj +++ b/src/cascadia/UnitTests_Control/Control.UnitTests.vcxproj @@ -10,7 +10,12 @@ true + + true + + + @@ -85,5 +90,7 @@ + + diff --git a/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj index 424e6f9b4b7..171b3eed2d5 100644 --- a/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj +++ b/src/cascadia/UnitTests_Remoting/Remoting.UnitTests.vcxproj @@ -19,9 +19,11 @@ Remoting.Unit.Tests DynamicLibrary true + + @@ -71,6 +73,6 @@ - + diff --git a/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj b/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj index 0bcdc582a67..42893d18fa4 100644 --- a/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj +++ b/src/cascadia/UnitTests_TerminalCore/UnitTests.vcxproj @@ -9,7 +9,11 @@ DynamicLibrary false + + true + + @@ -98,4 +102,8 @@ + + + + diff --git a/src/cascadia/WinRTUtils/WinRTUtils.vcxproj b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj index 0c6d74a8ac8..ac766d765fe 100644 --- a/src/cascadia/WinRTUtils/WinRTUtils.vcxproj +++ b/src/cascadia/WinRTUtils/WinRTUtils.vcxproj @@ -10,7 +10,11 @@ Console true + + true + + @@ -33,10 +37,6 @@ - - - - @@ -50,4 +50,5 @@ + diff --git a/src/cascadia/WinRTUtils/packages.config b/src/cascadia/WinRTUtils/packages.config deleted file mode 100644 index 6ec12ca23d1..00000000000 --- a/src/cascadia/WinRTUtils/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj index 47655445e79..e4e6186e799 100644 --- a/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj +++ b/src/cascadia/WindowsTerminal/WindowsTerminal.vcxproj @@ -1,6 +1,5 @@ - {CA5CAD1A-1754-4A9D-93D7-857A9D17CB1B} @@ -17,7 +16,15 @@ true + + true + true + true + true + + + @@ -135,17 +142,11 @@ - - 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/common.build.post.props b/src/common.build.post.props index 399cec3bf1e..4e2a1f7d086 100644 --- a/src/common.build.post.props +++ b/src/common.build.post.props @@ -33,8 +33,6 @@ - - diff --git a/src/common.build.pre.props b/src/common.build.pre.props index 4d494f911ab..b8178508228 100644 --- a/src/common.build.pre.props +++ b/src/common.build.pre.props @@ -181,7 +181,6 @@ - diff --git a/src/common.build.tests.props b/src/common.build.tests.props index 3e43e16fe81..0c93e53ed75 100644 --- a/src/common.build.tests.props +++ b/src/common.build.tests.props @@ -5,11 +5,7 @@ INLINE_TEST_METHOD_MARKUP;UNIT_TESTING;%(PreprocessorDefinitions) - - - - 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}. - - - + + true + diff --git a/src/common.nugetversions.props b/src/common.nugetversions.props new file mode 100644 index 00000000000..170b9062dcb --- /dev/null +++ b/src/common.nugetversions.props @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + $(MSBuildThisFileDirectory)..\packages\Microsoft.Taef.10.60.210621002 + + + + + $(MSBuildThisFileDirectory)..\packages\Appium.WebDriver.3.0.0.2 + + + + + $(MSBuildThisFileDirectory)..\packages\Castle.Core.4.1.1 + + + + + $(MSBuildThisFileDirectory)..\packages\Newtonsoft.Json.12.0.3 + + + + + $(MSBuildThisFileDirectory)..\packages\Selenium.WebDriver.3.5.0 + + + + + $(MSBuildThisFileDirectory)..\packages\Selenium.Support.3.5.0 + + + diff --git a/src/common.nugetversions.targets b/src/common.nugetversions.targets new file mode 100644 index 00000000000..025d7418948 --- /dev/null +++ b/src/common.nugetversions.targets @@ -0,0 +1,97 @@ + + + + + + + + + $(SolutionDir)dep\nuget\nuget.exe + $(SolutionDir)dep\nuget\packages.config + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/cppwinrt.build.post.props b/src/cppwinrt.build.post.props index 72d925f0b13..68fc7ec2616 100644 --- a/src/cppwinrt.build.post.props +++ b/src/cppwinrt.build.post.props @@ -1,15 +1,4 @@ - - - - - - - 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/cppwinrt.build.pre.props b/src/cppwinrt.build.pre.props index 263da4fd9bf..30a659e33c4 100644 --- a/src/cppwinrt.build.pre.props +++ b/src/cppwinrt.build.pre.props @@ -8,8 +8,6 @@ - - AnyValueHereWillDisableTheOptOut true diff --git a/src/host/exe/packages.config b/src/host/exe/packages.config deleted file mode 100644 index 9e477b65215..00000000000 --- a/src/host/exe/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/host/ft_host/Host.FeatureTests.vcxproj b/src/host/ft_host/Host.FeatureTests.vcxproj index 1538e9b5455..6f782a5404e 100644 --- a/src/host/ft_host/Host.FeatureTests.vcxproj +++ b/src/host/ft_host/Host.FeatureTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -60,4 +61,5 @@ + diff --git a/src/host/ft_uia/Host.Tests.UIA.csproj b/src/host/ft_uia/Host.Tests.UIA.csproj index 1f71e6a4919..675ddd7086a 100644 --- a/src/host/ft_uia/Host.Tests.UIA.csproj +++ b/src/host/ft_uia/Host.Tests.UIA.csproj @@ -21,6 +21,10 @@ + + true + + ARM64 @@ -43,35 +47,35 @@ - ..\..\..\packages\Appium.WebDriver.3.0.0.2\lib\net45\appium-dotnet-driver.dll + $(AppiumWebDriverPathRoot)\lib\net45\appium-dotnet-driver.dll - ..\..\..\packages\Castle.Core.4.1.1\lib\net45\Castle.Core.dll + $(CastleCorePathRoot)\lib\net45\Castle.Core.dll - ..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + $(NewtonsoftJSONPathRoot)\lib\net45\Newtonsoft.Json.dll - ..\..\..\packages\Microsoft.Taef.10.60.210621002\lib\net45\TE.Managed.dll + $(TAEFPackagePathRoot)\lib\net45\TE.Managed.dll - ..\..\..\packages\Microsoft.Taef.10.60.210621002\lib\net45\TE.Model.Managed.dll + $(TAEFPackagePathRoot)\lib\net45\TE.Model.Managed.dll - ..\..\..\packages\Selenium.WebDriver.3.5.0\lib\net40\WebDriver.dll + $(SeleniumWebDriverPathRoot)\lib\net40\WebDriver.dll - ..\..\..\packages\Selenium.Support.3.5.0\lib\net40\WebDriver.Support.dll + $(SeleniumSupportPathRoot)\lib\net40\WebDriver.Support.dll - ..\..\..\packages\Microsoft.Taef.10.60.210621002\lib\net45\Wex.Common.Managed.dll + $(TAEFPackagePathRoot)\lib\net45\Wex.Common.Managed.dll - ..\..\..\packages\Microsoft.Taef.10.60.210621002\lib\net45\Wex.Logger.Interop.dll + $(TAEFPackagePathRoot)\lib\net45\Wex.Logger.Interop.dll @@ -121,7 +125,6 @@ - @@ -148,11 +151,5 @@ copy "$(SolutionDir)\dep\WinAppDriver\*" "$(OutDir)\" - - - - 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/host/ft_uia/packages.config b/src/host/ft_uia/packages.config deleted file mode 100644 index 0579ab06c6a..00000000000 --- a/src/host/ft_uia/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/host/ut_host/Host.UnitTests.vcxproj b/src/host/ut_host/Host.UnitTests.vcxproj index 6b5bedf1b11..633c990e6fa 100644 --- a/src/host/ut_host/Host.UnitTests.vcxproj +++ b/src/host/ut_host/Host.UnitTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -112,4 +113,5 @@ + diff --git a/src/host/ut_lib/host.unittest.vcxproj b/src/host/ut_lib/host.unittest.vcxproj index 75821ebc8a8..3faad5afefe 100644 --- a/src/host/ut_lib/host.unittest.vcxproj +++ b/src/host/ut_lib/host.unittest.vcxproj @@ -6,9 +6,10 @@ hostlib.unittest Host.unittest ConhostV2Lib.unittest - StaticLibrary + StaticLibrary + @@ -19,4 +20,5 @@ + diff --git a/src/interactivity/win32/ut_interactivity_win32/Interactivity.Win32.UnitTests.vcxproj b/src/interactivity/win32/ut_interactivity_win32/Interactivity.Win32.UnitTests.vcxproj index fa2fa30b086..5a03a6df2d3 100644 --- a/src/interactivity/win32/ut_interactivity_win32/Interactivity.Win32.UnitTests.vcxproj +++ b/src/interactivity/win32/ut_interactivity_win32/Interactivity.Win32.UnitTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -79,4 +80,5 @@ + diff --git a/src/renderer/dx/ut_dx/Dx.Unit.Tests.vcxproj b/src/renderer/dx/ut_dx/Dx.Unit.Tests.vcxproj index 78b8de06872..4c6abb8075e 100644 --- a/src/renderer/dx/ut_dx/Dx.Unit.Tests.vcxproj +++ b/src/renderer/dx/ut_dx/Dx.Unit.Tests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -37,4 +38,5 @@ - \ No newline at end of file + + diff --git a/src/renderer/vt/ut_lib/vt.unittest.vcxproj b/src/renderer/vt/ut_lib/vt.unittest.vcxproj index 2c146516195..9f33a67e60d 100644 --- a/src/renderer/vt/ut_lib/vt.unittest.vcxproj +++ b/src/renderer/vt/ut_lib/vt.unittest.vcxproj @@ -9,9 +9,11 @@ StaticLibrary + + diff --git a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj index b1ce6a9d73e..8ee87c5ba50 100644 --- a/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj +++ b/src/terminal/adapter/ut_adapter/Adapter.UnitTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -45,4 +46,5 @@ + diff --git a/src/terminal/parser/ft_fuzzer/VTCommandFuzzer.vcxproj b/src/terminal/parser/ft_fuzzer/VTCommandFuzzer.vcxproj index 906822e1e2d..587d6ed1d1e 100644 --- a/src/terminal/parser/ft_fuzzer/VTCommandFuzzer.vcxproj +++ b/src/terminal/parser/ft_fuzzer/VTCommandFuzzer.vcxproj @@ -9,6 +9,7 @@ Application + Create @@ -34,4 +35,5 @@ + diff --git a/src/terminal/parser/ft_fuzzwrapper/FuzzWrapper.vcxproj b/src/terminal/parser/ft_fuzzwrapper/FuzzWrapper.vcxproj index f9b51f59f2f..98b4a98515b 100644 --- a/src/terminal/parser/ft_fuzzwrapper/FuzzWrapper.vcxproj +++ b/src/terminal/parser/ft_fuzzwrapper/FuzzWrapper.vcxproj @@ -9,6 +9,7 @@ Application + Create @@ -39,4 +40,5 @@ - \ No newline at end of file + + diff --git a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj index 549eb4170ff..6ddbcdd116b 100644 --- a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj +++ b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -42,10 +43,8 @@ {06ec74cb-9a12-429c-b551-8562ec964846} - - - + diff --git a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters index 888240512c2..61ed48e9af8 100644 --- a/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters +++ b/src/terminal/parser/ut_parser/Parser.UnitTests.vcxproj.filters @@ -39,7 +39,4 @@ - - - \ No newline at end of file diff --git a/src/terminal/parser/ut_parser/packages.config b/src/terminal/parser/ut_parser/packages.config deleted file mode 100644 index bd5fb454237..00000000000 --- a/src/terminal/parser/ut_parser/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/til/ut_til/til.unit.tests.vcxproj b/src/til/ut_til/til.unit.tests.vcxproj index 48f9a8d3dea..ac5fa9bebb4 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj +++ b/src/til/ut_til/til.unit.tests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + Create @@ -44,4 +45,5 @@ + diff --git a/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj b/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj index 0daaf4a3d0e..1e4815a9cfe 100644 --- a/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj +++ b/src/tools/MonarchPeasantSample/MonarchPeasantSample.vcxproj @@ -13,7 +13,12 @@ Windows + + true + + + @@ -54,10 +59,6 @@ - - - - @@ -87,5 +88,8 @@ + + + diff --git a/src/tools/MonarchPeasantSample/packages.config b/src/tools/MonarchPeasantSample/packages.config deleted file mode 100644 index 21e62883af7..00000000000 --- a/src/tools/MonarchPeasantSample/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/types/ut_types/Types.Unit.Tests.vcxproj b/src/types/ut_types/Types.Unit.Tests.vcxproj index 3f5087de763..dfd180e294e 100644 --- a/src/types/ut_types/Types.Unit.Tests.vcxproj +++ b/src/types/ut_types/Types.Unit.Tests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -32,4 +33,5 @@ + diff --git a/src/winconpty/ft_pty/winconpty.FeatureTests.vcxproj b/src/winconpty/ft_pty/winconpty.FeatureTests.vcxproj index 438f6014dae..503ff27408b 100644 --- a/src/winconpty/ft_pty/winconpty.FeatureTests.vcxproj +++ b/src/winconpty/ft_pty/winconpty.FeatureTests.vcxproj @@ -9,6 +9,7 @@ DynamicLibrary + @@ -31,6 +32,7 @@ + $(OutDir)\conptylib.lib;%(AdditionalDependencies) diff --git a/tools/OpenConsole.psm1 b/tools/OpenConsole.psm1 index bad0afb0d95..fd0ededc112 100644 --- a/tools/OpenConsole.psm1 +++ b/tools/OpenConsole.psm1 @@ -261,6 +261,7 @@ function Invoke-OpenConsoleBuild() { $root = Find-OpenConsoleRoot & "$root\dep\nuget\nuget.exe" restore "$root\OpenConsole.sln" + & "$root\dep\nuget\nuget.exe" restore "$root\dep\nuget\packages.config" msbuild.exe "$root\OpenConsole.sln" @args } diff --git a/tools/razzle.cmd b/tools/razzle.cmd index 456e271d1b2..f5b567d77f0 100644 --- a/tools/razzle.cmd +++ b/tools/razzle.cmd @@ -22,6 +22,7 @@ set PATH=%PATH%%OPENCON%\dep\nuget; rem Run nuget restore so you can use vswhere nuget restore %OPENCON%\OpenConsole.sln -Verbosity quiet +nuget restore %OPENCON%\dep\nuget\packages.config -Verbosity quiet :FIND_MSBUILD set MSBUILD=