From becc254f67337b78f80908ab154e52bdd9c78798 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Thu, 10 Jun 2021 00:45:06 +0200 Subject: [PATCH 01/12] Add percentage sign to opacity slider values (#10369) This PR adds a new PercentageSignConverter that appends the percentage sign to a number. The new converter is being used by the Acrylic opacity slider label and the Background image opacity slider label. * [x] Closes #10289 --- .../TerminalSettingsEditor/Converters.idl | 5 ++++ ...Microsoft.Terminal.Settings.Editor.vcxproj | 8 ++++- ...t.Terminal.Settings.Editor.vcxproj.filters | 8 ++++- .../PercentageSignConverter.cpp | 29 +++++++++++++++++++ .../PercentageSignConverter.h | 9 ++++++ .../TerminalSettingsEditor/Profiles.xaml | 5 ++-- 6 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 src/cascadia/TerminalSettingsEditor/PercentageSignConverter.cpp create mode 100644 src/cascadia/TerminalSettingsEditor/PercentageSignConverter.h diff --git a/src/cascadia/TerminalSettingsEditor/Converters.idl b/src/cascadia/TerminalSettingsEditor/Converters.idl index 58fb27db0..89eb05791 100644 --- a/src/cascadia/TerminalSettingsEditor/Converters.idl +++ b/src/cascadia/TerminalSettingsEditor/Converters.idl @@ -39,6 +39,11 @@ namespace Microsoft.Terminal.Settings.Editor PercentageConverter(); }; + runtimeclass PercentageSignConverter : [default] Windows.UI.Xaml.Data.IValueConverter + { + PercentageSignConverter(); + }; + runtimeclass StringIsEmptyConverter : [default] Windows.UI.Xaml.Data.IValueConverter { StringIsEmptyConverter(); diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj index de8f8435b..a3df59932 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj @@ -60,6 +60,9 @@ Converters.idl + + Converters.idl + Converters.idl @@ -167,6 +170,9 @@ Converters.idl + + Converters.idl + Converters.idl @@ -332,4 +338,4 @@ - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters index ba44e4844..4273f715d 100644 --- a/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters +++ b/src/cascadia/TerminalSettingsEditor/Microsoft.Terminal.Settings.Editor.vcxproj.filters @@ -10,10 +10,16 @@ + + Converters + + + Converters + @@ -43,4 +49,4 @@ {00f725c8-41b4-40a8-995e-8ee2e49a4a4c} - + \ No newline at end of file diff --git a/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.cpp b/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.cpp new file mode 100644 index 000000000..a8acb84d1 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.cpp @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "PercentageSignConverter.h" +#include "PercentageSignConverter.g.cpp" + +using namespace winrt::Windows; +using namespace winrt::Windows::UI::Xaml; + +namespace winrt::Microsoft::Terminal::Settings::Editor::implementation +{ + Foundation::IInspectable PercentageSignConverter::Convert(Foundation::IInspectable const& value, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /* parameter */, + hstring const& /* language */) + { + const auto number{ winrt::unbox_value(value) }; + return winrt::box_value(to_hstring((int)number) + L"%"); + } + + Foundation::IInspectable PercentageSignConverter::ConvertBack(Foundation::IInspectable const& /*value*/, + Windows::UI::Xaml::Interop::TypeName const& /* targetType */, + Foundation::IInspectable const& /*parameter*/, + hstring const& /* language */) + { + throw hresult_not_implemented(); + } +} diff --git a/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.h b/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.h new file mode 100644 index 000000000..08b4f13b3 --- /dev/null +++ b/src/cascadia/TerminalSettingsEditor/PercentageSignConverter.h @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "PercentageSignConverter.g.h" +#include "Utils.h" + +DECLARE_CONVERTER(winrt::Microsoft::Terminal::Settings::Editor, PercentageSignConverter); diff --git a/src/cascadia/TerminalSettingsEditor/Profiles.xaml b/src/cascadia/TerminalSettingsEditor/Profiles.xaml index 68c592d5b..3ec1ab719 100644 --- a/src/cascadia/TerminalSettingsEditor/Profiles.xaml +++ b/src/cascadia/TerminalSettingsEditor/Profiles.xaml @@ -35,6 +35,7 @@ + @@ -626,7 +627,7 @@ Value="{x:Bind State.Profile.BackgroundImageOpacity, Converter={StaticResource PercentageConverter}, Mode=TwoWay}" /> + Text="{Binding ElementName=BIOpacitySlider, Path=Value, Mode=OneWay, Converter={StaticResource PercentageSignConverter}}" /> @@ -664,7 +665,7 @@ Value="{x:Bind State.Profile.AcrylicOpacity, Converter={StaticResource PercentageConverter}, Mode=TwoWay}" /> + Text="{Binding ElementName=AcrylicOpacitySlider, Path=Value, Mode=OneWay, Converter={StaticResource PercentageSignConverter}}" /> From 3c81b51b78d2bcea961c779d7bb9d7e13d84db7c Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Wed, 9 Jun 2021 23:42:11 -0700 Subject: [PATCH 02/12] Activate window only (no toggle) when inbound connection arrives (#10389) Activate window only (no toggle) when inbound connection arrives ## PR Checklist * [x] Closes #10386 * [x] I work here * [x] Manual test passed. ## Detailed Description of the Pull Request / Additional comments The default for the `SummonWindowBehavior` is a toggle of the visibility state. I didn't realize that. We do not want that for inbound connections. We want always-brought-to-front. ## Validation Steps Performed - Made the change. Launched Terminal as default as active window. Runbox'd another command. It didn't hide itself like it used to. Stays visible. --- src/cascadia/WindowsTerminal/AppHost.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 00fe42c04..49f51f031 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -935,5 +935,6 @@ void AppHost::_SummonWindowRequested(const winrt::Windows::Foundation::IInspecta summonArgs.MoveToCurrentDesktop(false); summonArgs.DropdownDuration(0); summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace); + summonArgs.ToggleVisibility(false); // Do not toggle, just make visible. _HandleSummon(sender, summonArgs); } From d0d3cc6a754451d80853baa8cbe8a951e47cc498 Mon Sep 17 00:00:00 2001 From: gabrielconl <84711285+gabrielconl@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:49:52 +0300 Subject: [PATCH 03/12] Terminal color picker tweaks (#10219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The flyout wasn't very polished, so I did some adjustments. It's all visual changes, functionality should be the same. * made the flyout use OverlayCornerRadius and 16px padding (to match WinUI 2.6) * changed ColorPicker to muxc:ColorPicker for new styles (the color schemes picker too) * changed "Custom" Button into a ToggleButton * no longer needs ellipsis - localization files should be updated * OK button was moved to the right and uses accent color * adjusted margins and padding * tweaked the color boxes to _look_ like the ones in color schemes ![collapsednew](https://user-images.githubusercontent.com/84711285/119713282-33cfcf80-be6a-11eb-9ad9-d18a97b1058a.png) ![expandednew](https://user-images.githubusercontent.com/84711285/119713295-35999300-be6a-11eb-8423-c1c03526b23a.png) ## Validation Steps Performed * Color picker in settings UI still works ✔️ * Color picker for tabs still works ✔️ --- src/cascadia/TerminalApp/App.xaml | 3 + .../TerminalApp/ColorPickupFlyout.cpp | 2 +- src/cascadia/TerminalApp/ColorPickupFlyout.h | 2 +- .../TerminalApp/ColorPickupFlyout.xaml | 332 +++++++++--------- .../TerminalSettingsEditor/ColorSchemes.cpp | 11 +- .../TerminalSettingsEditor/ColorSchemes.h | 2 +- .../TerminalSettingsEditor/ColorSchemes.xaml | 6 +- 7 files changed, 189 insertions(+), 169 deletions(-) diff --git a/src/cascadia/TerminalApp/App.xaml b/src/cascadia/TerminalApp/App.xaml index 88630a11c..a0678e05e 100644 --- a/src/cascadia/TerminalApp/App.xaml +++ b/src/cascadia/TerminalApp/App.xaml @@ -47,6 +47,9 @@ 8,0,8,0 + + 12 + diff --git a/src/cascadia/TerminalApp/ColorPickupFlyout.cpp b/src/cascadia/TerminalApp/ColorPickupFlyout.cpp index b1c15be48..ed1d20b83 100644 --- a/src/cascadia/TerminalApp/ColorPickupFlyout.cpp +++ b/src/cascadia/TerminalApp/ColorPickupFlyout.cpp @@ -96,7 +96,7 @@ namespace winrt::TerminalApp::implementation Hide(); } - void ColorPickupFlyout::ColorPicker_ColorChanged(const Windows::UI::Xaml::Controls::ColorPicker&, const Windows::UI::Xaml::Controls::ColorChangedEventArgs& args) + void ColorPickupFlyout::ColorPicker_ColorChanged(const Microsoft::UI::Xaml::Controls::ColorPicker&, const Microsoft::UI::Xaml::Controls::ColorChangedEventArgs& args) { _ColorSelectedHandlers(args.NewColor()); } diff --git a/src/cascadia/TerminalApp/ColorPickupFlyout.h b/src/cascadia/TerminalApp/ColorPickupFlyout.h index c73f68881..b124dfe43 100644 --- a/src/cascadia/TerminalApp/ColorPickupFlyout.h +++ b/src/cascadia/TerminalApp/ColorPickupFlyout.h @@ -12,7 +12,7 @@ namespace winrt::TerminalApp::implementation void ShowColorPickerButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void CustomColorButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); void ClearColorButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& args); - void ColorPicker_ColorChanged(const Windows::UI::Xaml::Controls::ColorPicker&, const Windows::UI::Xaml::Controls::ColorChangedEventArgs& args); + void ColorPicker_ColorChanged(const Microsoft::UI::Xaml::Controls::ColorPicker&, const Microsoft::UI::Xaml::Controls::ColorChangedEventArgs& args); WINRT_CALLBACK(ColorCleared, TerminalApp::ColorClearedArgs); WINRT_CALLBACK(ColorSelected, TerminalApp::ColorSelectedArgs); diff --git a/src/cascadia/TerminalApp/ColorPickupFlyout.xaml b/src/cascadia/TerminalApp/ColorPickupFlyout.xaml index 7674b357e..b7da875af 100644 --- a/src/cascadia/TerminalApp/ColorPickupFlyout.xaml +++ b/src/cascadia/TerminalApp/ColorPickupFlyout.xaml @@ -4,190 +4,200 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="using:TerminalApp" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:muxc="using:Microsoft.UI.Xaml.Controls" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 0f42ee189d23307cc09a046fea57319c09090837 Mon Sep 17 00:00:00 2001 From: Michael Niksa Date: Thu, 10 Jun 2021 10:22:22 -0700 Subject: [PATCH 04/12] Summon, not toggle visibility, window on command line dispatch (#10396) Summon, not toggle visibility, window on command line dispatch ## PR Checklist * [x] Closes #10292 * [x] I work here * [x] Manual test ## Detailed Description of the Pull Request / Additional comments - This is the same as #10389, just a different route. I didn't realize it at the time. ## Validation Steps Performed - Opened a window. Dispatched the `wt -w 0 - p ` and watched it join/summon instead of minimize the active WT. --- src/cascadia/WindowsTerminal/AppHost.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 49f51f031..753166bf1 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -594,6 +594,7 @@ void AppHost::_DispatchCommandline(winrt::Windows::Foundation::IInspectable send summonArgs.MoveToCurrentDesktop(false); summonArgs.DropdownDuration(0); summonArgs.ToMonitor(Remoting::MonitorBehavior::InPlace); + summonArgs.ToggleVisibility(false); // Do not toggle, just make visible. // Summon the window whenever we dispatch a commandline to it. This will // make it obvious when a new tab/pane is created in a window. _HandleSummon(sender, summonArgs); From fe283fc28b19fa85b6dec3aab76da86b41cbadeb Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Thu, 10 Jun 2021 12:49:12 -0500 Subject: [PATCH 05/12] Add a Scratch.sln for prototyping (#10067) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### ⚠️ This targets #10051 ## Summary of the Pull Request This PR creates a `Samples` solution which can be used for quickly testing our particular WinUI + Xaml Islands + wapproj setup, with a `TermControl`. This lets us quickly prototype and minimally repro xaml islands bugs, but also lets us iterate quickly on changes in the process model. I'll be using this in the future to prove that the out-of-proc control works (for tear-out), without having to tear up the entire `TerminalApp` all at once. While I'll be leaning on this extensively for tear-out prototyping, I've also found myself wanting this kind of sample sln many times in the past. We run into bugs all the time where we're not sure if they're XAML Islands bugs or Terminal bugs. However, standing up a scratch sln with MUX hooked up, and a `XamlApplication` from the toolkit, and XAML Islands is time consuming. This sample sln should let us validate if bugs are XI bugs or Terminal bugs much easier. ## References * Tear-out: #1256 * Megathread: #5000 * Project: https://github.com/microsoft/terminal/projects/5 ## PR Checklist * [x] Closes one bullet point of https://github.com/microsoft/terminal/projects/5#card-50760312 * [x] I work here * [x] Tests added/passed * [n/a] Requires documentation to be updated ## Detailed Description of the Pull Request / Additional comments This is _largely_ a copy-pasta of our existing projects, in massively simplified form. I'm gonna wontfix most comments on the code that was copy-pasta'd. If it's bad here, it's probably also bad in the real version in `OpenConsole.sln`. * I made an entirely new `.sln` for this, so that these samples wouldn't need to build in CI. They're not critical code, they're literally just for prototyping. * `TerminalAppLib` & `TerminalApp` became `SampleAppLib` and `SampleApp`. This is just a simple application hosting a single `TermControl`. It references the winmds and dlls from the main `OpenConsole.sln`, but in a way that would be more like a project consuming a nuget feed of the control, rather than a `ProjectReference`. - It still does all the `App.xaml` and `Toolkit.XamlApplication` stuff that TerminalApp does, so that it can load MUX resources, and do MUX-y things. * `WindowsTerminal` became `WindowExe`. I tore out all of the `NonClientIslandWindow` stuff - this is just a window with a xaml island. * `CascadiaPackage` became `Package`. It does the vclibs hack again for the `TerminalConnection` dlls (because this package can't actually determine that `TerminalConnection.dll` requires `cprest*.dll`), as well as for `windowsterminal.exe` (which we'll need in the future for out-of-proc controls). I got rid of all the Dev/Preview/Release asset machinations as well. Wherever possible, I changed filenames slightly so that they won't get accitdentally opened when someone tries to open a file by name in their editor (**cough** sublime's ctrl+p menu **cough**). ## Validation Steps Performed The sample app launches, and displays a TermControl. What else do you want? (_rhetorical, not a prompt for a wishlist_) (cherry picked from commit 30d6cf4839fca8ac8203f6c2489b02a4088b851e) --- Scratch.sln | 221 +++++++++++++++++ consolegit2gitfilters.json | 2 + .../Package/Package.appxmanifest | 68 ++++++ .../ScratchIslandApp/Package/Package.wapproj | 155 ++++++++++++ .../Package/Resources/Resources.resw | 126 ++++++++++ .../Package/Resources/en-US/Resources.resw | 123 ++++++++++ scratch/ScratchIslandApp/SampleApp/App.base.h | 40 +++ scratch/ScratchIslandApp/SampleApp/App.cpp | 65 +++++ scratch/ScratchIslandApp/SampleApp/App.h | 29 +++ scratch/ScratchIslandApp/SampleApp/App.idl | 16 ++ scratch/ScratchIslandApp/SampleApp/App.xaml | 71 ++++++ scratch/ScratchIslandApp/SampleApp/MyPage.cpp | 54 +++++ scratch/ScratchIslandApp/SampleApp/MyPage.h | 28 +++ scratch/ScratchIslandApp/SampleApp/MyPage.idl | 10 + .../ScratchIslandApp/SampleApp/MyPage.xaml | 56 +++++ .../ScratchIslandApp/SampleApp/MySettings.cpp | 11 + .../ScratchIslandApp/SampleApp/MySettings.h | 97 ++++++++ .../ScratchIslandApp/SampleApp/MySettings.idl | 14 ++ .../SampleApp/Resources/en-US/Resources.resw | 120 +++++++++ .../SampleApp/SampleAppLib.vcxproj | 181 ++++++++++++++ .../SampleApp/SampleAppLogic.cpp | 84 +++++++ .../SampleApp/SampleAppLogic.h | 41 ++++ .../SampleApp/SampleAppLogic.idl | 19 ++ .../SampleApp/dll/SampleApp.def | 3 + .../SampleApp/dll/SampleApp.vcxproj | 101 ++++++++ .../ScratchIslandApp/SampleApp/dll/pch.cpp | 4 + scratch/ScratchIslandApp/SampleApp/dll/pch.h | 12 + scratch/ScratchIslandApp/SampleApp/init.cpp | 13 + .../SampleApp/packages.config | 6 + scratch/ScratchIslandApp/SampleApp/pch.cpp | 4 + scratch/ScratchIslandApp/SampleApp/pch.h | 71 ++++++ .../WindowExe/SampleAppHost.cpp | 78 ++++++ .../WindowExe/SampleAppHost.h | 20 ++ .../WindowExe/SampleBaseWindow.h | 228 ++++++++++++++++++ .../WindowExe/SampleIslandWindow.cpp | 220 +++++++++++++++++ .../WindowExe/SampleIslandWindow.h | 45 ++++ .../ScratchIslandApp/WindowExe/SampleMain.cpp | 122 ++++++++++ .../ScratchIslandApp/WindowExe/WindowExe.def | 1 + .../WindowExe/WindowExe.manifest | 20 ++ .../ScratchIslandApp/WindowExe/WindowExe.rc | 97 ++++++++ .../WindowExe/WindowExe.vcxproj | 220 +++++++++++++++++ scratch/ScratchIslandApp/WindowExe/icon.cpp | 49 ++++ scratch/ScratchIslandApp/WindowExe/icon.h | 6 + .../WindowExe/packages.config | 7 + scratch/ScratchIslandApp/WindowExe/pch.cpp | 4 + scratch/ScratchIslandApp/WindowExe/pch.h | 82 +++++++ scratch/ScratchIslandApp/WindowExe/resource.h | 27 +++ 47 files changed, 3071 insertions(+) create mode 100644 Scratch.sln create mode 100644 scratch/ScratchIslandApp/Package/Package.appxmanifest create mode 100644 scratch/ScratchIslandApp/Package/Package.wapproj create mode 100644 scratch/ScratchIslandApp/Package/Resources/Resources.resw create mode 100644 scratch/ScratchIslandApp/Package/Resources/en-US/Resources.resw create mode 100644 scratch/ScratchIslandApp/SampleApp/App.base.h create mode 100644 scratch/ScratchIslandApp/SampleApp/App.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/App.h create mode 100644 scratch/ScratchIslandApp/SampleApp/App.idl create mode 100644 scratch/ScratchIslandApp/SampleApp/App.xaml create mode 100644 scratch/ScratchIslandApp/SampleApp/MyPage.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/MyPage.h create mode 100644 scratch/ScratchIslandApp/SampleApp/MyPage.idl create mode 100644 scratch/ScratchIslandApp/SampleApp/MyPage.xaml create mode 100644 scratch/ScratchIslandApp/SampleApp/MySettings.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/MySettings.h create mode 100644 scratch/ScratchIslandApp/SampleApp/MySettings.idl create mode 100644 scratch/ScratchIslandApp/SampleApp/Resources/en-US/Resources.resw create mode 100644 scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj create mode 100644 scratch/ScratchIslandApp/SampleApp/SampleAppLogic.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/SampleAppLogic.h create mode 100644 scratch/ScratchIslandApp/SampleApp/SampleAppLogic.idl create mode 100644 scratch/ScratchIslandApp/SampleApp/dll/SampleApp.def create mode 100644 scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj create mode 100644 scratch/ScratchIslandApp/SampleApp/dll/pch.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/dll/pch.h create mode 100644 scratch/ScratchIslandApp/SampleApp/init.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/packages.config create mode 100644 scratch/ScratchIslandApp/SampleApp/pch.cpp create mode 100644 scratch/ScratchIslandApp/SampleApp/pch.h create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleAppHost.cpp create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleAppHost.h create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleBaseWindow.h create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.cpp create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.h create mode 100644 scratch/ScratchIslandApp/WindowExe/SampleMain.cpp create mode 100644 scratch/ScratchIslandApp/WindowExe/WindowExe.def create mode 100644 scratch/ScratchIslandApp/WindowExe/WindowExe.manifest create mode 100644 scratch/ScratchIslandApp/WindowExe/WindowExe.rc create mode 100644 scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj create mode 100644 scratch/ScratchIslandApp/WindowExe/icon.cpp create mode 100644 scratch/ScratchIslandApp/WindowExe/icon.h create mode 100644 scratch/ScratchIslandApp/WindowExe/packages.config create mode 100644 scratch/ScratchIslandApp/WindowExe/pch.cpp create mode 100644 scratch/ScratchIslandApp/WindowExe/pch.h create mode 100644 scratch/ScratchIslandApp/WindowExe/resource.h diff --git a/Scratch.sln b/Scratch.sln new file mode 100644 index 000000000..ac6caf341 --- /dev/null +++ b/Scratch.sln @@ -0,0 +1,221 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31205.134 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Package", "scratch\ScratchIslandApp\Package\Package.wapproj", "{CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleAppLib", "scratch\ScratchIslandApp\SampleApp\SampleAppLib.vcxproj", "{A4394404-37F7-41C1-802B-49788D3720E3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleApp", "scratch\ScratchIslandApp\SampleApp\dll\SampleApp.vcxproj", "{26C51792-41A3-4FE0-AB5E-8B69D557BF91}" + ProjectSection(ProjectDependencies) = postProject + {A4394404-37F7-41C1-802B-49788D3720E3} = {A4394404-37F7-41C1-802B-49788D3720E3} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WindowExe", "scratch\ScratchIslandApp\WindowExe\WindowExe.vcxproj", "{B4427499-9FDE-4208-B456-5BC580637633}" + ProjectSection(ProjectDependencies) = postProject + {26C51792-41A3-4FE0-AB5E-8B69D557BF91} = {26C51792-41A3-4FE0-AB5E-8B69D557BF91} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common Props", "Common Props", "{53DD5520-E64C-4C06-B472-7CE62CA539C9}" + ProjectSection(SolutionItems) = preProject + 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 + 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 + 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 +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "src\dep\fmt\fmt.vcxproj", "{6BAE5851-50D5-4934-8D5E-30361A8A40F3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Types", "src\types\lib\types.vcxproj", "{18D09A24-8240-42D6-8CB6-236EEE820263}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{75AC9360-76FD-4ABC-AFEC-EF342BD2B3E9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + AuditMode|ARM64 = AuditMode|ARM64 + AuditMode|x64 = AuditMode|x64 + AuditMode|x86 = AuditMode|x86 + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Fuzzing|ARM64 = Fuzzing|ARM64 + Fuzzing|x64 = Fuzzing|x64 + Fuzzing|x86 = Fuzzing|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|ARM64.ActiveCfg = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|ARM64.Build.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|ARM64.Deploy.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x64.ActiveCfg = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x64.Build.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x64.Deploy.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x86.ActiveCfg = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x86.Build.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.AuditMode|x86.Deploy.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|ARM64.Build.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x64.ActiveCfg = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x64.Build.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x64.Deploy.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x86.ActiveCfg = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x86.Build.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Debug|x86.Deploy.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|ARM64.ActiveCfg = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|ARM64.Build.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|ARM64.Deploy.0 = Debug|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x64.ActiveCfg = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x64.Build.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x64.Deploy.0 = Debug|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x86.ActiveCfg = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x86.Build.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Fuzzing|x86.Deploy.0 = Debug|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|ARM64.ActiveCfg = Release|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|ARM64.Build.0 = Release|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|ARM64.Deploy.0 = Release|ARM64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x64.ActiveCfg = Release|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x64.Build.0 = Release|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x64.Deploy.0 = Release|x64 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x86.ActiveCfg = Release|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x86.Build.0 = Release|x86 + {CF31505E-3BAE-4C0A-81D7-F1EB279F40BB}.Release|x86.Deploy.0 = Release|x86 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|x64.Build.0 = AuditMode|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.AuditMode|x86.Build.0 = AuditMode|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|ARM64.Build.0 = Debug|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|x64.ActiveCfg = Debug|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|x64.Build.0 = Debug|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|x86.ActiveCfg = Debug|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Debug|x86.Build.0 = Debug|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|ARM64.ActiveCfg = Release|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|ARM64.Build.0 = Release|ARM64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|x64.ActiveCfg = Release|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|x64.Build.0 = Release|x64 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|x86.ActiveCfg = Release|Win32 + {A4394404-37F7-41C1-802B-49788D3720E3}.Release|x86.Build.0 = Release|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|x64.Build.0 = AuditMode|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.AuditMode|x86.Build.0 = AuditMode|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|ARM64.Build.0 = Debug|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|x64.ActiveCfg = Debug|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|x64.Build.0 = Debug|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|x86.ActiveCfg = Debug|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Debug|x86.Build.0 = Debug|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|ARM64.ActiveCfg = Release|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|ARM64.Build.0 = Release|ARM64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|x64.ActiveCfg = Release|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|x64.Build.0 = Release|x64 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|x86.ActiveCfg = Release|Win32 + {26C51792-41A3-4FE0-AB5E-8B69D557BF91}.Release|x86.Build.0 = Release|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|x64.Build.0 = AuditMode|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.AuditMode|x86.Build.0 = AuditMode|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|ARM64.Build.0 = Debug|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|x64.ActiveCfg = Debug|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|x64.Build.0 = Debug|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|x86.ActiveCfg = Debug|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Debug|x86.Build.0 = Debug|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|ARM64.ActiveCfg = Release|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|ARM64.Build.0 = Release|ARM64 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|x64.ActiveCfg = Release|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|x64.Build.0 = Release|x64 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|x86.ActiveCfg = Release|Win32 + {B4427499-9FDE-4208-B456-5BC580637633}.Release|x86.Build.0 = Release|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x64.Build.0 = AuditMode|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.AuditMode|x86.Build.0 = AuditMode|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|ARM64.Build.0 = Debug|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x64.ActiveCfg = Debug|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x64.Build.0 = Debug|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x86.ActiveCfg = Debug|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Debug|x86.Build.0 = Debug|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|ARM64.ActiveCfg = Release|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|ARM64.Build.0 = Release|ARM64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x64.ActiveCfg = Release|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x64.Build.0 = Release|x64 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.ActiveCfg = Release|Win32 + {6BAE5851-50D5-4934-8D5E-30361A8A40F3}.Release|x86.Build.0 = Release|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x64.Build.0 = AuditMode|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.AuditMode|x86.Build.0 = AuditMode|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|ARM64.Build.0 = Debug|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x64.ActiveCfg = Debug|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x64.Build.0 = Debug|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x86.ActiveCfg = Debug|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Debug|x86.Build.0 = Debug|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|ARM64.ActiveCfg = Release|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|ARM64.Build.0 = Release|ARM64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x64.ActiveCfg = Release|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x64.Build.0 = Release|x64 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.ActiveCfg = Release|Win32 + {18D09A24-8240-42D6-8CB6-236EEE820263}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6BAE5851-50D5-4934-8D5E-30361A8A40F3} = {75AC9360-76FD-4ABC-AFEC-EF342BD2B3E9} + {18D09A24-8240-42D6-8CB6-236EEE820263} = {75AC9360-76FD-4ABC-AFEC-EF342BD2B3E9} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {05EAE315-9188-4D7B-B889-7D5F480A8915} + EndGlobalSection +EndGlobal diff --git a/consolegit2gitfilters.json b/consolegit2gitfilters.json index 5a760692b..5f5d0eb26 100644 --- a/consolegit2gitfilters.json +++ b/consolegit2gitfilters.json @@ -23,6 +23,8 @@ "/doc/cascadia/", "/doc/user-docs/", "/src/tools/MonarchPeasantSample/", + "/scratch/", + "Scratch.sln", ], "SuffixFilters": [ ".dbb", diff --git a/scratch/ScratchIslandApp/Package/Package.appxmanifest b/scratch/ScratchIslandApp/Package/Package.appxmanifest new file mode 100644 index 000000000..dcd54adca --- /dev/null +++ b/scratch/ScratchIslandApp/Package/Package.appxmanifest @@ -0,0 +1,68 @@ + + + + + + + + Sample App + A Lone Developer + Images\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scratch/ScratchIslandApp/Package/Package.wapproj b/scratch/ScratchIslandApp/Package/Package.wapproj new file mode 100644 index 000000000..262719541 --- /dev/null +++ b/scratch/ScratchIslandApp/Package/Package.wapproj @@ -0,0 +1,155 @@ + + + + + + + false + false + + + cf31505e-3bae-4c0a-81d7-f1eb279f40bb + ..\WindowExe\WindowExe.vcxproj + NativeOnly + + + false + Never + + + true + False + Package_TemporaryKey.pfx + + + + + + + Designer + + + + + + + + + + + + + + + + + + $(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd + $(OpenConsoleCommonOutDir)TerminalConnection\TerminalConnection.dll + true + true + true + + + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.winmd + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.dll + true + true + true + + + + + + + + <_TemporaryFilteredWapProjOutput Include="@(_FilteredNonWapProjProjectOutput)" /> + <_FilteredNonWapProjProjectOutput Remove="@(_TemporaryFilteredWapProjOutput)" /> + <_FilteredNonWapProjProjectOutput Include="@(_TemporaryFilteredWapProjOutput)"> + + + + + + + + + + <_GenerateProjectPriFileDependsOn Condition="$(MSBuildVersion) < '16.3.0'">OpenConsoleLiftDesktopBridgePriFiles;$(_GenerateProjectPriFileDependsOn) + + + + <_PriFile Include="@(_NonWapProjProjectOutput)" Condition="'%(Extension)' == '.pri'" /> + + + + + + + + $([MSBuild]::Unescape('$(WapProjBeforeGenerateAppxManifestDependsOn.Replace('_RemoveAllNonWapUWPItems', '_OpenConsoleRemoveAllNonWapUWPItems'))')) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/Package/Resources/Resources.resw b/scratch/ScratchIslandApp/Package/Resources/Resources.resw new file mode 100644 index 000000000..872f430e7 --- /dev/null +++ b/scratch/ScratchIslandApp/Package/Resources/Resources.resw @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Scratch XAML Island App + + + Scratch App + + diff --git a/scratch/ScratchIslandApp/Package/Resources/en-US/Resources.resw b/scratch/ScratchIslandApp/Package/Resources/en-US/Resources.resw new file mode 100644 index 000000000..4ffee428e --- /dev/null +++ b/scratch/ScratchIslandApp/Package/Resources/en-US/Resources.resw @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + A scratch app for XAML Islands tests + + diff --git a/scratch/ScratchIslandApp/SampleApp/App.base.h b/scratch/ScratchIslandApp/SampleApp/App.base.h new file mode 100644 index 000000000..d6da79f1b --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/App.base.h @@ -0,0 +1,40 @@ +#pragma once + +namespace winrt::SampleApp::implementation +{ + template + struct App_baseWithProvider : public App_base + { + using IXamlType = ::winrt::Windows::UI::Xaml::Markup::IXamlType; + + IXamlType GetXamlType(::winrt::Windows::UI::Xaml::Interop::TypeName const& type) + { + return AppProvider()->GetXamlType(type); + } + + IXamlType GetXamlType(::winrt::hstring const& fullName) + { + return AppProvider()->GetXamlType(fullName); + } + + ::winrt::com_array<::winrt::Windows::UI::Xaml::Markup::XmlnsDefinition> GetXmlnsDefinitions() + { + return AppProvider()->GetXmlnsDefinitions(); + } + + private: + bool _contentLoaded{ false }; + std::shared_ptr _appProvider; + std::shared_ptr AppProvider() + { + if (!_appProvider) + { + _appProvider = std::make_shared(); + } + return _appProvider; + } + }; + + template + using AppT2 = App_baseWithProvider; +} diff --git a/scratch/ScratchIslandApp/SampleApp/App.cpp b/scratch/ScratchIslandApp/SampleApp/App.cpp new file mode 100644 index 000000000..5a2ae523f --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/App.cpp @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "App.h" +#include "App.g.cpp" + +using namespace winrt; +using namespace winrt::Windows::ApplicationModel::Activation; +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Xaml::Navigation; + +namespace winrt::SampleApp::implementation +{ + App::App() + { + // This is the same trick that Initialize() is about to use to figure out whether we're coming + // from a UWP context or from a Win32 context + // See https://github.com/windows-toolkit/Microsoft.Toolkit.Win32/blob/52611c57d89554f357f281d0c79036426a7d9257/Microsoft.Toolkit.Win32.UI.XamlApplication/XamlApplication.cpp#L42 + const auto dispatcherQueue = ::winrt::Windows::System::DispatcherQueue::GetForCurrentThread(); + if (dispatcherQueue) + { + _isUwp = true; + } + + Initialize(); + + // Disable XAML's automatic backplating of text when in High Contrast + // mode: we want full control of and responsibility for the foreground + // and background colors that we draw in XAML. + HighContrastAdjustment(::winrt::Windows::UI::Xaml::ApplicationHighContrastAdjustment::None); + } + + SampleAppLogic App::Logic() + { + static SampleAppLogic logic; + return logic; + } + + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + void App::OnLaunched(LaunchActivatedEventArgs const& /*e*/) + { + // if this is a UWP... it means its our problem to hook up the content to the window here. + if (_isUwp) + { + auto content = Window::Current().Content(); + if (content == nullptr) + { + auto logic = Logic(); + logic.Create(); + + auto page = logic.GetRoot().as(); + + Window::Current().Content(page); + Window::Current().Activate(); + } + } + } +} diff --git a/scratch/ScratchIslandApp/SampleApp/App.h b/scratch/ScratchIslandApp/SampleApp/App.h new file mode 100644 index 000000000..fb6235d75 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/App.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "App.g.h" +#include "App.base.h" + +namespace winrt::SampleApp::implementation +{ + struct App : AppT2 + { + public: + App(); + void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs const&); + + SampleApp::SampleAppLogic Logic(); + + private: + bool _isUwp = false; + }; +} + +namespace winrt::SampleApp::factory_implementation +{ + struct App : AppT + { + }; +} diff --git a/scratch/ScratchIslandApp/SampleApp/App.idl b/scratch/ScratchIslandApp/SampleApp/App.idl new file mode 100644 index 000000000..e51a73332 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/App.idl @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "SampleAppLogic.idl"; + +namespace SampleApp +{ + // ADD ARBITRARY APP LOGIC TO SampleAppLogic.idl, NOT HERE. + // This is for XAML platform setup only. + [default_interface] runtimeclass App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication + { + App(); + + SampleAppLogic Logic { get; }; + } +} diff --git a/scratch/ScratchIslandApp/SampleApp/App.xaml b/scratch/ScratchIslandApp/SampleApp/App.xaml new file mode 100644 index 000000000..c6a2328f4 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/App.xaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + 8,0,8,0 + + + + + + + + + + + + + + + + + + + + + diff --git a/scratch/ScratchIslandApp/SampleApp/MyPage.cpp b/scratch/ScratchIslandApp/SampleApp/MyPage.cpp new file mode 100644 index 000000000..d4af3d80e --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MyPage.cpp @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "MyPage.h" +#include +#include "MyPage.g.cpp" +#include "..\..\..\src\cascadia\UnitTests_Control\MockControlSettings.h" + +using namespace std::chrono_literals; +using namespace winrt::Microsoft::Terminal; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +namespace winrt::SampleApp::implementation +{ + MyPage::MyPage() + { + InitializeComponent(); + } + + void MyPage::Create() + { + TerminalConnection::EchoConnection conn{}; + auto settings = winrt::make_self(); + + Control::TermControl control{ *settings, conn }; + + InProcContent().Children().Append(control); + + // Once the control loads (and not before that), write some text for debugging: + control.Initialized([conn](auto&&, auto&&) { + conn.WriteInput(L"This TermControl is hosted in-proc..."); + }); + } + + // Method Description: + // - Gets the title of the currently focused terminal control. If there + // isn't a control selected for any reason, returns "Windows Terminal" + // Arguments: + // - + // Return Value: + // - the title of the focused control if there is one, else "Windows Terminal" + hstring MyPage::Title() + { + return { L"Sample Application" }; + } + +} diff --git a/scratch/ScratchIslandApp/SampleApp/MyPage.h b/scratch/ScratchIslandApp/SampleApp/MyPage.h new file mode 100644 index 000000000..c16c02bb3 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MyPage.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "MyPage.g.h" +#include "../../../src/cascadia/inc/cppwinrt_utils.h" + +namespace winrt::SampleApp::implementation +{ + struct MyPage : MyPageT + { + public: + MyPage(); + + void Create(); + + hstring Title(); + + private: + friend struct MyPageT; // for Xaml to bind events + }; +} + +namespace winrt::SampleApp::factory_implementation +{ + BASIC_FACTORY(MyPage); +} diff --git a/scratch/ScratchIslandApp/SampleApp/MyPage.idl b/scratch/ScratchIslandApp/SampleApp/MyPage.idl new file mode 100644 index 000000000..d3d0645b5 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MyPage.idl @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace SampleApp +{ + [default_interface] runtimeclass MyPage : Windows.UI.Xaml.Controls.Page + { + MyPage(); + } +} diff --git a/scratch/ScratchIslandApp/SampleApp/MyPage.xaml b/scratch/ScratchIslandApp/SampleApp/MyPage.xaml new file mode 100644 index 000000000..f6e129ee7 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MyPage.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scratch/ScratchIslandApp/SampleApp/MySettings.cpp b/scratch/ScratchIslandApp/SampleApp/MySettings.cpp new file mode 100644 index 000000000..c042a34e7 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MySettings.cpp @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "MySettings.h" +#include "MySettings.g.cpp" + +namespace winrt::SampleApp::implementation +{ +} diff --git a/scratch/ScratchIslandApp/SampleApp/MySettings.h b/scratch/ScratchIslandApp/SampleApp/MySettings.h new file mode 100644 index 000000000..170c9b540 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MySettings.h @@ -0,0 +1,97 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. +--*/ +#pragma once +#include "../../inc/cppwinrt_utils.h" +#include +#include +#include "MySettings.g.h" + +namespace winrt::SampleApp::implementation +{ + struct MySettings : MySettingsT + { + public: + MySettings() = default; + + // --------------------------- Core Settings --------------------------- + // All of these settings are defined in ICoreSettings. + + WINRT_PROPERTY(til::color, DefaultForeground, DEFAULT_FOREGROUND); + WINRT_PROPERTY(til::color, DefaultBackground, DEFAULT_BACKGROUND); + WINRT_PROPERTY(til::color, SelectionBackground, DEFAULT_FOREGROUND); + WINRT_PROPERTY(int32_t, HistorySize, DEFAULT_HISTORY_SIZE); + WINRT_PROPERTY(int32_t, InitialRows, 30); + WINRT_PROPERTY(int32_t, InitialCols, 80); + + WINRT_PROPERTY(bool, SnapOnInput, true); + WINRT_PROPERTY(bool, AltGrAliasing, true); + WINRT_PROPERTY(til::color, CursorColor, DEFAULT_CURSOR_COLOR); + WINRT_PROPERTY(winrt::Microsoft::Terminal::Core::CursorStyle, CursorShape, winrt::Microsoft::Terminal::Core::CursorStyle::Vintage); + WINRT_PROPERTY(uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT); + WINRT_PROPERTY(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS); + WINRT_PROPERTY(bool, CopyOnSelect, false); + WINRT_PROPERTY(bool, InputServiceWarning, true); + WINRT_PROPERTY(bool, FocusFollowMouse, false); + + WINRT_PROPERTY(winrt::Windows::Foundation::IReference, TabColor, nullptr); + + WINRT_PROPERTY(winrt::Windows::Foundation::IReference, StartingTabColor, nullptr); + + winrt::Microsoft::Terminal::Core::ICoreAppearance UnfocusedAppearance() { return {}; }; + + WINRT_PROPERTY(bool, TrimBlockSelection, false); + // ------------------------ End of Core Settings ----------------------- + + WINRT_PROPERTY(winrt::hstring, ProfileName); + WINRT_PROPERTY(bool, UseAcrylic, false); + WINRT_PROPERTY(double, TintOpacity, 0.5); + WINRT_PROPERTY(winrt::hstring, Padding, DEFAULT_PADDING); + WINRT_PROPERTY(winrt::hstring, FontFace, L"Consolas"); + WINRT_PROPERTY(int32_t, FontSize, DEFAULT_FONT_SIZE); + + WINRT_PROPERTY(winrt::Windows::UI::Text::FontWeight, FontWeight); + + WINRT_PROPERTY(winrt::hstring, BackgroundImage); + WINRT_PROPERTY(double, BackgroundImageOpacity, 1.0); + + WINRT_PROPERTY(winrt::Windows::UI::Xaml::Media::Stretch, BackgroundImageStretchMode, winrt::Windows::UI::Xaml::Media::Stretch::UniformToFill); + WINRT_PROPERTY(winrt::Windows::UI::Xaml::HorizontalAlignment, BackgroundImageHorizontalAlignment, winrt::Windows::UI::Xaml::HorizontalAlignment::Center); + WINRT_PROPERTY(winrt::Windows::UI::Xaml::VerticalAlignment, BackgroundImageVerticalAlignment, winrt::Windows::UI::Xaml::VerticalAlignment::Center); + + WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::IKeyBindings, KeyBindings, nullptr); + + WINRT_PROPERTY(winrt::hstring, Commandline); + WINRT_PROPERTY(winrt::hstring, StartingDirectory); + WINRT_PROPERTY(winrt::hstring, StartingTitle); + WINRT_PROPERTY(bool, SuppressApplicationTitle); + WINRT_PROPERTY(winrt::hstring, EnvironmentVariables); + + WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::ScrollbarState, ScrollState, winrt::Microsoft::Terminal::Control::ScrollbarState::Visible); + + WINRT_PROPERTY(winrt::Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, winrt::Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale); + + WINRT_PROPERTY(bool, RetroTerminalEffect, false); + WINRT_PROPERTY(bool, ForceFullRepaintRendering, false); + WINRT_PROPERTY(bool, SoftwareRendering, false); + WINRT_PROPERTY(bool, ForceVTInput, false); + + WINRT_PROPERTY(winrt::hstring, PixelShaderPath); + + WINRT_PROPERTY(bool, DetectURLs, true); + + private: + std::array _ColorTable; + + public: + winrt::Microsoft::Terminal::Core::Color GetColorTableEntry(int32_t index) noexcept { return _ColorTable.at(index); } + std::array ColorTable() { return _ColorTable; } + void ColorTable(std::array /*colors*/) {} + }; +} + +namespace winrt::SampleApp::factory_implementation +{ + BASIC_FACTORY(MySettings); +} diff --git a/scratch/ScratchIslandApp/SampleApp/MySettings.idl b/scratch/ScratchIslandApp/SampleApp/MySettings.idl new file mode 100644 index 000000000..42422b21a --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/MySettings.idl @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace SampleApp +{ + [default_interface] runtimeclass MySettings : Microsoft.Terminal.Core.ICoreSettings, + Microsoft.Terminal.Control.IControlSettings, + Microsoft.Terminal.Core.ICoreAppearance, + Microsoft.Terminal.Control.IControlAppearance + + { + MySettings(); + } +} diff --git a/scratch/ScratchIslandApp/SampleApp/Resources/en-US/Resources.resw b/scratch/ScratchIslandApp/SampleApp/Resources/en-US/Resources.resw new file mode 100644 index 000000000..f4af46df5 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/Resources/en-US/Resources.resw @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj new file mode 100644 index 000000000..c16d6be6f --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLib.vcxproj @@ -0,0 +1,181 @@ + + + + {a4394404-37f7-41c1-802b-49788d3720e3} + Win32Proj + SampleApp + SampleAppLib + SampleAppLib + StaticLibrary + Console + true + + false + nested + + + + + + + + + true + + + + + + + + Designer + + + + + + Designer + + + + + + + + MyPage.xaml + Code + + + App.xaml + + + SampleAppLogic.idl + + + + + + + + MyPage.xaml + Code + + + Create + + + App.xaml + + + SampleAppLogic.idl + + + + + + + + + App.xaml + + + + + MyPage.xaml + Code + + + + + + + + + + + + Warning + + + + + + $(OpenConsoleCommonOutDir)TerminalCore\Microsoft.Terminal.Core.winmd + true + false + false + + + $(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd + true + false + false + + + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.winmd + true + false + false + + + + + + pch.h + ..;%(AdditionalIncludeDirectories); + + 4702;%(DisableSpecificWarnings) + + + $(OpenConsoleCommonOutDir)\ConTypes.lib;WindowsApp.lib;shell32.lib;%(AdditionalDependencies) + + + + + + + + + + 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}. + + + + + + + + <_GenerateProjectPriFileDependsOn>OpenConsolePlaceAppXbfAtRootOfResourceTree;$(_GenerateProjectPriFileDependsOn) + + + + <_RelocatedAppXamlData Include="@(PackagingOutputs)" Condition="'%(Filename)' == 'App' and ('%(Extension)' == '.xaml' or '%(Extension)' == '.xbf')" /> + + + %(Filename)%(Extension) + + + + + + diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.cpp b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.cpp new file mode 100644 index 000000000..4498f70df --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.cpp @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SampleAppLogic.h" +#include "SampleAppLogic.g.cpp" + +#include + +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Controls; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::System; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + using IInspectable = Windows::Foundation::IInspectable; +} + +namespace winrt::SampleApp::implementation +{ + // Function Description: + // - Get the SampleAppLogic for the current active Xaml application, or null if there isn't one. + // Return value: + // - A pointer (bare) to the SampleAppLogic, or nullptr. The app logic outlives all other objects, + // unless the application is in a terrible way, so this is "safe." + SampleAppLogic* SampleAppLogic::Current() noexcept + try + { + if (auto currentXamlApp{ winrt::Windows::UI::Xaml::Application::Current().try_as() }) + { + if (auto SampleAppLogicPointer{ winrt::get_self(currentXamlApp.Logic()) }) + { + return SampleAppLogicPointer; + } + } + return nullptr; + } + catch (...) + { + LOG_CAUGHT_EXCEPTION(); + return nullptr; + } + + SampleAppLogic::SampleAppLogic() + { + // For your own sanity, it's better to do setup outside the ctor. + // If you do any setup in the ctor that ends up throwing an exception, + // then it might look like App just failed to activate, which will + // cause you to chase down the rabbit hole of "why is App not + // registered?" when it definitely is. + + // The MyPage has to be constructed during our construction, to + // make sure that there's a terminal page for callers of + // SetTitleBarContent + _root = winrt::make_self(); + } + + // Method Description: + // - Build the UI for the terminal app. Before this method is called, it + // should not be assumed that the SampleApp is usable. The Settings + // should be loaded before this is called, either with LoadSettings or + // GetLaunchDimensions (which will call LoadSettings) + // Arguments: + // - + // Return Value: + // - + void SampleAppLogic::Create() + { + _root->Create(); + } + + UIElement SampleAppLogic::GetRoot() noexcept + { + return _root.as(); + } + + hstring SampleAppLogic::Title() + { + return _root->Title(); + } + +} diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.h b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.h new file mode 100644 index 000000000..8b8642a8e --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.h @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +#include "SampleAppLogic.g.h" +#include "MyPage.h" +#include "../../../src/cascadia/inc/cppwinrt_utils.h" + +namespace winrt::SampleApp::implementation +{ + struct SampleAppLogic : SampleAppLogicT + { + public: + static SampleAppLogic* Current() noexcept; + + SampleAppLogic(); + ~SampleAppLogic() = default; + + void Create(); + + Windows::UI::Xaml::UIElement GetRoot() noexcept; + + winrt::hstring Title(); + + private: + // If you add controls here, but forget to null them either here or in + // the ctor, you're going to have a bad time. It'll mysteriously fail to + // activate the SampleAppLogic. + // ALSO: If you add any UIElements as roots here, make sure they're + // updated in _ApplyTheme. The root currently is _root. + winrt::com_ptr _root{ nullptr }; + }; +} + +namespace winrt::SampleApp::factory_implementation +{ + struct SampleAppLogic : SampleAppLogicT + { + }; +} diff --git a/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.idl b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.idl new file mode 100644 index 000000000..9b7d49e1a --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/SampleAppLogic.idl @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + + +namespace SampleApp +{ + + [default_interface] runtimeclass SampleAppLogic + { + SampleAppLogic(); + + void Create(); + + Windows.UI.Xaml.UIElement GetRoot(); + + String Title { get; }; + + } +} diff --git a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.def b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.def new file mode 100644 index 000000000..8c1a02932 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj new file mode 100644 index 000000000..1b1961fac --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/dll/SampleApp.vcxproj @@ -0,0 +1,101 @@ + + + + {26c51792-41a3-4fe0-ab5e-8b69d557bf91} + SampleApp + SampleApp + + + DynamicLibrary + Console + + true + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + true + true + + + + + + + + $(OpenConsoleCommonOutDir)TerminalCore\Microsoft.Terminal.Core.winmd + true + false + false + + + $(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd + true + false + false + + + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.winmd + true + false + false + + + + + + + + + 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}. + + + + + + + + $(OpenConsoleDir)\dep\jsoncpp\json;%(AdditionalIncludeDirectories); + + + WindowsApp.lib;%(AdditionalDependencies) + + /INCLUDE:_DllMain@12 %(AdditionalOptions) + /INCLUDE:DllMain %(AdditionalOptions) + + + + diff --git a/scratch/ScratchIslandApp/SampleApp/dll/pch.cpp b/scratch/ScratchIslandApp/SampleApp/dll/pch.cpp new file mode 100644 index 000000000..3c27d44d5 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/dll/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/scratch/ScratchIslandApp/SampleApp/dll/pch.h b/scratch/ScratchIslandApp/SampleApp/dll/pch.h new file mode 100644 index 000000000..76004c88f --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/dll/pch.h @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// pch.h +// Header for platform projection include files +// + +#pragma once + +// This file can be empty - the pch.h in SampleApp/lib does the heavy lifting +// of including all the headers we need. As this project is just a dll wrapper, +// we don't actually need anything in here. diff --git a/scratch/ScratchIslandApp/SampleApp/init.cpp b/scratch/ScratchIslandApp/SampleApp/init.cpp new file mode 100644 index 000000000..1c169d9d9 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/init.cpp @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation +// Licensed under the MIT license. + +#include "pch.h" +#include +#include + +BOOL WINAPI DllMain(HINSTANCE /*hInstDll*/, DWORD /*reason*/, LPVOID /*reserved*/) +{ + return TRUE; +} + +UTILS_DEFINE_LIBRARY_RESOURCE_SCOPE(L"SampleApp/Resources") diff --git a/scratch/ScratchIslandApp/SampleApp/packages.config b/scratch/ScratchIslandApp/SampleApp/packages.config new file mode 100644 index 000000000..a6c68340a --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/packages.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/scratch/ScratchIslandApp/SampleApp/pch.cpp b/scratch/ScratchIslandApp/SampleApp/pch.cpp new file mode 100644 index 000000000..3c27d44d5 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/scratch/ScratchIslandApp/SampleApp/pch.h b/scratch/ScratchIslandApp/SampleApp/pch.h new file mode 100644 index 000000000..fd9d893f9 --- /dev/null +++ b/scratch/ScratchIslandApp/SampleApp/pch.h @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +// pch.h +// Header for platform projection include files +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +#include +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "winrt/Windows.UI.Xaml.Markup.h" +#include "winrt/Windows.UI.ViewManagement.h" + +#include +#include +#include +#include + +#include + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hSampleAppProvider); +#include +#include + +#include +#include + +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#include "til.h" diff --git a/scratch/ScratchIslandApp/WindowExe/SampleAppHost.cpp b/scratch/ScratchIslandApp/WindowExe/SampleAppHost.cpp new file mode 100644 index 000000000..2d3afefc5 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleAppHost.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SampleAppHost.h" +#include "../types/inc/Viewport.hpp" +#include "../types/inc/utils.hpp" +#include "../types/inc/User32Utils.hpp" +#include "resource.h" + +using namespace winrt::Windows::UI; +using namespace winrt::Windows::UI::Composition; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Hosting; +using namespace winrt::Windows::Foundation::Numerics; +using namespace ::Microsoft::Console; +using namespace ::Microsoft::Console::Types; + +SampleAppHost::SampleAppHost() noexcept : + _app{}, + _logic{ nullptr }, // don't make one, we're going to take a ref on app's + _window{ nullptr } +{ + _logic = _app.Logic(); // get a ref to app's logic + + _window = std::make_unique(); + _window->MakeWindow(); +} + +SampleAppHost::~SampleAppHost() +{ + // destruction order is important for proper teardown here + _window = nullptr; + _app.Close(); + _app = nullptr; +} +// Method Description: +// - Initializes the XAML island, creates the terminal app, and sets the +// island's content to that of the terminal app's content. Also registers some +// callbacks with TermApp. +// !!! IMPORTANT!!! +// This must be called *AFTER* WindowsXamlManager::InitializeForCurrentThread. +// If it isn't, then we won't be able to create the XAML island. +// Arguments: +// - +// Return Value: +// - +void SampleAppHost::Initialize() +{ + _window->Initialize(); + + _logic.Create(); + + _window->UpdateTitle(_logic.Title()); + + // Set up the content of the application. If the app has a custom titlebar, + // set that content as well. + _window->SetContent(_logic.GetRoot()); + + _window->OnAppInitialized(); + + // THIS IS A HACK + // + // We've got a weird crash that happens terribly inconsistently, only in + // Debug mode. Apparently, there's some weird ref-counting magic that goes + // on during teardown, and our Application doesn't get closed quite right, + // which can cause us to crash into the debugger. This of course, only + // happens on exit, and happens somewhere in the XamlHost.dll code. + // + // Crazily, if we _manually leak the Application_ here, then the crash + // doesn't happen. This doesn't matter, because we really want the + // Application to live for _the entire lifetime of the process_, so the only + // time when this object would actually need to get cleaned up is _during + // exit_. So we can safely leak this Application object, and have it just + // get cleaned up normally when our process exits. + ::winrt::SampleApp::App a{ _app }; + ::winrt::detach_abi(a); +} diff --git a/scratch/ScratchIslandApp/WindowExe/SampleAppHost.h b/scratch/ScratchIslandApp/WindowExe/SampleAppHost.h new file mode 100644 index 000000000..226f90a4b --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleAppHost.h @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" + +#include "SampleIslandWindow.h" + +class SampleAppHost +{ +public: + SampleAppHost() noexcept; + virtual ~SampleAppHost(); + + void Initialize(); + +private: + std::unique_ptr _window; + winrt::SampleApp::App _app; + winrt::SampleApp::SampleAppLogic _logic; +}; diff --git a/scratch/ScratchIslandApp/WindowExe/SampleBaseWindow.h b/scratch/ScratchIslandApp/WindowExe/SampleBaseWindow.h new file mode 100644 index 000000000..5c25452b3 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleBaseWindow.h @@ -0,0 +1,228 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +// Custom window messages +#define CM_UPDATE_TITLE (WM_USER) + +#include + +template +class BaseWindow +{ +public: + virtual ~BaseWindow() = 0; + static T* GetThisFromHandle(HWND const window) noexcept + { + return reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); + } + + [[nodiscard]] static LRESULT __stdcall WndProc(HWND const window, UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept + { + WINRT_ASSERT(window); + + if (WM_NCCREATE == message) + { + auto cs = reinterpret_cast(lparam); + T* that = static_cast(cs->lpCreateParams); + WINRT_ASSERT(that); + WINRT_ASSERT(!that->_window); + that->_window = wil::unique_hwnd(window); + + return that->_OnNcCreate(wparam, lparam); + } + else if (T* that = GetThisFromHandle(window)) + { + return that->MessageHandler(message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); + } + + [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept + { + switch (message) + { + case WM_DPICHANGED: + { + return HandleDpiChange(_window.get(), wparam, lparam); + } + + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + + case WM_SIZE: + { + UINT width = LOWORD(lparam); + UINT height = HIWORD(lparam); + + switch (wparam) + { + case SIZE_MAXIMIZED: + [[fallthrough]]; + case SIZE_RESTORED: + if (_minimized) + { + _minimized = false; + OnRestore(); + } + + // We always need to fire the resize event, even when we're transitioning from minimized. + // We might be transitioning directly from minimized to maximized, and we'll need + // to trigger any size-related content changes. + OnResize(width, height); + break; + case SIZE_MINIMIZED: + if (!_minimized) + { + _minimized = true; + OnMinimize(); + } + break; + default: + // do nothing. + break; + } + break; + } + case CM_UPDATE_TITLE: + { + SetWindowTextW(_window.get(), _title.c_str()); + break; + } + } + + return DefWindowProc(_window.get(), message, wparam, lparam); + } + + // DPI Change handler. on WM_DPICHANGE resize the window + [[nodiscard]] LRESULT HandleDpiChange(const HWND hWnd, const WPARAM wParam, const LPARAM lParam) + { + _inDpiChange = true; + const HWND hWndStatic = GetWindow(hWnd, GW_CHILD); + if (hWndStatic != nullptr) + { + const UINT uDpi = HIWORD(wParam); + + // Resize the window + auto lprcNewScale = reinterpret_cast(lParam); + + SetWindowPos(hWnd, nullptr, lprcNewScale->left, lprcNewScale->top, lprcNewScale->right - lprcNewScale->left, lprcNewScale->bottom - lprcNewScale->top, SWP_NOZORDER | SWP_NOACTIVATE); + + _currentDpi = uDpi; + } + _inDpiChange = false; + return 0; + } + + virtual void OnResize(const UINT width, const UINT height) = 0; + virtual void OnMinimize() = 0; + virtual void OnRestore() = 0; + + RECT GetWindowRect() const noexcept + { + RECT rc = { 0 }; + ::GetWindowRect(_window.get(), &rc); + return rc; + } + + HWND GetHandle() const noexcept + { + return _window.get(); + } + + float GetCurrentDpiScale() const noexcept + { + const auto dpi = ::GetDpiForWindow(_window.get()); + const auto scale = static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI); + return scale; + } + + // Gets the physical size of the client area of the HWND in _window + SIZE GetPhysicalSize() const noexcept + { + RECT rect = {}; + GetClientRect(_window.get(), &rect); + const auto windowsWidth = rect.right - rect.left; + const auto windowsHeight = rect.bottom - rect.top; + return SIZE{ windowsWidth, windowsHeight }; + } + + // Gets the logical (in DIPs) size of a physical size specified by the parameter physicalSize + // Remarks: + // XAML coordinate system is always in Display Independent Pixels (a.k.a DIPs or Logical). However Win32 GDI (because of legacy reasons) + // in DPI mode "Per-Monitor and Per-Monitor (V2) DPI Awareness" is always in physical pixels. + // The formula to transform is: + // logical = (physical / dpi) + 0.5 // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 + // See also: + // https://docs.microsoft.com/en-us/windows/desktop/LearnWin32/dpi-and-device-independent-pixels + // https://docs.microsoft.com/en-us/windows/desktop/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness + winrt::Windows::Foundation::Size GetLogicalSize(const SIZE physicalSize) const noexcept + { + const auto scale = GetCurrentDpiScale(); + // 0.5 is to ensure that we pixel snap correctly at the edges, this is necessary with odd DPIs like 1.25, 1.5, 1, .75 + const auto logicalWidth = (physicalSize.cx / scale) + 0.5f; + const auto logicalHeight = (physicalSize.cy / scale) + 0.5f; + return winrt::Windows::Foundation::Size(logicalWidth, logicalHeight); + } + + winrt::Windows::Foundation::Size GetLogicalSize() const noexcept + { + return GetLogicalSize(GetPhysicalSize()); + } + + // Method Description: + // - Sends a message to our message loop to update the title of the window. + // Arguments: + // - newTitle: a string to use as the new title of the window. + // Return Value: + // - + void UpdateTitle(std::wstring_view newTitle) + { + _title = newTitle; + PostMessageW(_window.get(), CM_UPDATE_TITLE, 0, reinterpret_cast(nullptr)); + } + + // Method Description: + // Reset the current dpi of the window. This method is only called after we change the + // initial launch position. This makes sure the dpi is consistent with the monitor on which + // the window will launch + void RefreshCurrentDPI() + { + _currentDpi = GetDpiForWindow(_window.get()); + } + +protected: + using base_type = BaseWindow; + wil::unique_hwnd _window; + + unsigned int _currentDpi = 0; + bool _inDpiChange = false; + + std::wstring _title = L""; + + bool _minimized = false; + + // Method Description: + // - This method is called when the window receives the WM_NCCREATE message. + // Return Value: + // - The value returned from the window proc. + virtual [[nodiscard]] LRESULT _OnNcCreate(WPARAM wParam, LPARAM lParam) noexcept + { + SetWindowLongPtr(_window.get(), GWLP_USERDATA, reinterpret_cast(this)); + + EnableNonClientDpiScaling(_window.get()); + _currentDpi = GetDpiForWindow(_window.get()); + + return DefWindowProc(_window.get(), WM_NCCREATE, wParam, lParam); + }; +}; + +template +inline BaseWindow::~BaseWindow() +{ +} diff --git a/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.cpp b/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.cpp new file mode 100644 index 000000000..1c5cf10d0 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.cpp @@ -0,0 +1,220 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SampleIslandWindow.h" +#include "../types/inc/Viewport.hpp" +#include "resource.h" +#include "icon.h" + +extern "C" IMAGE_DOS_HEADER __ImageBase; + +using namespace winrt::Windows::UI; +using namespace winrt::Windows::UI::Composition; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Xaml::Hosting; +using namespace winrt::Windows::Foundation::Numerics; +using namespace ::Microsoft::Console::Types; + +#define XAML_HOSTING_WINDOW_CLASS_NAME L"SCRATCH_HOSTING_WINDOW_CLASS" + +SampleIslandWindow::SampleIslandWindow() noexcept : + _interopWindowHandle{ nullptr }, + _rootGrid{ nullptr }, + _source{ nullptr } +{ +} + +SampleIslandWindow::~SampleIslandWindow() +{ + _source.Close(); +} + +// Method Description: +// - Create the actual window that we'll use for the application. +// Arguments: +// - +// Return Value: +// - +void SampleIslandWindow::MakeWindow() noexcept +{ + WNDCLASS wc{}; + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hInstance = reinterpret_cast(&__ImageBase); + wc.lpszClassName = XAML_HOSTING_WINDOW_CLASS_NAME; + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.hIcon = LoadIconW(wc.hInstance, MAKEINTRESOURCEW(IDI_APPICON)); + RegisterClass(&wc); + WINRT_ASSERT(!_window); + + // Create the window with the default size here - During the creation of the + // window, the system will give us a chance to set its size in WM_CREATE. + // WM_CREATE will be handled synchronously, before CreateWindow returns. + WINRT_VERIFY(CreateWindowEx(0, + wc.lpszClassName, + L"ScratchApp", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + nullptr, + nullptr, + wc.hInstance, + this)); + + WINRT_ASSERT(_window); +} + +// Method Description: +// - Called when no tab is remaining to close the window. +// Arguments: +// - +// Return Value: +// - +void SampleIslandWindow::Close() +{ + PostQuitMessage(0); +} + +// Method Description: +// - Handles a WM_CREATE message. Calls our create callback, if one's been set. +// Arguments: +// - wParam: unused +// - lParam: the lParam of a WM_CREATE, which is a pointer to a CREATESTRUCTW +// Return Value: +// - +void SampleIslandWindow::_HandleCreateWindow(const WPARAM, const LPARAM lParam) noexcept +{ + // Get proposed window rect from create structure + CREATESTRUCTW* pcs = reinterpret_cast(lParam); + RECT rc; + rc.left = pcs->x; + rc.top = pcs->y; + rc.right = rc.left + pcs->cx; + rc.bottom = rc.top + pcs->cy; + + ShowWindow(_window.get(), SW_SHOW); + + UpdateWindow(_window.get()); + + UpdateWindowIconForActiveMetrics(_window.get()); +} + +void SampleIslandWindow::Initialize() +{ + const bool initialized = (_interopWindowHandle != nullptr); + + _source = DesktopWindowXamlSource{}; + + auto interop = _source.as(); + winrt::check_hresult(interop->AttachToWindow(_window.get())); + + // stash the child interop handle so we can resize it when the main hwnd is resized + interop->get_WindowHandle(&_interopWindowHandle); + + _rootGrid = winrt::Windows::UI::Xaml::Controls::Grid(); + _source.Content(_rootGrid); +} + +void SampleIslandWindow::OnSize(const UINT width, const UINT height) +{ + // update the interop window size + SetWindowPos(_interopWindowHandle, nullptr, 0, 0, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE); + + if (_rootGrid) + { + const auto size = GetLogicalSize(); + _rootGrid.Width(size.Width); + _rootGrid.Height(size.Height); + } +} + +[[nodiscard]] LRESULT SampleIslandWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept +{ + switch (message) + { + case WM_CREATE: + { + _HandleCreateWindow(wparam, lparam); + return 0; + } + case WM_SETFOCUS: + { + if (_interopWindowHandle != nullptr) + { + // send focus to the child window + SetFocus(_interopWindowHandle); + return 0; + } + break; + } + case WM_MENUCHAR: + { + // GH#891: return this LRESULT here to prevent the app from making a + // bell when alt+key is pressed. A menu is active and the user presses a + // key that does not correspond to any mnemonic or accelerator key, + return MAKELRESULT(0, MNC_CLOSE); + } + case WM_THEMECHANGED: + UpdateWindowIconForActiveMetrics(_window.get()); + return 0; + } + + return base_type::MessageHandler(message, wparam, lparam); +} + +// Method Description: +// - Called when the window has been resized (or maximized) +// Arguments: +// - width: the new width of the window _in pixels_ +// - height: the new height of the window _in pixels_ +void SampleIslandWindow::OnResize(const UINT width, const UINT height) +{ + if (_interopWindowHandle) + { + OnSize(width, height); + } +} + +// Method Description: +// - Called when the window is minimized to the taskbar. +void SampleIslandWindow::OnMinimize() +{ +} + +// Method Description: +// - Called when the window is restored from having been minimized. +void SampleIslandWindow::OnRestore() +{ +} + +void SampleIslandWindow::SetContent(winrt::Windows::UI::Xaml::UIElement content) +{ + _rootGrid.Children().Clear(); + _rootGrid.Children().Append(content); +} + +void SampleIslandWindow::OnAppInitialized() +{ + // Do a quick resize to force the island to paint + const auto size = GetPhysicalSize(); + OnSize(size.cx, size.cy); +} + +// Method Description: +// - Called when the app wants to change its theme. We'll update the root UI +// element of the entire XAML tree, so that all UI elements get the theme +// applied. +// Arguments: +// - arg: the ElementTheme to use as the new theme for the UI +// Return Value: +// - +void SampleIslandWindow::OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) +{ + _rootGrid.RequestedTheme(requestedTheme); + // Invalidate the window rect, so that we'll repaint any elements we're + // drawing ourselves to match the new theme + ::InvalidateRect(_window.get(), nullptr, false); +} diff --git a/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.h b/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.h new file mode 100644 index 000000000..14a62f13c --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleIslandWindow.h @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SampleBaseWindow.h" +#include "../../../src/cascadia/inc/cppwinrt_utils.h" + +class SampleIslandWindow : + public BaseWindow +{ +public: + SampleIslandWindow() noexcept; + virtual ~SampleIslandWindow() override; + + virtual void MakeWindow() noexcept; + void Close(); + + virtual void OnSize(const UINT width, const UINT height); + + [[nodiscard]] virtual LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam) noexcept override; + void OnResize(const UINT width, const UINT height) override; + void OnMinimize() override; + void OnRestore() override; + virtual void OnAppInitialized(); + virtual void SetContent(winrt::Windows::UI::Xaml::UIElement content); + virtual void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme); + + virtual void Initialize(); + +protected: + void ForceResize() + { + // Do a quick resize to force the island to paint + const auto size = GetPhysicalSize(); + OnSize(size.cx, size.cy); + } + + HWND _interopWindowHandle; + + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource _source; + + winrt::Windows::UI::Xaml::Controls::Grid _rootGrid; + + void _HandleCreateWindow(const WPARAM wParam, const LPARAM lParam) noexcept; +}; diff --git a/scratch/ScratchIslandApp/WindowExe/SampleMain.cpp b/scratch/ScratchIslandApp/WindowExe/SampleMain.cpp new file mode 100644 index 000000000..4c09f262c --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/SampleMain.cpp @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "SampleAppHost.h" +#include "resource.h" +#include "../types/inc/User32Utils.hpp" +#include + +using namespace winrt; +using namespace winrt::Windows::UI; +using namespace winrt::Windows::UI::Composition; +using namespace winrt::Windows::UI::Xaml::Hosting; +using namespace winrt::Windows::Foundation::Numerics; + +// Routine Description: +// - Takes an image architecture and locates a string resource that maps to that architecture. +// Arguments: +// - imageArchitecture - An IMAGE_FILE_MACHINE architecture enum value +// - See https://docs.microsoft.com/en-us/windows/win32/sysinfo/image-file-machine-constants +// Return Value: +// - A string value representing the human-readable name of this architecture. +static std::wstring ImageArchitectureToString(USHORT imageArchitecture) +{ + // clang-format off + const auto id = imageArchitecture == IMAGE_FILE_MACHINE_I386 ? IDS_X86_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_AMD64 ? IDS_AMD64_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_ARM64 ? IDS_ARM64_ARCHITECTURE : + imageArchitecture == IMAGE_FILE_MACHINE_ARM ? IDS_ARM_ARCHITECTURE : + IDS_UNKNOWN_ARCHITECTURE; + // clang-format on + + return GetStringResource(id); +} + +// Routine Description: +// - Blocks the user from launching the application with a message box dialog and early exit +// if the process architecture doesn't match the system platform native architecture. +// - This is because the conhost.exe must match the condrv.sys on the system and the PTY +// infrastructure that powers everything won't work if we have a mismatch. +// Arguments: +// - +// Return Value: +// - +static void EnsureNativeArchitecture() +{ + USHORT processMachine{}; + USHORT nativeMachine{}; + THROW_IF_WIN32_BOOL_FALSE(IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine)); + if (processMachine != IMAGE_FILE_MACHINE_UNKNOWN && processMachine != nativeMachine) + { + const auto formatPattern = GetStringResource(IDS_ERROR_ARCHITECTURE_FORMAT); + + const auto nativeArchitecture = ImageArchitectureToString(nativeMachine); + const auto processArchitecture = ImageArchitectureToString(processMachine); + + auto buffer{ wil::str_printf(formatPattern.data(), nativeArchitecture.data(), processArchitecture.data()) }; + + MessageBoxW(nullptr, + buffer.data(), + GetStringResource(IDS_ERROR_DIALOG_TITLE).data(), + MB_OK | MB_ICONERROR); + + ExitProcess(0); + } +} + +static bool _messageIsF7Keypress(const MSG& message) +{ + return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7; +} +static bool _messageIsAltKeyup(const MSG& message) +{ + return (message.message == WM_KEYUP || message.message == WM_SYSKEYUP) && message.wParam == VK_MENU; +} + +int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int) +{ + // If Terminal is spawned by a shortcut that requests that it run in a new process group + // while attached to a console session, that request is nonsense. That request will, however, + // cause WT to start with Ctrl-C disabled. This wouldn't matter, because it's a Windows-subsystem + // application. Unfortunately, that state is heritable. In short, if you start WT using cmd in + // a weird way, ^C stops working _inside_ the terminal. Mad. + SetConsoleCtrlHandler(NULL, FALSE); + + // Block the user from starting if they launched the incorrect architecture version of the project. + // This should only be applicable to developer versions. The package installation process + // should choose and install the correct one from the bundle. + EnsureNativeArchitecture(); + + // Make sure to call this so we get WM_POINTER messages. + EnableMouseInPointer(true); + + // !!! LOAD BEARING !!! + // We must initialize the main thread as a single-threaded apartment before + // constructing any Xaml objects. Failing to do so will cause some issues + // in accessibility somewhere down the line when a UIAutomation object will + // be queried on the wrong thread at the wrong time. + // We used to initialize as STA only _after_ initializing the application + // host, which loaded the settings. The settings needed to be loaded in MTA + // because we were using the Windows.Storage APIs. Since we're no longer + // doing that, we can safely init as STA before any WinRT dispatches. + winrt::init_apartment(winrt::apartment_type::single_threaded); + + // Create the SampleAppHost object, which will create both the window and the + // Terminal App. This MUST BE constructed before the Xaml manager as TermApp + // provides an implementation of Windows.UI.Xaml.Application. + SampleAppHost host; + + // Initialize the xaml content. This must be called AFTER the + // WindowsXamlManager is initialized. + host.Initialize(); + + MSG message; + + while (GetMessage(&message, nullptr, 0, 0)) + { + TranslateMessage(&message); + DispatchMessage(&message); + } + return 0; +} diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.def b/scratch/ScratchIslandApp/WindowExe/WindowExe.def new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.def @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.manifest b/scratch/ScratchIslandApp/WindowExe/WindowExe.manifest new file mode 100644 index 000000000..9d91e543d --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + PerMonitorV2 + true + + + diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.rc b/scratch/ScratchIslandApp/WindowExe/WindowExe.rc new file mode 100644 index 000000000..b11a8c88d --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.rc @@ -0,0 +1,97 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. + +IDI_APPICON ICON "..\\..\\..\\res\\terminal.ico" +IDI_APPICON_HC_BLACK ICON "..\\..\\..\\res\\terminal\\images\\terminal_contrast-black.ico" +IDI_APPICON_HC_WHITE ICON "..\\..\\..\\res\\terminal\\images\\terminal_contrast-white.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ERROR_DIALOG_TITLE "Error" + IDS_HELP_DIALOG_TITLE "Help" + IDS_ERROR_ARCHITECTURE_FORMAT + "This sample is designed to run on your system's native architecture (%s).\nYou are currently using the %s version.\n\nPlease use the version of this sample that matches your system's native architecture." + IDS_X86_ARCHITECTURE "i386" +END + +STRINGTABLE +BEGIN + IDS_AMD64_ARCHITECTURE "AMD64" + IDS_ARM64_ARCHITECTURE "ARM64" + IDS_ARM_ARCHITECTURE "ARM" + IDS_UNKNOWN_ARCHITECTURE "Unknown" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj new file mode 100644 index 000000000..edd372c2f --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/WindowExe.vcxproj @@ -0,0 +1,220 @@ + + + + + + {b4427499-9fde-4208-b456-5bc580637633} + Win32Proj + WindowExe + WindowExe + WindowExe + Application + false + Windows Store + true + false + Windows + + + + + + + + true + + + + + + $(OpenConsoleDir)\src\inc;$(OpenConsoleDir)\dep;$(OpenConsoleDir)\dep\Console;$(OpenConsoleDir)\dep\Win32K;$(OpenConsoleDir)\dep\gsl\include;%(AdditionalIncludeDirectories); + + + %(AdditionalDependencies) + + + + true + true + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + + + + + $(OpenConsoleCommonOutDir)TerminalCore\Microsoft.Terminal.Core.winmd + true + true + true + + + $(OpenConsoleCommonOutDir)TerminalConnection\Microsoft.Terminal.TerminalConnection.winmd + $(OpenConsoleCommonOutDir)TerminalConnection\TerminalConnection.dll + true + true + true + + + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.winmd + $(OpenConsoleCommonOutDir)Microsoft.Terminal.Control\Microsoft.Terminal.Control.dll + true + true + true + + + + + + + + + WindowsLocalDebugger + + + + + + + + + + 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}. + + + + + + + + + + <_ContinueOnError Condition="'$(BuildingProject)' == 'true'">true + <_ContinueOnError Condition="'$(BuildingProject)' != 'true'">false + + + + + + + + + + + + + x86 + $(Platform) + + + + + <_OpenConsoleVCLibToCopy Include="$(VCToolsRedistInstallDir)\$(ReasonablePlatform)\Microsoft.VC142.CRT\*.dll" /> + + + $(ProjectName) + BuiltProjectOutputGroup + %(Filename)%(Extension) + + + + + + + + + <_TerminalConnectionDlls Include="$(OpenConsoleCommonOutDir)\TerminalConnection\*.dll" /> + + + $(ProjectName) + BuiltProjectOutputGroup + %(Filename)%(Extension) + + + + + + + <_WindowsTerminalExe Include="$(OpenConsoleCommonOutDir)\WindowsTerminal\*.exe" /> + + + $(ProjectName) + BuiltProjectOutputGroup + %(Filename)%(Extension) + + + + + + + + + + + + diff --git a/scratch/ScratchIslandApp/WindowExe/icon.cpp b/scratch/ScratchIslandApp/WindowExe/icon.cpp new file mode 100644 index 000000000..ca194de06 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/icon.cpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include "resource.h" + +static int _GetActiveAppIconResource() +{ + auto iconResource{ IDI_APPICON }; + + HIGHCONTRASTW hcInfo{}; + hcInfo.cbSize = sizeof(hcInfo); + + if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(hcInfo), &hcInfo, 0)) + { + if (WI_IsFlagSet(hcInfo.dwFlags, HCF_HIGHCONTRASTON)) + { + iconResource = IDI_APPICON_HC_BLACK; + + if (0x00FFFFFF == GetSysColor(COLOR_WINDOW)) // white window color == white high contrast + { + iconResource = IDI_APPICON_HC_WHITE; + } + } + } + + return iconResource; +} + +void UpdateWindowIconForActiveMetrics(HWND window) +{ + auto iconResource{ MAKEINTRESOURCEW(_GetActiveAppIconResource()) }; + + // These handles are loaded with LR_SHARED, so they are safe to "leak". + HANDLE smallIcon{ LoadImageW(wil::GetModuleInstanceHandle(), iconResource, IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED) }; + LOG_LAST_ERROR_IF_NULL(smallIcon); + + HANDLE largeIcon{ LoadImageW(wil::GetModuleInstanceHandle(), iconResource, IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_SHARED) }; + LOG_LAST_ERROR_IF_NULL(largeIcon); + + if (smallIcon) + { + SendMessageW(window, WM_SETICON, ICON_SMALL, reinterpret_cast(smallIcon)); + } + if (largeIcon) + { + SendMessageW(window, WM_SETICON, ICON_BIG, reinterpret_cast(largeIcon)); + } +} diff --git a/scratch/ScratchIslandApp/WindowExe/icon.h b/scratch/ScratchIslandApp/WindowExe/icon.h new file mode 100644 index 000000000..5e418fc28 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/icon.h @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +void UpdateWindowIconForActiveMetrics(HWND window); diff --git a/scratch/ScratchIslandApp/WindowExe/packages.config b/scratch/ScratchIslandApp/WindowExe/packages.config new file mode 100644 index 000000000..409baff13 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/packages.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/scratch/ScratchIslandApp/WindowExe/pch.cpp b/scratch/ScratchIslandApp/WindowExe/pch.cpp new file mode 100644 index 000000000..398a99f66 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/pch.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" diff --git a/scratch/ScratchIslandApp/WindowExe/pch.h b/scratch/ScratchIslandApp/WindowExe/pch.h new file mode 100644 index 000000000..de08907f8 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/pch.h @@ -0,0 +1,82 @@ +/*++ +Copyright (c) Microsoft Corporation +Licensed under the MIT license. + +Module Name: +- pch.h + +Abstract: +- Contains external headers to include in the precompile phase of console build process. +- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building). +--*/ + +#pragma once + +// Ignore checked iterators warning from VC compiler. +#define _SCL_SECURE_NO_WARNINGS + +// Block minwindef.h min/max macros to prevent conflict +#define NOMINMAX + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +#include + +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) + +#include +#include +#include +#include +#include +#include +#include + +// Manually include til after we include Windows.Foundation to give it winrt superpowers +#define BLOCK_TIL +#include "../inc/LibraryIncludes.h" + +// This is inexplicable, but for whatever reason, cppwinrt conflicts with the +// SDK definition of this function, so the only fix is to undef it. +// from WinBase.h +// Windows::UI::Xaml::Media::Animation::IStoryboard::GetCurrentTime +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + +#include + +// Needed just for XamlIslands to work at all: +#include +#include +#include +#include + +// Additional headers for various xaml features. We need: +// * Core so we can resume_foreground with CoreDispatcher +// * Controls for grid +// * Media for ScaleTransform +#include +#include +#include + +#include + +#include +#include + +// Including TraceLogging essentials for the binary +#include +#include +TRACELOGGING_DECLARE_PROVIDER(g_hWindowsTerminalProvider); +#include +#include + +// For commandline argument processing +#include +#include +#include +#include "til.h" diff --git a/scratch/ScratchIslandApp/WindowExe/resource.h b/scratch/ScratchIslandApp/WindowExe/resource.h new file mode 100644 index 000000000..7e2918600 --- /dev/null +++ b/scratch/ScratchIslandApp/WindowExe/resource.h @@ -0,0 +1,27 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by WindowsTerminal.rc +// +#define IDI_APPICON 101 +#define IDI_APPICON_HC_BLACK 102 +#define IDI_APPICON_HC_WHITE 103 + +#define IDS_ERROR_DIALOG_TITLE 105 +#define IDS_HELP_DIALOG_TITLE 106 +#define IDS_ERROR_ARCHITECTURE_FORMAT 110 +#define IDS_X86_ARCHITECTURE 111 +#define IDS_AMD64_ARCHITECTURE 112 +#define IDS_ARM64_ARCHITECTURE 113 +#define IDS_ARM_ARCHITECTURE 114 +#define IDS_UNKNOWN_ARCHITECTURE 115 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif From 0788042540daf309cda3c192c4827204f28d8f48 Mon Sep 17 00:00:00 2001 From: Casper Verhaar Date: Thu, 10 Jun 2021 20:08:47 +0200 Subject: [PATCH 06/12] Replace feedback button with command palette button in dropdown (#10297) Replaces the "feedback" button in the dropdown menu with a "command palette" button. * [x] Closes #10171 ![image](https://user-images.githubusercontent.com/34449626/120193627-eca45e80-c21c-11eb-83bb-40b6539bce13.png) --- .../Resources/en-US/Resources.resw | 3 ++ src/cascadia/TerminalApp/TerminalPage.cpp | 39 ++++++++++--------- src/cascadia/TerminalApp/TerminalPage.h | 2 +- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index 27b1460a9..2e35662e3 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -634,4 +634,7 @@ Restore Down + + Command Palette + \ No newline at end of file diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 18afb5962..103c4bb45 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -553,7 +553,7 @@ namespace winrt::TerminalApp::implementation // attaches it to the button. Populates the flyout with one entry per // Profile, displaying the profile's name. Clicking each flyout item will // open a new tab with that profile. - // Below the profiles are the static menu items: settings, feedback + // Below the profiles are the static menu items: settings, command palette void TerminalPage::_CreateNewTabFlyout() { auto newTabFlyout = WUX::Controls::MenuFlyout{}; @@ -703,17 +703,23 @@ namespace winrt::TerminalApp::implementation _SetAcceleratorForMenuItem(settingsItem, settingsKeyChord); } - // Create the feedback button. - auto feedbackFlyout = WUX::Controls::MenuFlyoutItem{}; - feedbackFlyout.Text(RS_(L"FeedbackMenuItem")); + // Create the command palette button. + auto commandPaletteFlyout = WUX::Controls::MenuFlyoutItem{}; + commandPaletteFlyout.Text(RS_(L"CommandPaletteMenuItem")); - WUX::Controls::FontIcon feedbackIcon{}; - feedbackIcon.Glyph(L"\xE939"); - feedbackIcon.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); - feedbackFlyout.Icon(feedbackIcon); + WUX::Controls::FontIcon commandPaletteIcon{}; + commandPaletteIcon.Glyph(L"\xE945"); + commandPaletteIcon.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + commandPaletteFlyout.Icon(commandPaletteIcon); - feedbackFlyout.Click({ this, &TerminalPage::_FeedbackButtonOnClick }); - newTabFlyout.Items().Append(feedbackFlyout); + commandPaletteFlyout.Click({ this, &TerminalPage::_CommandPaletteButtonOnClick }); + newTabFlyout.Items().Append(commandPaletteFlyout); + + const auto commandPaletteKeyChord{ actionMap.GetKeyBindingForAction(ShortcutAction::ToggleCommandPalette) }; + if (commandPaletteKeyChord) + { + _SetAcceleratorForMenuItem(commandPaletteFlyout, commandPaletteKeyChord); + } } // Create the about button. @@ -884,15 +890,12 @@ namespace winrt::TerminalApp::implementation } // Method Description: - // - Called when the feedback button is clicked. Launches github in your - // default browser, navigated to the "issues" page of the Terminal repo. - void TerminalPage::_FeedbackButtonOnClick(const IInspectable&, - const RoutedEventArgs&) + // - Called when the command palette button is clicked. Opens the command palette. + void TerminalPage::_CommandPaletteButtonOnClick(const IInspectable&, + const RoutedEventArgs&) { - const auto feedbackUriValue = RS_(L"FeedbackUriValue"); - winrt::Windows::Foundation::Uri feedbackUri{ feedbackUriValue }; - - winrt::Windows::System::Launcher::LaunchUriAsync(feedbackUri); + CommandPalette().EnableCommandPaletteMode(CommandPaletteLaunchMode::Action); + CommandPalette().Visibility(Visibility::Visible); } // Method Description: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 9de1ded9b..500572dd0 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -196,7 +196,7 @@ namespace winrt::TerminalApp::implementation bool _displayingCloseDialog{ false }; void _SettingsButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); - void _FeedbackButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); + void _CommandPaletteButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _AboutButtonOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); void _ThirdPartyNoticesOnClick(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); From 8157605058ccb5a4dbfecb40d0e6a7cff02f463e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luan=20Vitor=20Simi=C3=A3o=20Oliveira?= Date: Thu, 10 Jun 2021 15:10:48 -0300 Subject: [PATCH 07/12] profile.schema: add missing actions and commands keys (#10384) Add missing keys to the schema: - globalSummon (and args: desktop, monitor, name, dropdownDuration, toggleVisibility) - quakeMode - iterateOn - commands Also enforces required keys for commands and deprecates "keybindings" ## PR Checklist * [x] Closes #7443 * [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA * [x] Schema updated. ## Detailed Description of the Pull Request / Additional comments There were some other pending keys mentioned on the issue, but I don't think they are pending anymore. ## Validation Steps Performed Changed the `"$schema"` value in my settings.json to point to the edited one. --- doc/cascadia/profiles.schema.json | 101 ++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index c5cb67540..c17f5662e 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -191,6 +191,7 @@ "find", "findMatch", "focusPane", + "globalSummon", "identifyWindow", "identifyWindows", "moveFocus", @@ -206,6 +207,7 @@ "prevTab", "renameTab", "openTabRenamer", + "quakeMode", "resetFontSize", "resizePane", "renameWindow", @@ -817,6 +819,63 @@ } ] }, + "GlobalSummonAction": { + "description": "This is a special action that works globally in the OS, rather than only in the context of the terminal window. When pressed, this action will summon the terminal window.", + "allOf": [ + { "$ref": "#/definitions/ShortcutAction" }, + { + "properties": { + "action": { "type": "string", "pattern": "globalSummon" }, + "desktop": { + "type": "string", + "default": "toCurrent", + "description": "This controls how the terminal should interact with virtual desktops.\n- \"any\": Leave the window on whichever desktop it's already on - will switch to that desktop as the window is activated.\n- \"toCurrent\" (default): Move the window to the current virtual desktop.\n- \"onCurrent\": Only summon the window if it's already on the current virtual desktop. ", + "enum": [ + "any", + "toCurrent", + "onCurrent" + ] + }, + "monitor": { + "type": "string", + "default": "toMouse", + "description": "This controls the monitor that the window will be summoned from/to.\n- \"any\": Summon the most recently used window, regardless of which monitor it's currently on.\n- \"toCurrent\": Summon the most recently used window to the monitor with the current foreground window.\n- \"toMouse\" (default): Summon the most recently used window to the monitor where the mouse cursor is.", + "enum": [ + "any", + "toCurrent", + "toMouse" + ] + }, + "name": { + "type": "string", + "description": "When provided, summon the window whose name or ID matches the given name value. If no such window exists, then create a new window with that name." + }, + "dropdownDuration": { + "type": "number", + "minimum": 0, + "default": 0, + "description": "When provided with a positive number, \"slide\" the window in from the top of the screen using an animation that lasts dropdownDuration milliseconds." + }, + "toggleVisibility": { + "type": "boolean", + "default": true, + "description": "When true, pressing the assigned keys for this action will dismiss (minimize) the window when the window is currently the foreground window." + } + } + } + ] + }, + "QuakeModeAction": { + "description": "This action is a special variation of the globalSummon action. It specifically summons a window called \"_quake\". If you would like to change the behavior of the quakeMode action, we recommended creating a new globalSummon entry.", + "allOf": [ + { "$ref": "#/definitions/ShortcutAction" }, + { + "properties": { + "action": { "type": "string", "pattern": "quakeMode" } + } + } + ] + }, "Keybinding": { "additionalProperties": false, "properties": { @@ -848,6 +907,8 @@ { "$ref": "#/definitions/RenameTabAction" }, { "$ref": "#/definitions/RenameWindowAction" }, { "$ref": "#/definitions/FocusPaneAction" }, + { "$ref": "#/definitions/GlobalSummonAction" }, + { "$ref": "#/definitions/QuakeModeAction" }, { "type": "null" } ] }, @@ -868,16 +929,38 @@ }, "icon": { "$ref": "#/definitions/Icon" }, "name": { - "description": "The name that will appear in the command palette. If one isn't provided, the terminal will attempt to automatically generate a name.", + "description": "The name that will appear in the command palette. If one isn't provided, the terminal will attempt to automatically generate a name.\nIf name is a string, it will be the name of the command.\nIf name is a object, the key property of the object will be used to lookup a localized string resource for the command", + "properties": { + "key": { + "type": "string" + } + }, "type": [ "string", + "object", "null" ] + }, + "iterateOn": { + "type": "string", + "description": "Used to create iterable commands based on other objects in your settings. Possible values:\n- \"profiles\" \n- \"schemes\"", + "enum": [ + "profiles", + "schemes" + ] + }, + "commands": { + "description": "List of commands to execute", + "items": { + "$ref": "#/definitions/Keybinding/properties/command" + }, + "minItems": 1, + "type": "array" } }, - "required": [ - "command", - "keys" + "anyOf": [ + {"required": ["name","commands"]}, + {"required": ["command"]} ], "type": "object" }, @@ -1011,8 +1094,16 @@ "type": [ "integer", "string" ], "deprecated": true }, + "actions": { + "description": "Properties are specific to each custom action.", + "items": { + "$ref": "#/definitions/Keybinding" + }, + "type": "array" + }, "keybindings": { - "description": "Properties are specific to each custom key binding.", + "description": "[deprecated] Use actions instead.", + "deprecated": true, "items": { "$ref": "#/definitions/Keybinding" }, From 9294ecc8e52a61bb0d47b56a0a5e3c42734427ba Mon Sep 17 00:00:00 2001 From: Carlos Zamora Date: Thu, 10 Jun 2021 14:25:27 -0400 Subject: [PATCH 08/12] Bugfix: serialize iterable commands (#10373) ## Summary of the Pull Request Fixes a bug where top-level iterable commands were not serialized. ## PR Checklist * [X] Closes #10365 * [X] Tests added/passed ## Detailed Description of the Pull Request / Additional comments - `Command::ToJson`: - iterable commands deserve the same treatment as nested commands - `ActionMap`: - Similar to how we store nested commands, iterable commands need to be handled separately from standard commands. Then, when generating the name map, we make sure we export the iterable commands at the same time we export the nested commands. --- .../SerializationTests.cpp | 23 +++++++++++ .../TerminalSettingsModel/ActionMap.cpp | 33 +++++++++++++--- .../TerminalSettingsModel/ActionMap.h | 3 +- .../ActionMapSerialization.cpp | 38 +++++++++++-------- .../TerminalSettingsModel/Command.cpp | 6 +-- 5 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp index 97251b8b2..164ac0bb8 100644 --- a/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp +++ b/src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp @@ -229,10 +229,12 @@ namespace SettingsModelLocalTests void SerializationTests::Actions() { + // simple command const std::string actionsString1{ R"([ { "command": "paste" } ])" }; + // complex command const std::string actionsString2A{ R"([ { "command": { "action": "setTabColor" } } ])" }; @@ -244,29 +246,35 @@ namespace SettingsModelLocalTests { "command": { "action": "copy", "singleLine": true, "copyFormatting": "html" } } ])" }; + // simple command with key chords const std::string actionsString3{ R"([ { "command": "toggleAlwaysOnTop", "keys": "ctrl+a" }, { "command": "toggleAlwaysOnTop", "keys": "ctrl+b" } ])" }; + // complex command with key chords const std::string actionsString4{ R"([ { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+c" }, { "command": { "action": "adjustFontSize", "delta": 1 }, "keys": "ctrl+d" } ])" }; + // command with name and icon and multiple key chords const std::string actionsString5{ R"([ { "icon": "image.png", "name": "Scroll To Top Name", "command": "scrollToTop", "keys": "ctrl+e" }, { "command": "scrollToTop", "keys": "ctrl+f" } ])" }; + // complex command with new terminal args const std::string actionsString6{ R"([ { "command": { "action": "newTab", "index": 0 }, "keys": "ctrl+g" }, ])" }; + // complex command with meaningful null arg const std::string actionsString7{ R"([ { "command": { "action": "renameWindow", "name": null }, "keys": "ctrl+h" } ])" }; + // nested command const std::string actionsString8{ R"([ { "name": "Change font size...", @@ -278,6 +286,7 @@ namespace SettingsModelLocalTests } ])" }; + // iterable command const std::string actionsString9A{ R"([ { "name": "New tab", @@ -330,7 +339,20 @@ namespace SettingsModelLocalTests "name": "Send Input (Evil) ..." } ])"" }; + const std::string actionsString9D{ R""([ + { + "command": + { + "action": "newTab", + "profile": "${profile.name}" + }, + "icon": "${profile.icon}", + "iterateOn": "profiles", + "name": "${profile.name}: New tab" + } + ])"" }; + // unbound command const std::string actionsString10{ R"([ { "command": "unbound", "keys": "ctrl+c" } ])" }; @@ -365,6 +387,7 @@ namespace SettingsModelLocalTests RoundtripTest(actionsString9A); RoundtripTest(actionsString9B); RoundtripTest(actionsString9C); + RoundtripTest(actionsString9D); Log::Comment(L"unbound command"); RoundtripTest(actionsString10); diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 433535dc5..a9ab094e7 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -29,7 +29,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } ActionMap::ActionMap() : - _NestedCommands{ single_threaded_map() } + _NestedCommands{ single_threaded_map() }, + _IterableCommands{ single_threaded_vector() } { } @@ -87,7 +88,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { // populate _NameMapCache std::unordered_map nameMap{}; - _PopulateNameMapWithNestedCommands(nameMap); + _PopulateNameMapWithSpecialCommands(nameMap); _PopulateNameMapWithStandardCommands(nameMap); _NameMapCache = single_threaded_map(std::move(nameMap)); @@ -96,18 +97,19 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation } // Method Description: - // - Populates the provided nameMap with all of our nested commands and our parents nested commands - // - Performs a top-down approach by going to the root first, then recursively adding the nested commands layer-by-layer + // - Populates the provided nameMap with all of our special commands and our parent's special commands. + // - Special commands include nested and iterable commands. + // - Performs a top-down approach by going to the root first, then recursively adding the nested commands layer-by-layer. // Arguments: // - nameMap: the nameMap we're populating. This maps the name (hstring) of a command to the command itself. - void ActionMap::_PopulateNameMapWithNestedCommands(std::unordered_map& nameMap) const + void ActionMap::_PopulateNameMapWithSpecialCommands(std::unordered_map& nameMap) const { // Update NameMap with our parents. // Starting with this means we're doing a top-down approach. FAIL_FAST_IF(_parents.size() > 1); for (const auto& parent : _parents) { - parent->_PopulateNameMapWithNestedCommands(nameMap); + parent->_PopulateNameMapWithSpecialCommands(nameMap); } // Add NestedCommands to NameMap _after_ we handle our parents. @@ -125,6 +127,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation nameMap.erase(name); } } + + // Add IterableCommands to NameMap + for (const auto& cmd : _IterableCommands) + { + nameMap.insert_or_assign(cmd.Name(), cmd); + } } // Method Description: @@ -296,6 +304,12 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation actionMap->_NestedCommands.Insert(name, *(get_self(cmd)->Copy())); } + // copy _IterableCommands + for (const auto& cmd : _IterableCommands) + { + actionMap->_IterableCommands.Append(*(get_self(cmd)->Copy())); + } + // repeat this for each of our parents FAIL_FAST_IF(_parents.size() > 1); for (const auto& parent : _parents) @@ -336,6 +350,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation return; } + // Handle iterable commands + if (cmdImpl->IterateOn() != ExpandCommandType::None) + { + _IterableCommands.Append(cmd); + return; + } + // General Case: // Add the new command to the KeyMap. // This map directs you to an entry in the ActionMap. diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.h b/src/cascadia/TerminalSettingsModel/ActionMap.h index e33638cff..3435d120c 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.h +++ b/src/cascadia/TerminalSettingsModel/ActionMap.h @@ -81,7 +81,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation std::optional _GetActionByID(const InternalActionID actionID) const; std::optional _GetActionByKeyChordInternal(Control::KeyChord const& keys) const; - void _PopulateNameMapWithNestedCommands(std::unordered_map& nameMap) const; + void _PopulateNameMapWithSpecialCommands(std::unordered_map& nameMap) const; void _PopulateNameMapWithStandardCommands(std::unordered_map& nameMap) const; void _PopulateKeyBindingMapWithStandardCommands(std::unordered_map& keyBindingsMap, std::unordered_set& unboundKeys) const; std::vector _GetCumulativeActions() const noexcept; @@ -94,6 +94,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Windows::Foundation::Collections::IMap _GlobalHotkeysCache{ nullptr }; Windows::Foundation::Collections::IMap _KeyBindingMapCache{ nullptr }; Windows::Foundation::Collections::IMap _NestedCommands{ nullptr }; + Windows::Foundation::Collections::IVector _IterableCommands{ nullptr }; std::unordered_map _KeyMap; std::unordered_map _ActionMap; diff --git a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp index 1358c7865..5956bf757 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMapSerialization.cpp @@ -60,34 +60,40 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { Json::Value actionList{ Json::ValueType::arrayValue }; - // Serialize all standard Command objects in the current layer - for (const auto& [_, cmd] : _ActionMap) - { - // Command serializes to an array of JSON objects. - // This is because a Command may have multiple key chords associated with it. - // The name and icon are only serialized in the first object. - // Example: - // { "name": "Custom Copy", "command": "copy", "keys": "ctrl+c" } - // { "command": "copy", "keys": "ctrl+shift+c" } - // { "command": "copy", "keys": "ctrl+ins" } + // Command serializes to an array of JSON objects. + // This is because a Command may have multiple key chords associated with it. + // The name and icon are only serialized in the first object. + // Example: + // { "name": "Custom Copy", "command": "copy", "keys": "ctrl+c" } + // { "command": "copy", "keys": "ctrl+shift+c" } + // { "command": "copy", "keys": "ctrl+ins" } + auto toJson = [&actionList](const Model::Command& cmd) { const auto cmdImpl{ winrt::get_self(cmd) }; const auto& cmdJsonArray{ cmdImpl->ToJson() }; for (const auto& cmdJson : cmdJsonArray) { actionList.append(cmdJson); } + }; + + // Serialize all standard Command objects in the current layer + for (const auto& [_, cmd] : _ActionMap) + { + toJson(cmd); } // Serialize all nested Command objects added in the current layer for (const auto& [_, cmd] : _NestedCommands) { - const auto cmdImpl{ winrt::get_self(cmd) }; - const auto& cmdJsonArray{ cmdImpl->ToJson() }; - for (const auto& cmdJson : cmdJsonArray) - { - actionList.append(cmdJson); - } + toJson(cmd); } + + // Serialize all iterable Command objects added in the current layer + for (const auto& cmd : _IterableCommands) + { + toJson(cmd); + } + return actionList; } diff --git a/src/cascadia/TerminalSettingsModel/Command.cpp b/src/cascadia/TerminalSettingsModel/Command.cpp index ee560b78d..dbdfd6e5c 100644 --- a/src/cascadia/TerminalSettingsModel/Command.cpp +++ b/src/cascadia/TerminalSettingsModel/Command.cpp @@ -394,10 +394,10 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { Json::Value cmdList{ Json::ValueType::arrayValue }; - if (_nestedCommand) + if (_nestedCommand || _IterateOn != ExpandCommandType::None) { - // handle nested command - // For nested commands, we can trust _originalJson to be correct. + // handle special commands + // For these, we can trust _originalJson to be correct. // In fact, we _need_ to use it here because we don't actually deserialize `iterateOn` // until we expand the command. cmdList.append(_originalJson); From 31a39b3b120018b59f37b558930968da67a5a239 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 10 Jun 2021 18:09:52 -0500 Subject: [PATCH 09/12] Add support for branch- and branding-based feature flagging (#10361) This pull request implements a "feature flagging" system that will let us turn Terminal and conhost features on/off by branch, "release" status or branding (Dev, Preview, etc.). It's loosely modelled after the Windows OS concept of "Velocity," but only insofar as it is driven by an XML document and there's a tool that emits a header file for you to include. It only supports toggling features at compile time, and the feature flag evaluators are intended to be fully constant expressions. Features are added to `src\features.xml` and marked with a "stage". For now, the only stages available are `AlwaysDisabled` and `AlwaysEnabled`. Features can be toggled to different states using branch and branding tokens, as documented in the included feature flag docs. For a given feature Feature_XYZ, we will emit two fixtures visible to the compiler: 1. A preprocessor define `TIL_FEATURE_XYZ_ENABLED` (usable from MIDL, C++ and C) 2. A feature class type `Feature_XYZ` with a static constexpr member `IsEnabled()` (usable from C++, designed for `if constexpr()`). Like Velocity, we rely on the compiler to eliminate dead code caused by things that compile down to `if constexpr (false)`. :) Michael suggested that we could use `WindowsInbox` as a branding to determine when we were being built inside Windows to supplant our use of the `__INSIDE_WINDOWS` preprocessor token. It was brilliant. Design Decisions ---------------- * Emitting the header as part of an MSBuild project * WHY: This allows the MSBuild engine to ensure that the build is only run once, even in a parallel build situation. * Only having one feature flag document for the entire project * WHY: Ease. * Forcibly including `TilFeatureStaging` with `/FI` for all CL compiler invocations. * WHY: If this is a project-wide feature system, we should make it as easy as possible to use. * Emitting preprocessor definitions instead of constexpr/consteval * WHY: Removing entire functions/includes is impossible with `if constexpr`. * WHY: MIDL cannot use a `static constexpr bool`, but it can rely on the C preprocessor to remove text. * Using MSBuild to emit the text instead of PowerShell * WHY: This allows us to leverage MSBuild's `WriteOnlyWhenDifferent` task parameter to avoid changing the file's modification time when it would have resulted in the same contents. This lets us use the same FeatureStaging header across multiple builds and multiple branches and brandings _assuming that they do not result in a feature flag change_. * The risk in using a force-include is always that it, for some reason, determines that the entire project is out of date. We've gone to great lengths to make sure that it only does so if the features _actually materially changed_. --- .github/actions/spelling/allow/apis.txt | 2 + .github/actions/spelling/expect/expect.txt | 1 + build/rules/GenerateFeatureFlags.proj | 97 ++++++++++ doc/feature_flags.md | 65 +++++++ src/common.build.post.props | 17 ++ src/features.xml | 13 ++ tools/FeatureStagingSchema.xsd | 83 +++++++++ tools/Generate-FeatureStagingHeader.ps1 | 206 +++++++++++++++++++++ 8 files changed, 484 insertions(+) create mode 100644 build/rules/GenerateFeatureFlags.proj create mode 100644 doc/feature_flags.md create mode 100644 src/features.xml create mode 100644 tools/FeatureStagingSchema.xsd create mode 100644 tools/Generate-FeatureStagingHeader.ps1 diff --git a/.github/actions/spelling/allow/apis.txt b/.github/actions/spelling/allow/apis.txt index 8252b10b7..56142fc56 100644 --- a/.github/actions/spelling/allow/apis.txt +++ b/.github/actions/spelling/allow/apis.txt @@ -45,6 +45,7 @@ IBind IBox IClass IComparable +IComparer IConnection ICustom IDialog @@ -85,6 +86,7 @@ NOREPEAT ntprivapi oaidl ocidl +ODR osver OSVERSIONINFOEXW otms diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index 722631b26..fc07e9d2a 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -163,6 +163,7 @@ BPBF bpp BPPF branchconfig +brandings BRK Browsable bsearch diff --git a/build/rules/GenerateFeatureFlags.proj b/build/rules/GenerateFeatureFlags.proj new file mode 100644 index 000000000..8a8b494a6 --- /dev/null +++ b/build/rules/GenerateFeatureFlags.proj @@ -0,0 +1,97 @@ + + + + + + + + Release + AnyCPU + + + Fuzzing + AnyCPU + + + AuditMode + AnyCPU + + + Debug + AnyCPU + + + + + d97c3c61-53cd-4e72-919b-9a0940e038f9 + + + + $(SolutionDir)obj\$(Configuration)\GenerateFeatureFlags\ + $(SolutionDir)bin\$(Configuration)\ + + <_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Preview'">Preview + <_WTBrandingName Condition="'$(WindowsTerminalBranding)'=='Release'">Release + <_WTBrandingName Condition="'$(_WTBrandingName)'==''">Dev + + + + + + + + + <_BrandingLines Include="$(_WTBrandingName)" /> + + + + + + + <_BranchBrandingCacheFiles Include="$(IntermediateOutputPath)branch_branding_cache.txt" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/feature_flags.md b/doc/feature_flags.md new file mode 100644 index 000000000..f4c2a7d68 --- /dev/null +++ b/doc/feature_flags.md @@ -0,0 +1,65 @@ +# til::feature + +Feature flags are controlled by an XML document stored at `src/features.xml`. + +## Example Document + +```xml + + + + + Feature_XYZ + + Does a cool thing + + + 1234 + + + AlwaysEnabled|AlwaysDisabled + + + + branch/with/wildcard/* + + + + + + ... + + + + + + Release + + + + + + ... + + + + + + +``` + +## Notes + +Features that are disabled for Release using `alwaysDisabledReleaseTokens` are +*always* disabled in Release, even if they come from a branch that would have +been enabled by the wildcard. + +### Precedence + +1. `alwaysDisabledReleaseTokens` +2. Enabled branches +3. Disabled branches + * The longest branch token that matches your branch will win. +3. Enabled brandings +4. Disabled brandings +5. The feature's default state diff --git a/src/common.build.post.props b/src/common.build.post.props index 87416a898..e5bc2f5f3 100644 --- a/src/common.build.post.props +++ b/src/common.build.post.props @@ -64,4 +64,21 @@ + + + + OCCallFeatureFlagGenerator; + $(BuildDependsOn) + + + + + + + + + + $(SolutionDir)\bin\$(Configuration)\inc\TilFeatureStaging.h;%(ForcedIncludeFiles) + + diff --git a/src/features.xml b/src/features.xml new file mode 100644 index 000000000..7f11a0ba2 --- /dev/null +++ b/src/features.xml @@ -0,0 +1,13 @@ + + + + + Feature_ExampleFeat + This feature will be replaced in the next pull request. + AlwaysEnabled + + Preview + WindowsInbox + + + diff --git a/tools/FeatureStagingSchema.xsd b/tools/FeatureStagingSchema.xsd new file mode 100644 index 000000000..3e6819244 --- /dev/null +++ b/tools/FeatureStagingSchema.xsd @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/Generate-FeatureStagingHeader.ps1 b/tools/Generate-FeatureStagingHeader.ps1 new file mode 100644 index 000000000..8206b7bb1 --- /dev/null +++ b/tools/Generate-FeatureStagingHeader.ps1 @@ -0,0 +1,206 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +################################################################################ +# This script generates a header describing which Terminal/Console features +# should be compiled-in, based on an XML document describing them. + +[CmdletBinding()] +Param( + [Parameter(Position=0, Mandatory=$True)] + [ValidateScript({ Test-Path $_ })] + [string]$Path, + + [ValidateSet("Dev", "Preview", "Release", "WindowsInbox")] + [string]$Branding = "Dev", + + [string]$BranchOverride = $Null, + + [string]$OutputPath +) + +Enum Stage { + AlwaysDisabled; + AlwaysEnabled; +} + +Function ConvertTo-FeatureStage([string]$stage) { + Switch($stage) { + "AlwaysEnabled" { [Stage]::AlwaysEnabled; Return } + "AlwaysDisabled" { [Stage]::AlwaysDisabled; Return } + } + Throw "Invalid feature stage $stage" +} + +Class Feature { + [string]$Name + [Stage]$Stage + [System.Collections.Generic.Dictionary[string, Stage]]$BranchTokenStages + [System.Collections.Generic.Dictionary[string, Stage]]$BrandingTokenStages + [bool]$DisabledReleaseToken + + Feature([System.Xml.XmlElement]$entry) { + $this.Name = $entry.name + $this.Stage = ConvertTo-FeatureStage $entry.stage + $this.BranchTokenStages = [System.Collections.Generic.Dictionary[string, Stage]]::new() + $this.BrandingTokenStages = [System.Collections.Generic.Dictionary[string, Stage]]::new() + $this.DisabledReleaseToken = $Null -Ne $entry.alwaysDisabledReleaseTokens + + ForEach ($b in $entry.alwaysDisabledBranchTokens.branchToken) { + $this.BranchTokenStages[$b] = [Stage]::AlwaysDisabled + } + + # AlwaysEnabled branches win over AlwaysDisabled branches + ForEach ($b in $entry.alwaysEnabledBranchTokens.branchToken) { + $this.BranchTokenStages[$b] = [Stage]::AlwaysEnabled + } + + ForEach ($b in $entry.alwaysDisabledBrandingTokens.brandingToken) { + $this.BrandingTokenStages[$b] = [Stage]::AlwaysDisabled + } + + # AlwaysEnabled brandings win over AlwaysDisabled brandings + ForEach ($b in $entry.alwaysEnabledBrandingTokens.brandingToken) { + $this.BrandingTokenStages[$b] = [Stage]::AlwaysEnabled + } + } + + [string] PreprocessorName() { + return "TIL_$($this.Name.ToUpper())_ENABLED" + } +} + +class FeatureComparer : System.Collections.Generic.IComparer[Feature] { + [int] Compare([Feature]$a, [Feature]$b) { + If ($a.Name -lt $b.Name) { + Return -1 + } ElseIf ($a.Name -gt $b.Name) { + Return 1 + } Else { + Return 0 + } + } +} + +Function Resolve-FinalFeatureStage { + Param( + [Feature]$Feature, + [string]$Branch, + [string]$Branding + ) + + # RELEASE=DISABLED wins all checks + # Then, branch match by most-specific branch + # Then, branding type (if no overriding branch match) + + If ($Branding -Eq "Release" -And $Feature.DisabledReleaseToken) { + [Stage]::AlwaysDisabled + Return + } + + If (-Not [String]::IsNullOrEmpty($Branch)) { + $lastMatchLen = 0 + $branchStage = $Null + ForEach ($branchToken in $Feature.BranchTokenStages.Keys) { + # Match the longest branch token -- it should be the most specific + If ($Branch -Like $branchToken -And $branchToken.Length -Gt $lastMatchLen) { + $lastMatchLen = $branchToken.Length + $branchStage = $Feature.BranchTokenStages[$branchToken] + } + } + If ($Null -Ne $branchStage) { + $branchStage + Return + } + } + + $BrandingStage = $Feature.BrandingTokenStages[$Branding] + If ($Null -Ne $BrandingStage) { + $BrandingStage + Return + } + + $Feature.Stage +} + +$ErrorActionPreference = "Stop" +$x = [xml](Get-Content $Path -EA:Stop) +$x.Schemas.Add('http://microsoft.com/TilFeatureStaging-Schema.xsd', (Resolve-Path (Join-Path $PSScriptRoot "FeatureStagingSchema.xsd")).Path) | Out-Null +$x.Validate($null) + +$featureComparer = [FeatureComparer]::new() +$features = [System.Collections.Generic.List[Feature]]::new(16) + +ForEach ($entry in $x.featureStaging.feature) { + $features.Add([Feature]::new($entry)) +} + +$features.Sort($featureComparer) + +$featureFinalStages = [System.Collections.Generic.Dictionary[string, Stage]]::new(16) + +$branch = $BranchOverride +If ([String]::IsNullOrEmpty($branch)) { + Try { + $branch = & git branch --show-current 2>$Null + } Catch { + Try { + $branch = & git rev-parse --abbrev-ref HEAD 2>$Null + } Catch { + Write-Verbose "Cannot determine current Git branch; skipping branch validation" + } + } +} + +ForEach ($feature in $features) { + $featureFinalStages[$feature.Name] = Resolve-FinalFeatureStage -Feature $feature -Branch $branch -Branding $Branding +} + +### CODE GENERATION + +$script:Output = "" +Function AddOutput($s) { + $script:Output += $s +} + +AddOutput @" +// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT IT +// INPUT FILE: $Path + +"@ + +ForEach ($feature in $features) { + $stage = $featureFinalStages[$feature.Name] + + AddOutput @" +#define $($feature.PreprocessorName()) $(If ($stage -eq [Stage]::AlwaysEnabled) { "1" } Else { "0" }) + +"@ +} + +AddOutput @" + +#if defined(__cplusplus) + +"@ + +ForEach ($feature in $features) { + AddOutput @" +__pragma(detect_mismatch("ODR_violation_$($feature.PreprocessorName())_mismatch", "$($feature.Stage)")) +struct $($feature.Name) +{ + static constexpr bool IsEnabled() { return $($feature.PreprocessorName()) == 1; } +}; + +"@ +} + +AddOutput @" +#endif +"@ + +If ([String]::IsNullOrEmpty($OutputPath)) { + $script:Output +} Else { + Out-File -Encoding UTF8 -FilePath $OutputPath -InputObject $script:Output +} From e34897cd1ffe963d49a299d8d033615d816b54e1 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Jun 2021 01:24:21 +0200 Subject: [PATCH 10/12] Add a language switcher using PrimaryLanguageOverride (#10309) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request This PR adds a global "language" setting, which may be set to any supported BCP 47 tag. Additionally a ComboBox is added to the settings UI under "Appearance", listing all languages with their localized names. This PR introduces one new issue: If you change the language while the app is running, the UI will be in a torn state, as not all UI elements refresh automatically if the `PrimaryLanguageOverride` is changed. ## PR Checklist * [x] Closes #5497 * [x] I work here * [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 * [x] Schema updated ## Validation Steps Performed * UI language changes when changing the "language" in settings.json before starting WT / while WT is running. ✔️ * "language" field is removed from settings.json if "Use system default" is selected. ✔️ * "language" field is added or updated in settings.json if any other language is selected. ✔️ * Removes qps- languages if debugFeatures is false. ✔️ * Correctly refreshes all UI elements with the new language. ❌ --- .github/actions/spelling/allow/allow.txt | 3 + .vscode/settings.json | 2 +- doc/cascadia/profiles.schema.json | 5 + src/cascadia/TerminalApp/AppLogic.cpp | 15 ++ src/cascadia/TerminalApp/AppLogic.h | 1 + src/cascadia/TerminalApp/pch.h | 45 +++--- .../GlobalAppearance.cpp | 144 +++++++++++++++++- .../TerminalSettingsEditor/GlobalAppearance.h | 17 ++- .../GlobalAppearance.idl | 6 +- .../GlobalAppearance.xaml | 16 +- .../Resources/en-US/Resources.resw | 12 ++ src/cascadia/TerminalSettingsEditor/pch.h | 11 +- .../TerminalSettingsModel/AppearanceConfig.h | 1 - .../GlobalAppSettings.cpp | 5 + .../TerminalSettingsModel/GlobalAppSettings.h | 1 + .../GlobalAppSettings.idl | 1 + src/inc/LibraryIncludes.h | 31 ++-- src/inc/til.h | 2 +- .../{visualize_control_codes.h => string.h} | 19 +++ src/renderer/dx/precomp.h | 3 - 20 files changed, 278 insertions(+), 62 deletions(-) rename src/inc/til/{visualize_control_codes.h => string.h} (50%) diff --git a/.github/actions/spelling/allow/allow.txt b/.github/actions/spelling/allow/allow.txt index 3452bf4a8..fcaaf797a 100644 --- a/.github/actions/spelling/allow/allow.txt +++ b/.github/actions/spelling/allow/allow.txt @@ -12,6 +12,7 @@ downsides dze dzhe Enum'd +formattings ftp geeksforgeeks ghe @@ -34,6 +35,7 @@ overlined postmodern ptys qof +qps reimplementation reserialization reserialize @@ -46,6 +48,7 @@ tokenizes tonos tshe UIs +und versioned We'd wildcards diff --git a/.vscode/settings.json b/.vscode/settings.json index 2b746e3d1..142fa20f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -100,6 +100,6 @@ "**/bin/**": true, "**/obj/**": true, "**/packages/**": true, - "**/generated files/**": true + "**/Generated Files/**": true } } \ No newline at end of file diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index c17f5662e..66e914fa9 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -1109,6 +1109,11 @@ }, "type": "array" }, + "language": { + "default": "", + "description": "Sets an override for the app's preferred language, expressed as a BCP-47 language tag like en-US.", + "type": "string" + }, "theme": { "default": "system", "description": "Sets the theme of the application. The special value \"system\" refers to the active Windows system theme.", diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 449d19b6b..52ab7e0a1 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -306,6 +306,7 @@ namespace winrt::TerminalApp::implementation }); _root->Create(); + _ApplyLanguageSettingChange(); _ApplyTheme(_settings.GlobalSettings().Theme()); _ApplyStartupTaskStateChange(); @@ -904,6 +905,19 @@ namespace winrt::TerminalApp::implementation } } + void AppLogic::_ApplyLanguageSettingChange() + { + using ApplicationLanguages = winrt::Windows::Globalization::ApplicationLanguages; + + const auto language = _settings.GlobalSettings().Language(); + const auto primaryLanguageOverride = ApplicationLanguages::PrimaryLanguageOverride(); + + if (primaryLanguageOverride != language) + { + ApplicationLanguages::PrimaryLanguageOverride(language); + } + } + fire_and_forget AppLogic::_LoadErrorsDialogRoutine() { co_await winrt::resume_foreground(_root->Dispatcher()); @@ -1023,6 +1037,7 @@ namespace winrt::TerminalApp::implementation // Update the settings in TerminalPage _root->SetSettings(_settings, true); + _ApplyLanguageSettingChange(); _RefreshThemeRoutine(); _ApplyStartupTaskStateChange(); diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 50b7b95f0..5a046242d 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -133,6 +133,7 @@ namespace winrt::TerminalApp::implementation bool _IsKeyboardServiceEnabled(); void _ShowKeyboardServiceDisabledDialog(); + void _ApplyLanguageSettingChange(); fire_and_forget _LoadErrorsDialogRoutine(); fire_and_forget _ShowLoadWarningsDialogRoutine(); fire_and_forget _RefreshThemeRoutine(); diff --git a/src/cascadia/TerminalApp/pch.h b/src/cascadia/TerminalApp/pch.h index f2c566352..299d7de63 100644 --- a/src/cascadia/TerminalApp/pch.h +++ b/src/cascadia/TerminalApp/pch.h @@ -25,40 +25,39 @@ #include -#include - -#include - +#include +#include #include #include #include +#include #include -#include -#include +#include +#include +#include #include +#include +#include #include #include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include "winrt/Windows.UI.Xaml.Markup.h" -#include "winrt/Windows.UI.Xaml.Documents.h" -#include "winrt/Windows.UI.Xaml.Automation.h" -#include "winrt/Windows.UI.Xaml.Automation.Peers.h" -#include "winrt/Windows.UI.ViewManagement.h" -#include -#include #include #include #include #include -#include +#include +#include +#include +#include +#include -#include +#include // Including TraceLogging essentials for the binary #include @@ -70,14 +69,6 @@ TRACELOGGING_DECLARE_PROVIDER(g_hTerminalAppProvider); #include #include -#include -#include -#include -#include -#include - -#include - #include // Manually include til after we include Windows.Foundation to give it winrt superpowers diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp index 7556dc067..1c53db7dd 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.cpp @@ -2,10 +2,12 @@ // Licensed under the MIT license. #include "pch.h" +#include "EnumEntry.h" #include "GlobalAppearance.h" #include "GlobalAppearance.g.cpp" #include "GlobalAppearancePageNavigationState.g.cpp" -#include "EnumEntry.h" + +#include using namespace winrt; using namespace winrt::Windows::UI::Xaml; @@ -16,6 +18,11 @@ using namespace winrt::Windows::Foundation::Collections; namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { + // For ComboBox an empty SelectedItem string denotes no selection. + // What we want instead is for "Use system language" to be selected by default. + // --> "und" is synonymous for "Use system language". + constexpr std::wstring_view systemLanguageTag{ L"und" }; + GlobalAppearance::GlobalAppearance() { InitializeComponent(); @@ -28,4 +35,139 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { _State = e.Parameter().as(); } + + winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag) + { + if (tag == systemLanguageTag) + { + return RS_(L"Globals_LanguageDefault"); + } + + winrt::Windows::Globalization::Language language{ tag }; + return language.NativeName(); + } + + // Returns the list of languages the user may override the application language with. + // The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}. + // "und" is short for "undefined" and is synonymous for "Use system language" in this code. + winrt::Windows::Foundation::Collections::IObservableVector GlobalAppearance::LanguageList() + { + if (_languageList) + { + return _languageList; + } + + // In order to return the language list this code does the following: + // [1] Get all possible languages we want to allow the user to choose. + // We have to acquire languages from multiple sources, creating duplicates. See below at [1]. + // [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order. + // I wanted to sort the localized language names initially, but it turned out to be complex. + // [3] Remove potential duplicates in our language list from [1]. + // We don't want to have en-US twice in the list, do we? + // [4] Optionally remove unwanted language tags (like pseudo-localizations). + + std::vector tags; + + // [1]: + { + // ManifestLanguages contains languages the app ships with. + // + // Languages is a computed list that merges the ManifestLanguages with the + // user's ranked list of preferred languages taken from the system settings. + // As is tradition the API documentation is incomplete though, as it can also + // contain regional language variants. If our app supports en-US, but the user + // has en-GB or en-DE in their system's preferred language list, Languages will + // contain those as well, as they're variants from a supported language. We should + // allow a user to select those, as regional formattings can vary significantly. + const std::array tagSources{ + winrt::Windows::Globalization::ApplicationLanguages::ManifestLanguages(), + winrt::Windows::Globalization::ApplicationLanguages::Languages() + }; + + // tags will hold all the flattened results from tagSources. + // We resize() the vector to the proper size in order to efficiently GetMany() all items. + tags.resize(std::accumulate( + tagSources.begin(), + tagSources.end(), + // tags[0] will be "und" - the "Use system language" item + // tags[1..n] will contain tags from tagSources. + // --> totalTags is offset by 1 + 1, + [](uint32_t sum, const auto& v) -> uint32_t { + return sum + v.Size(); + })); + + // As per the function definition, the first item + // is always "Use system language" ("und"). + auto data = tags.data(); + *data++ = systemLanguageTag; + + // Finally GetMany() all the tags from tagSources. + for (const auto& v : tagSources) + { + const auto size = v.Size(); + v.GetMany(0, winrt::array_view(data, size)); + data += size; + } + } + + // NOTE: The size of tags is always >0, due to tags[0] being hardcoded to "und". + const auto tagsBegin = ++tags.begin(); + const auto tagsEnd = tags.end(); + + // [2]: + std::sort(tagsBegin, tagsEnd); + + // I'd love for both, std::unique and std::remove_if, to occur in a single loop, + // but the code turned out to be complex and even less maintainable, so I gave up. + { + // [3] part 1: + auto it = std::unique(tagsBegin, tagsEnd); + + // The qps- languages are useful for testing ("pseudo-localization"). + // --> Leave them in if debug features are enabled. + if (!_State.Globals().DebugFeaturesEnabled()) + { + // [4] part 1: + it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool { + return til::starts_with(tag, L"qps-"); + }); + } + + // [3], [4] part 2 (completing the so called "erase-remove idiom"): + tags.erase(it, tagsEnd); + } + + _languageList = winrt::single_threaded_observable_vector(std::move(tags)); + return _languageList; + } + + winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentLanguage() + { + if (_currentLanguage.empty()) + { + _currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(); + if (_currentLanguage.empty()) + { + _currentLanguage = systemLanguageTag; + } + } + + return winrt::box_value(_currentLanguage); + } + + void GlobalAppearance::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag) + { + _currentLanguage = winrt::unbox_value(tag); + + const auto globals = _State.Globals(); + if (_currentLanguage == systemLanguageTag) + { + globals.ClearLanguage(); + } + else + { + globals.Language(_currentLanguage); + } + } } diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h index dddbf23f5..484fb1441 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.h @@ -26,9 +26,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation void OnNavigatedTo(const winrt::Windows::UI::Xaml::Navigation::NavigationEventArgs& e); WINRT_PROPERTY(Editor::GlobalAppearancePageNavigationState, State, nullptr); - GETSET_BINDABLE_ENUM_SETTING(Theme, winrt::Windows::UI::Xaml::ElementTheme, State().Globals, Theme); GETSET_BINDABLE_ENUM_SETTING(TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, State().Globals, TabWidthMode); + + public: + // LanguageDisplayConverter maps the given BCP 47 tag to a localized string. + // For instance "en-US" produces "English (United States)", while "de-DE" produces + // "Deutsch (Deutschland)". This works independently of the user's locale. + static winrt::hstring LanguageDisplayConverter(const winrt::hstring& tag); + + winrt::Windows::Foundation::Collections::IObservableVector LanguageList(); + winrt::Windows::Foundation::IInspectable CurrentLanguage(); + void CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag); + + private: + std::vector _GetSupportedLanguageTags(); + + winrt::Windows::Foundation::Collections::IObservableVector _languageList{ nullptr }; + winrt::hstring _currentLanguage; }; } diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl index 294f73022..8901b9934 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.idl @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import "EnumEntry.idl"; +import "EnumEntry.idl"; namespace Microsoft.Terminal.Settings.Editor { @@ -15,6 +15,10 @@ namespace Microsoft.Terminal.Settings.Editor GlobalAppearance(); GlobalAppearancePageNavigationState State { get; }; + static String LanguageDisplayConverter(String tag); + Windows.Foundation.Collections.IObservableVector LanguageList { get; }; + IInspectable CurrentLanguage; + IInspectable CurrentTheme; Windows.Foundation.Collections.IObservableVector ThemeList { get; }; diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml index 84c1408ae..7b33b45d0 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml @@ -28,9 +28,21 @@ - - + + + + + + + + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index a42629596..495191792 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -215,6 +215,18 @@ Yellow This is the header for a control that lets the user select the yellow color for text displayed on the screen. + + Language + The header for a control allowing users to choose the app's language. + + + Sets an override for the app's preferred language. + A description explaining how this control changes the app's language. + + + Use system default + The app contains a control allowing users to choose the app's language. If this value is chosen, the language preference list of the system settings is used instead. + Always show tabs Header for a control to toggle if the app should always show the tabs (similar to a website browser). diff --git a/src/cascadia/TerminalSettingsEditor/pch.h b/src/cascadia/TerminalSettingsEditor/pch.h index df45f6bd2..4ee6261bf 100644 --- a/src/cascadia/TerminalSettingsEditor/pch.h +++ b/src/cascadia/TerminalSettingsEditor/pch.h @@ -20,21 +20,14 @@ #undef GetCurrentTime #endif -#include - -#include - -#include #include #include -#include -#include - +#include #include #include -#include #include #include +#include #include #include #include diff --git a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h index d0b08e9e2..cdcdc9613 100644 --- a/src/cascadia/TerminalSettingsModel/AppearanceConfig.h +++ b/src/cascadia/TerminalSettingsModel/AppearanceConfig.h @@ -16,7 +16,6 @@ Author(s): #pragma once -#include "pch.h" #include "AppearanceConfig.g.h" #include "JsonUtils.h" #include "../inc/cppwinrt_utils.h" diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp index 39c370228..466031b05 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.cpp @@ -26,6 +26,7 @@ static constexpr std::string_view InitialColsKey{ "initialCols" }; static constexpr std::string_view InitialPositionKey{ "initialPosition" }; static constexpr std::string_view CenterOnLaunchKey{ "centerOnLaunch" }; static constexpr std::string_view ShowTitleInTitlebarKey{ "showTerminalTitleInTitlebar" }; +static constexpr std::string_view LanguageKey{ "language" }; static constexpr std::string_view ThemeKey{ "theme" }; static constexpr std::string_view TabWidthModeKey{ "tabWidthMode" }; static constexpr std::string_view ShowTabsInTitlebarKey{ "showTabsInTitlebar" }; @@ -101,6 +102,7 @@ winrt::com_ptr GlobalAppSettings::Copy() const globals->_AlwaysShowTabs = _AlwaysShowTabs; globals->_ShowTitleInTitlebar = _ShowTitleInTitlebar; globals->_ConfirmCloseAllTabs = _ConfirmCloseAllTabs; + globals->_Language = _Language; globals->_Theme = _Theme; globals->_TabWidthMode = _TabWidthMode; globals->_ShowTabsInTitlebar = _ShowTabsInTitlebar; @@ -279,6 +281,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json) JsonUtils::GetValueForKey(json, LaunchModeKey, _LaunchMode); + JsonUtils::GetValueForKey(json, LanguageKey, _Language); + JsonUtils::GetValueForKey(json, ThemeKey, _Theme); JsonUtils::GetValueForKey(json, TabWidthModeKey, _TabWidthMode); @@ -393,6 +397,7 @@ Json::Value GlobalAppSettings::ToJson() const JsonUtils::SetValueForKey(json, WarnAboutLargePasteKey, _WarnAboutLargePaste); JsonUtils::SetValueForKey(json, WarnAboutMultiLinePasteKey, _WarnAboutMultiLinePaste); JsonUtils::SetValueForKey(json, LaunchModeKey, _LaunchMode); + JsonUtils::SetValueForKey(json, LanguageKey, _Language); JsonUtils::SetValueForKey(json, ThemeKey, _Theme); JsonUtils::SetValueForKey(json, TabWidthModeKey, _TabWidthMode); JsonUtils::SetValueForKey(json, SnapToGridOnResizeKey, _SnapToGridOnResize); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h index f93bcd0e3..e11807b2a 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.h @@ -65,6 +65,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation INHERITABLE_SETTING(Model::GlobalAppSettings, bool, AlwaysShowTabs, true); INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ShowTitleInTitlebar, true); INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ConfirmCloseAllTabs, true); + INHERITABLE_SETTING(Model::GlobalAppSettings, hstring, Language); INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Windows::UI::Xaml::ElementTheme, Theme, winrt::Windows::UI::Xaml::ElementTheme::Default); INHERITABLE_SETTING(Model::GlobalAppSettings, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, TabWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::Equal); INHERITABLE_SETTING(Model::GlobalAppSettings, bool, ShowTabsInTitlebar, true); diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 9f3c8c745..fba59e1a4 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -43,6 +43,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Boolean, AlwaysShowTabs); INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar); INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs); + INHERITABLE_SETTING(String, Language); INHERITABLE_SETTING(Windows.UI.Xaml.ElementTheme, Theme); INHERITABLE_SETTING(Microsoft.UI.Xaml.Controls.TabViewWidthMode, TabWidthMode); INHERITABLE_SETTING(Boolean, ShowTabsInTitlebar); diff --git a/src/inc/LibraryIncludes.h b/src/inc/LibraryIncludes.h index 75bc23816..996f68d24 100644 --- a/src/inc/LibraryIncludes.h +++ b/src/inc/LibraryIncludes.h @@ -20,34 +20,35 @@ #include #include +#include #include +#include +#include +#include +#include +#include #include -#include -#include #include +#include +#include #include -#include #include +#include #include #include +#include +#include +#include +#include #include -#include #include +#include #include #include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // WIL #include diff --git a/src/inc/til.h b/src/inc/til.h index 9e8792b4c..509f02db9 100644 --- a/src/inc/til.h +++ b/src/inc/til.h @@ -19,7 +19,7 @@ #include "til/spsc.h" #include "til/coalesce.h" #include "til/replace.h" -#include "til/visualize_control_codes.h" +#include "til/string.h" #include "til/pmr.h" // Use keywords on TraceLogging providers to specify the category diff --git a/src/inc/til/visualize_control_codes.h b/src/inc/til/string.h similarity index 50% rename from src/inc/til/visualize_control_codes.h rename to src/inc/til/string.h index db5ae73d5..a60ae3659 100644 --- a/src/inc/til/visualize_control_codes.h +++ b/src/inc/til/string.h @@ -29,4 +29,23 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned" { return visualize_control_codes(std::wstring{ str }); } + + template + constexpr bool starts_with(const std::basic_string_view str, const std::basic_string_view prefix) noexcept + { +#ifdef __cpp_lib_starts_ends_with +#error This code can be replaced in C++20, which natively supports .starts_with(). +#endif + return str.size() >= prefix.size() && Traits::compare(str.data(), prefix.data(), prefix.size()) == 0; + }; + + constexpr bool starts_with(const std::string_view str, const std::string_view prefix) noexcept + { + return starts_with<>(str, prefix); + }; + + constexpr bool starts_with(const std::wstring_view str, const std::wstring_view prefix) noexcept + { + return starts_with<>(str, prefix); + }; } diff --git a/src/renderer/dx/precomp.h b/src/renderer/dx/precomp.h index d3d51af5f..9a09c25bb 100644 --- a/src/renderer/dx/precomp.h +++ b/src/renderer/dx/precomp.h @@ -15,11 +15,8 @@ #include -#include #include -#include #include -#include #include From 5bb8148ef98f970b886d0fc2842eb8bb1ba6651b Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Thu, 10 Jun 2021 18:48:54 -0500 Subject: [PATCH 11/12] Convert four INSIDE_WINDOWS blocks to til features (#10404) This pull request converts four of our existing `#ifdef` (or `#ifndef`) `INSIDE_WINDOWS` blocks to til::features: * Attempting to establish a handoff session (inside Windows only) * The ability to *receive* a handoff session (outside Windows only) * The DX engine (outside Windows only) and shaders (also outside only) * Whether we use numpad event synthesis for clipboard/conpty (inside Windows only) Most of these are using the preprocessor verison of til::feature, only because it is more difficult to gate the inclusion of headers on constant expressions. I'd love to prefer the compile time version. --- .../actions/spelling/patterns/patterns.txt | 1 + src/features.xml | 45 +++++++++++++++++-- src/host/exe/exemain.cpp | 6 +-- src/host/srvinit.cpp | 10 ++--- src/host/ut_host/ClipboardTests.cpp | 43 +++++++++--------- src/host/ut_host/VtIoTests.cpp | 6 +-- src/interactivity/base/EventSynthesis.cpp | 14 +----- src/interactivity/win32/window.cpp | 6 +-- src/renderer/dx/DxRenderer.cpp | 2 +- src/renderer/dx/ScreenPixelShader.h | 2 +- src/renderer/dx/ScreenVertexShader.h | 2 +- src/server/IoDispatchers.cpp | 2 +- 12 files changed, 86 insertions(+), 53 deletions(-) diff --git a/.github/actions/spelling/patterns/patterns.txt b/.github/actions/spelling/patterns/patterns.txt index bee4446d4..882243396 100644 --- a/.github/actions/spelling/patterns/patterns.txt +++ b/.github/actions/spelling/patterns/patterns.txt @@ -23,3 +23,4 @@ VERIFY_ARE_EQUAL\(L"[^"]+" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\+/" std::memory_order_[\w]+ D2DERR_SHADER_COMPILE_FAILED +TIL_FEATURE_[0-9A-Z_]+ diff --git a/src/features.xml b/src/features.xml index 7f11a0ba2..7ad1db40c 100644 --- a/src/features.xml +++ b/src/features.xml @@ -2,12 +2,51 @@ - Feature_ExampleFeat - This feature will be replaced in the next pull request. + Feature_ReceiveIncomingHandoff + OpenConsole should be able to receive incoming connections AlwaysEnabled - Preview WindowsInbox + + + Feature_AttemptHandoff + conhost should try to hand connections over to OpenConsole + AlwaysDisabled + + WindowsInbox + + + + + Feature_ConhostDxEngine + Controls whether conhost supports the DX engine and the UseDx registry key + AlwaysEnabled + + WindowsInbox + + + + Feature_DxEngineShaderSupport + Controls whether the DX engine is built with shader support. + AlwaysEnabled + + WindowsInbox + + + + + Feature_UseNumpadEventsForClipboardInput + Controls whether the clipboard converter (and ConPTY InputStateMachine) uses Numpad events instead of UChar + AlwaysDisabled + + + WindowsInbox + + diff --git a/src/host/exe/exemain.cpp b/src/host/exe/exemain.cpp index d7ff119ff..a85c8a313 100644 --- a/src/host/exe/exemain.cpp +++ b/src/host/exe/exemain.cpp @@ -9,7 +9,7 @@ #include "../interactivity/inc/ServiceLocator.hpp" #include "../inc/conint.h" -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED #include "CConsoleHandoff.h" #endif @@ -247,7 +247,7 @@ int CALLBACK wWinMain( // messages going forward. // 7. The out-of-box `OpenConsole.exe` can then attempt to lookup and invoke a `CTerminalHandoff` to ask a registered // Terminal to become the UI. This OpenConsole.exe will put itself in PTY mode and let the Terminal handle user interaction. -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED auto& module = OutOfProcModuleWithRegistrationFlag::Create(&_releaseNotifier); #endif @@ -264,7 +264,7 @@ int CALLBACK wWinMain( if (SUCCEEDED(hr)) { // Only try to register as a handoff target if we are NOT a part of Windows. -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED bool defAppEnabled = false; if (args.ShouldRunAsComServer() && SUCCEEDED(Microsoft::Console::Internal::DefaultApp::CheckDefaultAppPolicy(defAppEnabled)) && defAppEnabled) { diff --git a/src/host/srvinit.cpp b/src/host/srvinit.cpp index c5283dca4..bebec95b2 100644 --- a/src/host/srvinit.cpp +++ b/src/host/srvinit.cpp @@ -27,9 +27,9 @@ #include "../inc/conint.h" #include "../propslib/DelegationConfig.hpp" -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED #include "ITerminalHandoff.h" -#endif // __INSIDE_WINDOWS +#endif // TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED #pragma hdrstop @@ -367,9 +367,9 @@ HRESULT ConsoleCreateIoThread(_In_ HANDLE Server, [[maybe_unused]] PCONSOLE_API_MSG connectMessage) try { -#ifdef __INSIDE_WINDOWS +#if !TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); -#else // !__INSIDE_WINDOWS +#else // TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED auto& g = ServiceLocator::LocateGlobals(); g.handoffTarget = true; @@ -444,7 +444,7 @@ try RETURN_IF_FAILED(consoleArgs.ParseCommandline()); return ConsoleCreateIoThread(Server, &consoleArgs, driverInputEvent, connectMessage); -#endif // __INSIDE_WINDOWS +#endif // TIL_FEATURE_RECEIVEINCOMINGHANDOFF_ENABLED } CATCH_RETURN() diff --git a/src/host/ut_host/ClipboardTests.cpp b/src/host/ut_host/ClipboardTests.cpp index 206559244..fb389757e 100644 --- a/src/host/ut_host/ClipboardTests.cpp +++ b/src/host/ut_host/ClipboardTests.cpp @@ -308,26 +308,29 @@ class ClipboardTests wstr.size()); std::deque expectedEvents; -#ifdef __INSIDE_WINDOWS - // Inside Windows, where numpad events are enabled, this generated numpad events. - // should be converted to: - // 1. left alt keydown - // 2. 1st numpad keydown - // 3. 1st numpad keyup - // 4. 2nd numpad keydown - // 5. 2nd numpad keyup - // 6. left alt keyup - expectedEvents.push_back({ TRUE, 1, VK_MENU, altScanCode, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ TRUE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ TRUE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); - expectedEvents.push_back({ FALSE, 1, VK_MENU, altScanCode, wstr[0], 0 }); -#else - // Outside Windows, without numpad events, we just emit the key with a nonzero UnicodeChar - expectedEvents.push_back({ TRUE, 1, 0, 0, wstr[0], 0 }); - expectedEvents.push_back({ FALSE, 1, 0, 0, wstr[0], 0 }); -#endif + if constexpr (Feature_UseNumpadEventsForClipboardInput::IsEnabled()) + { + // Inside Windows, where numpad events are enabled, this generated numpad events. + // should be converted to: + // 1. left alt keydown + // 2. 1st numpad keydown + // 3. 1st numpad keyup + // 4. 2nd numpad keydown + // 5. 2nd numpad keyup + // 6. left alt keyup + expectedEvents.push_back({ TRUE, 1, VK_MENU, altScanCode, L'\0', LEFT_ALT_PRESSED }); + expectedEvents.push_back({ TRUE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); + expectedEvents.push_back({ FALSE, 1, 0x66, 0x4D, L'\0', LEFT_ALT_PRESSED }); + expectedEvents.push_back({ TRUE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); + expectedEvents.push_back({ FALSE, 1, 0x63, 0x51, L'\0', LEFT_ALT_PRESSED }); + expectedEvents.push_back({ FALSE, 1, VK_MENU, altScanCode, wstr[0], 0 }); + } + else + { + // Outside Windows, without numpad events, we just emit the key with a nonzero UnicodeChar + expectedEvents.push_back({ TRUE, 1, 0, 0, wstr[0], 0 }); + expectedEvents.push_back({ FALSE, 1, 0, 0, wstr[0], 0 }); + } VERIFY_ARE_EQUAL(expectedEvents.size(), events.size()); diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index 6497cb6bc..f180d03e0 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -12,7 +12,7 @@ #include "../Settings.hpp" #include "../VtIo.hpp" -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED #include "../../renderer/dx/DxRenderer.hpp" #endif @@ -38,7 +38,7 @@ class Microsoft::Console::VirtualTerminal::VtIoTests TEST_METHOD(RendererDtorAndThread); -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED TEST_METHOD(RendererDtorAndThreadAndDx); #endif @@ -433,7 +433,7 @@ void VtIoTests::RendererDtorAndThread() } } -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED void VtIoTests::RendererDtorAndThreadAndDx() { Log::Comment(NoThrowString().Format( diff --git a/src/interactivity/base/EventSynthesis.cpp b/src/interactivity/base/EventSynthesis.cpp index e72f80318..9d1c217d9 100644 --- a/src/interactivity/base/EventSynthesis.cpp +++ b/src/interactivity/base/EventSynthesis.cpp @@ -16,19 +16,9 @@ static constexpr WORD altScanCode = 0x38; static constexpr WORD leftShiftScanCode = 0x2A; -#ifdef __INSIDE_WINDOWS -// To reduce the risk of compatibility issues inside Windows, we're going to continue using the old -// version of GetQuickCharWidth to determine whether a character should be synthesized into numpad -// events. -static constexpr bool useNumpadEvents{ true }; -#else // !defined(__INSIDE_WINDOWS) -// In Terminal, however, we will *always* use normal key events (!) -static constexpr bool useNumpadEvents{ false }; -#endif // __INSIDE_WINDOWS - // Routine Description: // - naively determines the width of a UCS2 encoded wchar (with caveats noted above) -#pragma warning(suppress : 4505) // this function will be deleted if useNumpadEvents is false +#pragma warning(suppress : 4505) // this function will be deleted if numpad events are disabled static CodepointWidth GetQuickCharWidthLegacyForNumpadEventSynthesis(const wchar_t wch) noexcept { if ((0x1100 <= wch && wch <= 0x115f) // From Unicode 9.0, Hangul Choseong is wide @@ -62,7 +52,7 @@ std::deque> Microsoft::Console::Interactivity::CharToK if (keyState == invalidKey) { - if constexpr (useNumpadEvents) + if constexpr (Feature_UseNumpadEventsForClipboardInput::IsEnabled()) { // Determine DBCS character because these character does not know by VkKeyScan. // GetStringTypeW(CT_CTYPE3) & C3_ALPHA can determine all linguistic characters. However, this is diff --git a/src/interactivity/win32/window.cpp b/src/interactivity/win32/window.cpp index 8bd5d0ae4..75235fc94 100644 --- a/src/interactivity/win32/window.cpp +++ b/src/interactivity/win32/window.cpp @@ -29,7 +29,7 @@ #include "../../renderer/base/renderer.hpp" #include "../../renderer/gdi/gdirenderer.hpp" -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED #include "../../renderer/dx/DxRenderer.hpp" #else // Forward-declare this so we don't blow up later. @@ -217,7 +217,7 @@ void Window::_UpdateSystemMetrics() const [[maybe_unused]] DxEngine* pDxEngine = nullptr; try { -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED if (useDx) { pDxEngine = new DxEngine(); @@ -324,7 +324,7 @@ void Window::_UpdateSystemMetrics() const { _hWnd = hWnd; -#ifndef __INSIDE_WINDOWS +#if TIL_FEATURE_CONHOSTDXENGINE_ENABLED if (useDx) { status = NTSTATUS_FROM_WIN32(HRESULT_CODE((pDxEngine->SetHwnd(hWnd)))); diff --git a/src/renderer/dx/DxRenderer.cpp b/src/renderer/dx/DxRenderer.cpp index 2b148c68c..c94a7147e 100644 --- a/src/renderer/dx/DxRenderer.cpp +++ b/src/renderer/dx/DxRenderer.cpp @@ -194,7 +194,7 @@ _CompileShader( std::string target, std::string entry = "main") { -#ifdef __INSIDE_WINDOWS +#if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED THROW_HR(E_UNEXPECTED); return 0; #else diff --git a/src/renderer/dx/ScreenPixelShader.h b/src/renderer/dx/ScreenPixelShader.h index 0cbf185e2..fa5322057 100644 --- a/src/renderer/dx/ScreenPixelShader.h +++ b/src/renderer/dx/ScreenPixelShader.h @@ -1,6 +1,6 @@ #pragma once -#ifdef __INSIDE_WINDOWS +#if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED constexpr std::string_view retroPixelShaderString{ "" }; #else constexpr std::string_view retroPixelShaderString{ R"( diff --git a/src/renderer/dx/ScreenVertexShader.h b/src/renderer/dx/ScreenVertexShader.h index f834c18d2..c0a4c8eaf 100644 --- a/src/renderer/dx/ScreenVertexShader.h +++ b/src/renderer/dx/ScreenVertexShader.h @@ -1,6 +1,6 @@ #pragma once -#ifdef __INSIDE_WINDOWS +#if !TIL_FEATURE_DXENGINESHADERSUPPORT_ENABLED const char screenVertexShaderString[] = ""; #else const char screenVertexShaderString[] = R"( diff --git a/src/server/IoDispatchers.cpp b/src/server/IoDispatchers.cpp index c6009e9d4..a55419c17 100644 --- a/src/server/IoDispatchers.cpp +++ b/src/server/IoDispatchers.cpp @@ -146,7 +146,7 @@ static bool _shouldAttemptHandoff(const Globals& globals, const CONSOLE_INFORMATION& gci, CONSOLE_API_CONNECTINFO& cac) { -#ifndef __INSIDE_WINDOWS +#if !TIL_FEATURE_ATTEMPTHANDOFF_ENABLED UNREFERENCED_PARAMETER(globals); UNREFERENCED_PARAMETER(gci); From b034fc9ae51185745cedfc823b4439be29c76f82 Mon Sep 17 00:00:00 2001 From: Leonard Hecker Date: Fri, 11 Jun 2021 02:38:10 +0200 Subject: [PATCH 12/12] Fix SUI race conditions when reloading settings (#10390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary of the Pull Request This commit fixes various race conditions regarding the settings UI. It's unsafe to write to class members from background threads without acquiring mutexes or yielding to the main thread first. By changing the settings reload code path to yield to the main thread early, we're able to cut down on code complexity and unsafe member accesses. ## PR Checklist * [x] Closes #9273 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Settings UI reloads without crashing ✔️ --- src/cascadia/TerminalApp/AppLogic.cpp | 91 +++++++------------ src/cascadia/TerminalApp/AppLogic.h | 6 +- src/cascadia/TerminalApp/TerminalPage.cpp | 48 ++++------ src/cascadia/TerminalApp/TerminalPage.h | 6 +- .../TerminalSettingsEditor/MainPage.cpp | 67 ++++++++------ .../TerminalSettingsEditor/MainPage.h | 2 +- 6 files changed, 94 insertions(+), 126 deletions(-) diff --git a/src/cascadia/TerminalApp/AppLogic.cpp b/src/cascadia/TerminalApp/AppLogic.cpp index 52ab7e0a1..6d60c146b 100644 --- a/src/cascadia/TerminalApp/AppLogic.cpp +++ b/src/cascadia/TerminalApp/AppLogic.cpp @@ -307,7 +307,7 @@ namespace winrt::TerminalApp::implementation _root->Create(); _ApplyLanguageSettingChange(); - _ApplyTheme(_settings.GlobalSettings().Theme()); + _RefreshThemeRoutine(); _ApplyStartupTaskStateChange(); TraceLoggingWrite( @@ -896,10 +896,18 @@ namespace winrt::TerminalApp::implementation // this stops us from reloading too many times or too quickly. fire_and_forget AppLogic::_DispatchReloadSettings() { - static constexpr auto FileActivityQuiesceTime{ std::chrono::milliseconds(50) }; - if (!_settingsReloadQueued.exchange(true)) + if (_settingsReloadQueued.exchange(true)) + { + co_return; + } + + auto weakSelf = get_weak(); + + co_await winrt::resume_after(std::chrono::milliseconds(100)); + co_await winrt::resume_foreground(_root->Dispatcher()); + + if (auto self{ weakSelf.get() }) { - co_await winrt::resume_after(FileActivityQuiesceTime); _ReloadSettings(); _settingsReloadQueued.store(false); } @@ -918,27 +926,8 @@ namespace winrt::TerminalApp::implementation } } - fire_and_forget AppLogic::_LoadErrorsDialogRoutine() + void AppLogic::_RefreshThemeRoutine() { - co_await winrt::resume_foreground(_root->Dispatcher()); - - const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); - const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); - _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); - } - - fire_and_forget AppLogic::_ShowLoadWarningsDialogRoutine() - { - co_await winrt::resume_foreground(_root->Dispatcher()); - - _ShowLoadWarningsDialog(); - } - - fire_and_forget AppLogic::_RefreshThemeRoutine() - { - co_await winrt::resume_foreground(_root->Dispatcher()); - - // Refresh the UI theme _ApplyTheme(_settings.GlobalSettings().Theme()); } @@ -973,39 +962,26 @@ namespace winrt::TerminalApp::implementation return; } - auto weakThis{ get_weak() }; - co_await winrt::resume_foreground(_root->Dispatcher(), CoreDispatcherPriority::Normal); - if (auto page{ weakThis.get() }) - { - StartupTaskState state; - bool tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin(); - StartupTask task = co_await StartupTask::GetAsync(StartupTaskName); + const auto tryEnableStartupTask = _settings.GlobalSettings().StartOnUserLogin(); + const auto task = co_await StartupTask::GetAsync(StartupTaskName); - state = task.State(); - switch (state) + switch (task.State()) + { + case StartupTaskState::Disabled: + if (tryEnableStartupTask) { - case StartupTaskState::Disabled: + co_await task.RequestEnableAsync(); + } + break; + case StartupTaskState::DisabledByUser: + // TODO: GH#6254: define UX for other StartupTaskStates + break; + case StartupTaskState::Enabled: + if (!tryEnableStartupTask) { - if (tryEnableStartupTask) - { - co_await task.RequestEnableAsync(); - } - break; - } - case StartupTaskState::DisabledByUser: - { - // TODO: GH#6254: define UX for other StartupTaskStates - break; - } - case StartupTaskState::Enabled: - { - if (!tryEnableStartupTask) - { - task.Disable(); - } - break; - } + task.Disable(); } + break; } } CATCH_LOG(); @@ -1023,12 +999,15 @@ namespace winrt::TerminalApp::implementation if (FAILED(_settingsLoadedResult)) { - _LoadErrorsDialogRoutine(); + const winrt::hstring titleKey = USES_RESOURCE(L"ReloadJsonParseErrorTitle"); + const winrt::hstring textKey = USES_RESOURCE(L"ReloadJsonParseErrorText"); + _ShowLoadErrorsDialog(titleKey, textKey, _settingsLoadedResult); return; } - else if (_settingsLoadedResult == S_FALSE) + + if (_settingsLoadedResult == S_FALSE) { - _ShowLoadWarningsDialogRoutine(); + _ShowLoadWarningsDialog(); } // Here, we successfully reloaded the settings, and created a new diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 5a046242d..ad520894b 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -124,19 +124,15 @@ namespace winrt::TerminalApp::implementation ::TerminalApp::AppCommandlineArgs _appArgs; ::TerminalApp::AppCommandlineArgs _settingsAppArgs; - int _ParseArgs(winrt::array_view& args); static TerminalApp::FindTargetWindowResult _doFindTargetWindow(winrt::array_view args, const Microsoft::Terminal::Settings::Model::WindowingMode& windowingBehavior); void _ShowLoadErrorsDialog(const winrt::hstring& titleKey, const winrt::hstring& contentKey, HRESULT settingsLoadedResult); void _ShowLoadWarningsDialog(); bool _IsKeyboardServiceEnabled(); - void _ShowKeyboardServiceDisabledDialog(); void _ApplyLanguageSettingChange(); - fire_and_forget _LoadErrorsDialogRoutine(); - fire_and_forget _ShowLoadWarningsDialogRoutine(); - fire_and_forget _RefreshThemeRoutine(); + void _RefreshThemeRoutine(); fire_and_forget _ApplyStartupTaskStateChange(); void _OnLoaded(const IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& eventArgs); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 103c4bb45..7a836a23f 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -92,31 +92,26 @@ namespace winrt::TerminalApp::implementation } } - winrt::fire_and_forget TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) + void TerminalPage::SetSettings(CascadiaSettings settings, bool needRefreshUI) { _settings = settings; - auto weakThis{ get_weak() }; - co_await winrt::resume_foreground(Dispatcher()); - if (auto page{ weakThis.get() }) + // Make sure to _UpdateCommandsForPalette before + // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make + // sure the KeyChordText of Commands is updated, which needs to + // happen before the Settings UI is reloaded and tries to re-read + // those values. + _UpdateCommandsForPalette(); + CommandPalette().SetActionMap(_settings.ActionMap()); + + if (needRefreshUI) { - // Make sure to _UpdateCommandsForPalette before - // _RefreshUIForSettingsReload. _UpdateCommandsForPalette will make - // sure the KeyChordText of Commands is updated, which needs to - // happen before the Settings UI is reloaded and tries to re-read - // those values. - _UpdateCommandsForPalette(); - CommandPalette().SetActionMap(_settings.ActionMap()); - - if (needRefreshUI) - { - _RefreshUIForSettingsReload(); - } - - // Upon settings update we reload the system settings for scrolling as well. - // TODO: consider reloading this value periodically. - _systemRowsToScroll = _ReadSystemRowsToScroll(); + _RefreshUIForSettingsReload(); } + + // Upon settings update we reload the system settings for scrolling as well. + // TODO: consider reloading this value periodically. + _systemRowsToScroll = _ReadSystemRowsToScroll(); } void TerminalPage::Create() @@ -1833,7 +1828,7 @@ namespace winrt::TerminalApp::implementation // This includes update the settings of all the tabs according // to their profiles, update the title and icon of each tab, and // finally create the tab flyout - winrt::fire_and_forget TerminalPage::_RefreshUIForSettingsReload() + void TerminalPage::_RefreshUIForSettingsReload() { // Re-wire the keybindings to their handlers, as we'll have created a // new AppKeyBindings object. @@ -1888,17 +1883,10 @@ namespace winrt::TerminalApp::implementation tabImpl->SetActionMap(_settings.ActionMap()); } - auto weakThis{ get_weak() }; - - co_await winrt::resume_foreground(Dispatcher()); - // repopulate the new tab button's flyout with entries for each // profile, which might have changed - if (auto page{ weakThis.get() }) - { - _UpdateTabWidthMode(); - _CreateNewTabFlyout(); - } + _UpdateTabWidthMode(); + _CreateNewTabFlyout(); // Reload the current value of alwaysOnTop from the settings file. This // will let the user hot-reload this setting, but any runtime changes to diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 500572dd0..1b08f8717 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -54,7 +54,7 @@ namespace winrt::TerminalApp::implementation // put it in our inheritance graph. https://github.com/microsoft/microsoft-ui-xaml/issues/3331 STDMETHODIMP Initialize(HWND hwnd); - winrt::fire_and_forget SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); + void SetSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings, bool needRefreshUI); void Create(); @@ -67,8 +67,6 @@ namespace winrt::TerminalApp::implementation winrt::hstring ApplicationDisplayName(); winrt::hstring ApplicationVersion(); - winrt::hstring ThirdPartyNoticesLink(); - winrt::fire_and_forget CloseWindow(); void ToggleFocusMode(); @@ -296,7 +294,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::Control::TermControl _InitControl(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection& connection); - winrt::fire_and_forget _RefreshUIForSettingsReload(); + void _RefreshUIForSettingsReload(); void _SetNonClientAreaColors(const Windows::UI::Color& selectedTabColor); void _ClearNonClientAreaColors(); diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.cpp b/src/cascadia/TerminalSettingsEditor/MainPage.cpp index fc256a9f4..69d52294e 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.cpp +++ b/src/cascadia/TerminalSettingsEditor/MainPage.cpp @@ -65,13 +65,11 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // - settings - the new settings source // Return value: // - - fire_and_forget MainPage::UpdateSettings(Model::CascadiaSettings settings) + void MainPage::UpdateSettings(const Model::CascadiaSettings& settings) { _settingsSource = settings; _settingsClone = settings.Copy(); - co_await winrt::resume_foreground(Dispatcher()); - // Deduce information about the currently selected item IInspectable selectedItemTag; auto menuItems{ SettingsNav().MenuItems() }; @@ -83,34 +81,43 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation } } - // remove all profile-related NavViewItems by populating a std::vector - // with the ones we want to keep. - // NOTE: menuItems.Remove() causes an out-of-bounds crash. Using ReplaceAll() - // gets around this crash. - std::vector menuItemsSTL; - for (const auto& item : menuItems) - { - if (const auto& navViewItem{ item.try_as() }) - { - if (const auto& tag{ navViewItem.Tag() }) - { - if (tag.try_as()) + // We'll remove a bunch of items and iterate over it twice. + // --> Copy it into an STL vector to simplify our code and reduce COM overhead. + std::vector menuItemsSTL(menuItems.Size(), nullptr); + menuItems.GetMany(0, menuItemsSTL); + + // We want to refresh the list of profiles in the NavigationView. + // In order to add profiles we can use _InitializeProfilesList(); + // But before we can do that we have to remove existing profiles first of course. + // This "erase-remove" idiom will achieve just that. + menuItemsSTL.erase( + std::remove_if( + menuItemsSTL.begin(), + menuItemsSTL.end(), + [](const auto& item) -> bool { + if (const auto& navViewItem{ item.try_as() }) { - // don't add NavViewItem pointing to a Profile - continue; - } - else if (const auto& stringTag{ tag.try_as() }) - { - if (stringTag == addProfileTag) + if (const auto& tag{ navViewItem.Tag() }) { - // don't add the "Add Profile" item - continue; + if (tag.try_as()) + { + // remove NavViewItem pointing to a Profile + return true; + } + if (const auto& stringTag{ tag.try_as() }) + { + if (stringTag == addProfileTag) + { + // remove the "Add Profile" item + return true; + } + } } } - } - } - menuItemsSTL.emplace_back(item); - } + return false; + }), + menuItemsSTL.end()); + menuItems.ReplaceAll(menuItemsSTL); // Repopulate profile-related menu items @@ -123,7 +130,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // refresh the current page using the SelectedItem data we collected before the refresh if (selectedItemTag) { - for (const auto& item : menuItems) + for (const auto& item : menuItemsSTL) { if (const auto& menuItem{ item.try_as() }) { @@ -138,7 +145,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // found the one that was selected before the refresh SettingsNav().SelectedItem(item); _Navigate(*stringTag); - co_return; + return; } } } @@ -151,7 +158,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation // found the one that was selected before the refresh SettingsNav().SelectedItem(item); _Navigate(*profileTag); - co_return; + return; } } } diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.h b/src/cascadia/TerminalSettingsEditor/MainPage.h index 677b7c6d1..401c2576e 100644 --- a/src/cascadia/TerminalSettingsEditor/MainPage.h +++ b/src/cascadia/TerminalSettingsEditor/MainPage.h @@ -13,7 +13,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation MainPage() = delete; MainPage(const Model::CascadiaSettings& settings); - fire_and_forget UpdateSettings(Model::CascadiaSettings settings); + void UpdateSettings(const Model::CascadiaSettings& settings); void OpenJsonKeyDown(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs const& args); void OpenJsonTapped(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs const& args);